mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01:00
Everywhere: Remove AudioCodecPlugin and Qt Multimedia
These are no longer needed now that audio is played through PlaybackManager.
This commit is contained in:
parent
d17e7fd921
commit
9f44fcbded
|
|
@ -7,4 +7,4 @@ dnf install -y git gh
|
|||
# Ladybird dev dependencies
|
||||
dnf install -y autoconf-archive automake ccache cmake curl google-noto-sans-mono-fonts liberation-sans-fonts \
|
||||
libglvnd-devel libtool nasm ninja-build patchelf perl-FindBin perl-IPC-Cmd perl-lib qt6-qtbase-devel \
|
||||
qt6-qtmultimedia-devel qt6-qttools-devel qt6-qtwayland-devel tar unzip zip zlib-ng-compat-static
|
||||
qt6-qttools-devel qt6-qtwayland-devel tar unzip zip zlib-ng-compat-static
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ install_llvm_key() {
|
|||
### Install packages
|
||||
|
||||
apt update -y
|
||||
apt install -y lsb-release git python3 autoconf autoconf-archive automake build-essential cmake libdrm-dev libgl1-mesa-dev libtool nasm ninja-build pkg-config qt6-base-dev qt6-tools-dev-tools qt6-multimedia-dev qt6-wayland ccache fonts-liberation2 zip unzip curl tar
|
||||
apt install -y lsb-release git python3 autoconf autoconf-archive automake build-essential cmake libdrm-dev libgl1-mesa-dev libtool nasm ninja-build pkg-config qt6-base-dev qt6-tools-dev-tools qt6-wayland ccache fonts-liberation2 zip unzip curl tar
|
||||
### Ensure new enough host compiler is available
|
||||
|
||||
VERSION="0.0.0"
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ We currently use gcc-14 and clang-20 in our CI pipeline. If these versions are n
|
|||
|
||||
CMake 3.25 or newer must be available in $PATH.
|
||||
|
||||
> [!NOTE]
|
||||
> In all of the below lists of packages, the Qt6 multimedia package is not needed if your Linux system supports PulseAudio.
|
||||
|
||||
---
|
||||
|
||||
### Debian/Ubuntu:
|
||||
|
|
@ -67,34 +64,34 @@ sudo apt update && sudo apt install g++-14 libstdc++-14-dev
|
|||
|
||||
#### Audio support:
|
||||
|
||||
- Recommendation: Install PulseAudio development package:
|
||||
- Install PulseAudio development package:
|
||||
|
||||
```bash
|
||||
sudo apt install libpulse-dev
|
||||
```
|
||||
|
||||
- Alternative: Install Qt6's multimedia package:
|
||||
|
||||
```bash
|
||||
sudo apt install qt6-multimedia-dev
|
||||
```
|
||||
|
||||
### Arch Linux/Manjaro:
|
||||
|
||||
```
|
||||
sudo pacman -S --needed autoconf-archive automake base-devel ccache cmake curl libgl nasm ninja qt6-base qt6-multimedia qt6-tools qt6-wayland ttf-liberation tar unzip zip
|
||||
sudo pacman -S --needed autoconf-archive automake base-devel ccache cmake curl libgl nasm ninja qt6-base qt6-tools qt6-wayland ttf-liberation tar unzip zip
|
||||
```
|
||||
|
||||
Optionally, install the PulseAudio headers for audio playback support:
|
||||
|
||||
```
|
||||
sudo pacman -S libpulse
|
||||
```
|
||||
|
||||
### Fedora or derivatives:
|
||||
|
||||
```
|
||||
sudo dnf install autoconf-archive automake ccache cmake curl git libdrm-devel liberation-sans-fonts libglvnd-devel libtool nasm ninja-build patchelf perl-FindBin perl-IPC-Cmd perl-lib perl-Time-Piece qt6-qtbase-devel qt6-qtmultimedia-devel qt6-qttools-devel qt6-qtwayland-devel tar unzip zip zlib-ng-compat-static
|
||||
sudo dnf install autoconf-archive automake ccache cmake curl git libdrm-devel liberation-sans-fonts libglvnd-devel libtool nasm ninja-build patchelf perl-FindBin perl-IPC-Cmd perl-lib perl-Time-Piece qt6-qtbase-devel qt6-qttools-devel qt6-qtwayland-devel tar unzip zip zlib-ng-compat-static
|
||||
```
|
||||
|
||||
### openSUSE:
|
||||
|
||||
```
|
||||
sudo zypper install autoconf-archive automake ccache cmake curl gcc14 gcc14-c++ git liberation-fonts libglvnd-devel libtool nasm ninja qt6-base-devel qt6-multimedia-devel qt6-tools-devel qt6-wayland-devel tar unzip zip
|
||||
sudo zypper install autoconf-archive automake ccache cmake curl gcc14 gcc14-c++ git liberation-fonts libglvnd-devel libtool nasm ninja qt6-base-devel qt6-tools-devel qt6-wayland-devel tar unzip zip
|
||||
```
|
||||
|
||||
If one or more of the base repository packages are flagged as having an out-of-date version during the build process, you may need add the `devel:tools:building` repository. For example, on Leap 15.6, the `autoconf` package might be version 2.69, whereas the `gperf` package requires 2.70 to build.
|
||||
|
|
@ -119,11 +116,10 @@ Nothing to do.
|
|||
> sudo zypper install autoconf-2.72-80.d_t_b.1.noarch
|
||||
```
|
||||
|
||||
It is currently recommended to install the `libpulse-devel` package to avoid runtime dynamic linking issues. If issues persist you may need to remove the `qt6-multimedia-devel` package to avoid linking issues.
|
||||
It is necessary to install the `libpulse-devel` package to enable audio playback:
|
||||
|
||||
```
|
||||
sudo zypper install libpulse-devel
|
||||
sudo zypper remove qt6-multimedia-devel
|
||||
```
|
||||
|
||||
The build process requires at least python3.7; openSUSE Leap only features Python 3.6 as default, so it is recommendable to install the package `python312` and create a virtual environment (venv) in this case.
|
||||
|
|
@ -141,7 +137,7 @@ This virtual environment can be created once and reused in future shell sessions
|
|||
|
||||
```
|
||||
sudo xbps-install -Su # (optional) ensure packages are up to date to avoid "Transaction aborted due to unresolved dependencies."
|
||||
sudo xbps-install -S git bash gcc python3 curl cmake zip unzip linux-headers make pkg-config autoconf automake autoconf-archive nasm MesaLib-devel ninja qt6-base-devel qt6-multimedia-devel qt6-tools-devel qt6-wayland-devel
|
||||
sudo xbps-install -S git bash gcc python3 curl cmake zip unzip linux-headers make pkg-config autoconf automake autoconf-archive nasm MesaLib-devel ninja qt6-base-devel qt6-tools-devel qt6-wayland-devel
|
||||
```
|
||||
|
||||
### NixOS or with Nix:
|
||||
|
|
@ -209,7 +205,7 @@ Or, download a version of Gradle >= 8.0.0, and run the ``gradlew`` program in ``
|
|||
### FreeBSD
|
||||
|
||||
```
|
||||
pkg install autoconf-archive automake autoconf bash cmake curl gmake gn libdrm libtool libxcb libxkbcommon libX11 libXrender libXi nasm ninja patchelf pkgconf python3 qt6-base qt6-multimedia unzip zip
|
||||
pkg install autoconf-archive automake autoconf bash cmake curl gmake gn libdrm libtool libxcb libxkbcommon libX11 libXrender libXi nasm ninja patchelf pkgconf python3 qt6-base unzip zip
|
||||
```
|
||||
|
||||
## Build steps
|
||||
|
|
|
|||
|
|
@ -836,8 +836,6 @@ set(SOURCES
|
|||
PerformanceTimeline/PerformanceObserverEntryList.cpp
|
||||
PermissionsPolicy/AutoplayAllowlist.cpp
|
||||
PixelUnits.cpp
|
||||
Platform/AudioCodecPlugin.cpp
|
||||
Platform/AudioCodecPluginAgnostic.cpp
|
||||
Platform/EventLoopPlugin.cpp
|
||||
Platform/EventLoopPluginSerenity.cpp
|
||||
Platform/FontPlugin.cpp
|
||||
|
|
|
|||
|
|
@ -988,7 +988,6 @@ class AutoplayAllowlist;
|
|||
|
||||
namespace Web::Platform {
|
||||
|
||||
class AudioCodecPlugin;
|
||||
class Timer;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Time.h>
|
||||
#include <LibMedia/Audio/Loader.h>
|
||||
#include <LibMedia/Audio/Sample.h>
|
||||
#include <LibWeb/Platform/AudioCodecPlugin.h>
|
||||
|
||||
namespace Web::Platform {
|
||||
|
||||
static AudioCodecPlugin::AudioCodecPluginCreator s_creation_hook;
|
||||
|
||||
AudioCodecPlugin::AudioCodecPlugin() = default;
|
||||
AudioCodecPlugin::~AudioCodecPlugin() = default;
|
||||
|
||||
void AudioCodecPlugin::install_creation_hook(AudioCodecPluginCreator creation_hook)
|
||||
{
|
||||
VERIFY(!s_creation_hook);
|
||||
s_creation_hook = move(creation_hook);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<AudioCodecPlugin>> AudioCodecPlugin::create(NonnullRefPtr<Audio::Loader> loader)
|
||||
{
|
||||
VERIFY(s_creation_hook);
|
||||
return s_creation_hook(move(loader));
|
||||
}
|
||||
|
||||
ErrorOr<FixedArray<Audio::Sample>> AudioCodecPlugin::read_samples_from_loader(Audio::Loader& loader, size_t samples_to_load)
|
||||
{
|
||||
auto buffer_or_error = loader.get_more_samples(samples_to_load);
|
||||
if (buffer_or_error.is_error()) {
|
||||
dbgln("Error while loading samples: {}", buffer_or_error.error());
|
||||
return Error::from_string_literal("Error while loading samples");
|
||||
}
|
||||
|
||||
return buffer_or_error.release_value();
|
||||
}
|
||||
|
||||
AK::Duration AudioCodecPlugin::set_loader_position(Audio::Loader& loader, double position, AK::Duration duration)
|
||||
{
|
||||
if (loader.total_samples() == 0)
|
||||
return current_loader_position(loader);
|
||||
|
||||
auto duration_value = static_cast<double>(duration.to_milliseconds()) / 1000.0;
|
||||
position = position / duration_value * static_cast<double>(loader.total_samples() - 1);
|
||||
|
||||
loader.seek(static_cast<int>(position)).release_value_but_fixme_should_propagate_errors();
|
||||
return current_loader_position(loader);
|
||||
}
|
||||
|
||||
AK::Duration AudioCodecPlugin::current_loader_position(Audio::Loader const& loader)
|
||||
{
|
||||
auto samples_played = static_cast<double>(loader.loaded_samples());
|
||||
auto sample_rate = static_cast<double>(loader.sample_rate());
|
||||
|
||||
return AK::Duration::from_milliseconds(static_cast<i64>(samples_played / sample_rate * 1000.0));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/FixedArray.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/Weakable.h>
|
||||
#include <LibMedia/Audio/Forward.h>
|
||||
#include <LibWeb/Export.h>
|
||||
|
||||
namespace Web::Platform {
|
||||
|
||||
class WEB_API AudioCodecPlugin : public Weakable<AudioCodecPlugin> {
|
||||
public:
|
||||
using AudioCodecPluginCreator = Function<ErrorOr<NonnullOwnPtr<AudioCodecPlugin>>(NonnullRefPtr<Audio::Loader>)>;
|
||||
|
||||
static void install_creation_hook(AudioCodecPluginCreator);
|
||||
static ErrorOr<NonnullOwnPtr<AudioCodecPlugin>> create(NonnullRefPtr<Audio::Loader>);
|
||||
|
||||
virtual ~AudioCodecPlugin();
|
||||
|
||||
static ErrorOr<FixedArray<Audio::Sample>> read_samples_from_loader(Audio::Loader&, size_t samples_to_load);
|
||||
static AK::Duration set_loader_position(Audio::Loader&, double position, AK::Duration duration);
|
||||
static AK::Duration current_loader_position(Audio::Loader const&);
|
||||
|
||||
virtual void resume_playback() = 0;
|
||||
virtual void pause_playback() = 0;
|
||||
virtual void set_volume(double) = 0;
|
||||
virtual void seek(double) = 0;
|
||||
|
||||
virtual AK::Duration duration() = 0;
|
||||
|
||||
Function<void(AK::Duration)> on_playback_position_updated;
|
||||
Function<void(String)> on_decoder_error;
|
||||
|
||||
protected:
|
||||
AudioCodecPlugin();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/ThreadedPromise.h>
|
||||
#include <LibCore/Timer.h>
|
||||
#include <LibMedia/Audio/Loader.h>
|
||||
|
||||
#include "AudioCodecPluginAgnostic.h"
|
||||
|
||||
namespace Web::Platform {
|
||||
|
||||
constexpr int update_interval = 50;
|
||||
|
||||
static AK::Duration timestamp_from_samples(i64 samples, u32 sample_rate)
|
||||
{
|
||||
return AK::Duration::from_milliseconds(samples * 1000 / sample_rate);
|
||||
}
|
||||
|
||||
static AK::Duration get_loader_timestamp(NonnullRefPtr<Audio::Loader> const& loader)
|
||||
{
|
||||
return timestamp_from_samples(loader->loaded_samples(), loader->sample_rate());
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<AudioCodecPluginAgnostic>> AudioCodecPluginAgnostic::create(NonnullRefPtr<Audio::Loader> const& loader)
|
||||
{
|
||||
auto duration = timestamp_from_samples(loader->total_samples(), loader->sample_rate());
|
||||
|
||||
auto update_timer = Core::Timer::create();
|
||||
update_timer->set_interval(update_interval);
|
||||
|
||||
auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) AudioCodecPluginAgnostic(loader, duration, move(update_timer))));
|
||||
|
||||
constexpr u32 latency_ms = 100;
|
||||
// FIXME: Audio loaders are hard-coded to output stereo audio. Once that changes, the channel count provided
|
||||
// below should be retrieved from the audio loader instead of being hard-coded to 2.
|
||||
RefPtr<Audio::PlaybackStream> output = TRY(Audio::PlaybackStream::create(
|
||||
Audio::OutputState::Suspended, loader->sample_rate(), /* channels = */ 2, latency_ms,
|
||||
[&plugin = *plugin, loader](Bytes buffer, Audio::PcmSampleFormat format, size_t sample_count) -> ReadonlyBytes {
|
||||
VERIFY(format == Audio::PcmSampleFormat::Float32);
|
||||
|
||||
auto samples_result = loader->get_more_samples(sample_count);
|
||||
if (samples_result.is_error()) {
|
||||
dbgln("Error while loading samples: {}", samples_result.error());
|
||||
plugin.on_decoder_error(MUST(String::formatted("Decoding failure: {}", samples_result.error())));
|
||||
return buffer.trim(0);
|
||||
}
|
||||
|
||||
auto samples = samples_result.release_value();
|
||||
VERIFY(samples.size() <= sample_count);
|
||||
|
||||
FixedMemoryStream writing_stream { buffer };
|
||||
|
||||
for (auto& sample : samples) {
|
||||
MUST(writing_stream.write_value(sample.left));
|
||||
MUST(writing_stream.write_value(sample.right));
|
||||
}
|
||||
|
||||
// FIXME: Check if we have loaded samples past the current known duration, and if so, update it
|
||||
// and notify the media element.
|
||||
return buffer.trim(writing_stream.offset());
|
||||
}));
|
||||
|
||||
output->set_underrun_callback([&plugin = *plugin, loader, output]() {
|
||||
auto new_device_time = output->total_time_played();
|
||||
auto new_media_time = timestamp_from_samples(loader->loaded_samples(), loader->sample_rate());
|
||||
plugin.m_main_thread_event_loop.deferred_invoke([&plugin, new_device_time, new_media_time]() {
|
||||
plugin.m_last_resume_in_device_time = new_device_time;
|
||||
plugin.m_last_resume_in_media_time = new_media_time;
|
||||
});
|
||||
});
|
||||
|
||||
plugin->m_output = move(output);
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
AudioCodecPluginAgnostic::AudioCodecPluginAgnostic(NonnullRefPtr<Audio::Loader> loader, AK::Duration duration, NonnullRefPtr<Core::Timer> update_timer)
|
||||
: m_loader(move(loader))
|
||||
, m_duration(duration)
|
||||
, m_main_thread_event_loop(Core::EventLoop::current())
|
||||
, m_update_timer(move(update_timer))
|
||||
{
|
||||
m_update_timer->on_timeout = [self = make_weak_ptr<AudioCodecPluginAgnostic>()]() {
|
||||
if (self)
|
||||
self->update_timestamp();
|
||||
};
|
||||
}
|
||||
|
||||
void AudioCodecPluginAgnostic::resume_playback()
|
||||
{
|
||||
m_paused = false;
|
||||
m_output->resume()
|
||||
->when_resolved([self = make_weak_ptr<AudioCodecPluginAgnostic>()](AK::Duration new_device_time) {
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
self->m_main_thread_event_loop.deferred_invoke([self, new_device_time]() {
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
self->m_last_resume_in_device_time = new_device_time;
|
||||
self->m_update_timer->start();
|
||||
});
|
||||
})
|
||||
.when_rejected([](Error&&) {
|
||||
// FIXME: Propagate errors.
|
||||
});
|
||||
}
|
||||
|
||||
void AudioCodecPluginAgnostic::pause_playback()
|
||||
{
|
||||
m_paused = true;
|
||||
m_output->drain_buffer_and_suspend()
|
||||
->when_resolved([self = make_weak_ptr<AudioCodecPluginAgnostic>()]() {
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
auto new_media_time = timestamp_from_samples(self->m_loader->loaded_samples(), self->m_loader->sample_rate());
|
||||
auto new_device_time = self->m_output->total_time_played();
|
||||
|
||||
self->m_main_thread_event_loop.deferred_invoke([self, new_media_time, new_device_time]() {
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
self->m_last_resume_in_media_time = new_media_time;
|
||||
self->m_last_resume_in_device_time = new_device_time;
|
||||
self->m_update_timer->stop();
|
||||
self->update_timestamp();
|
||||
});
|
||||
})
|
||||
.when_rejected([](Error&&) {
|
||||
// FIXME: Propagate errors.
|
||||
});
|
||||
}
|
||||
|
||||
void AudioCodecPluginAgnostic::set_volume(double volume)
|
||||
{
|
||||
m_output->set_volume(volume)->when_rejected([](Error&&) {
|
||||
// FIXME: Propagate errors.
|
||||
});
|
||||
}
|
||||
|
||||
void AudioCodecPluginAgnostic::seek(double position)
|
||||
{
|
||||
m_output->discard_buffer_and_suspend()
|
||||
->when_resolved([self = make_weak_ptr<AudioCodecPluginAgnostic>(), position, was_paused = m_paused]() -> ErrorOr<void> {
|
||||
if (!self)
|
||||
return {};
|
||||
|
||||
auto sample_position = static_cast<i32>(position * self->m_loader->sample_rate());
|
||||
auto seek_result = self->m_loader->seek(sample_position);
|
||||
if (seek_result.is_error())
|
||||
return Error::from_string_literal("Seeking in audio loader failed");
|
||||
|
||||
auto new_media_time = get_loader_timestamp(self->m_loader);
|
||||
auto new_device_time = self->m_output->total_time_played();
|
||||
|
||||
self->m_main_thread_event_loop.deferred_invoke([self, was_paused, new_device_time, new_media_time]() {
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
self->m_last_resume_in_device_time = new_device_time;
|
||||
self->m_last_resume_in_media_time = new_media_time;
|
||||
|
||||
if (was_paused) {
|
||||
self->update_timestamp();
|
||||
} else {
|
||||
self->m_output->resume()->when_rejected([](Error&&) {
|
||||
// FIXME: Propagate errors.
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {};
|
||||
})
|
||||
.when_rejected([](Error&&) {
|
||||
// FIXME: Propagate errors.
|
||||
});
|
||||
}
|
||||
|
||||
AK::Duration AudioCodecPluginAgnostic::duration()
|
||||
{
|
||||
return m_duration;
|
||||
}
|
||||
|
||||
void AudioCodecPluginAgnostic::update_timestamp()
|
||||
{
|
||||
auto current_device_time_delta = m_output->total_time_played() - m_last_resume_in_device_time;
|
||||
|
||||
auto current_media_time = m_last_resume_in_media_time + current_device_time_delta;
|
||||
current_media_time = min(current_media_time, m_duration);
|
||||
on_playback_position_updated(current_media_time);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibCore/Timer.h>
|
||||
#include <LibMedia/Audio/PlaybackStream.h>
|
||||
#include <LibWeb/Export.h>
|
||||
#include <LibWeb/Platform/AudioCodecPlugin.h>
|
||||
|
||||
namespace Web::Platform {
|
||||
|
||||
class WEB_API AudioCodecPluginAgnostic final : public AudioCodecPlugin {
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<AudioCodecPluginAgnostic>> create(NonnullRefPtr<Audio::Loader> const&);
|
||||
|
||||
virtual void resume_playback() override;
|
||||
virtual void pause_playback() override;
|
||||
virtual void set_volume(double) override;
|
||||
virtual void seek(double) override;
|
||||
|
||||
virtual AK::Duration duration() override;
|
||||
|
||||
private:
|
||||
explicit AudioCodecPluginAgnostic(NonnullRefPtr<Audio::Loader> loader, AK::Duration, NonnullRefPtr<Core::Timer> update_timer);
|
||||
|
||||
void update_timestamp();
|
||||
|
||||
NonnullRefPtr<Audio::Loader> m_loader;
|
||||
RefPtr<Audio::PlaybackStream> m_output { nullptr };
|
||||
AK::Duration m_duration { AK::Duration::zero() };
|
||||
AK::Duration m_last_resume_in_media_time { AK::Duration::zero() };
|
||||
AK::Duration m_last_resume_in_device_time { AK::Duration::zero() };
|
||||
Core::EventLoop& m_main_thread_event_loop;
|
||||
NonnullRefPtr<Core::Timer> m_update_timer;
|
||||
bool m_paused { true };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -3,25 +3,12 @@ import("//Ladybird/link_qt.gni")
|
|||
import("//Ladybird/moc_qt_objects.gni")
|
||||
import("//Meta/gn/build/libs/pulse/enable.gni")
|
||||
|
||||
enable_qt_multimedia = !enable_pulseaudio && current_os != "mac"
|
||||
|
||||
moc_qt_objects("generate_moc") {
|
||||
sources = [ "//Ladybird/Qt/EventLoopImplementationQtEventTarget.h" ]
|
||||
|
||||
if (enable_qt_multimedia) {
|
||||
sources += [
|
||||
"//Ladybird/Qt/AudioCodecPluginQt.cpp",
|
||||
"//Ladybird/Qt/AudioThread.cpp",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
link_qt("WebContent_qt") {
|
||||
qt_components = [ "Core" ]
|
||||
|
||||
if (enable_qt_multimedia) {
|
||||
qt_components += [ "Multimedia" ]
|
||||
}
|
||||
}
|
||||
|
||||
executable("WebContent") {
|
||||
|
|
@ -75,14 +62,6 @@ executable("WebContent") {
|
|||
"//Ladybird/Qt/StringUtils.cpp",
|
||||
]
|
||||
|
||||
if (enable_qt_multimedia) {
|
||||
defines += [ "HAVE_QT_MULTIMEDIA" ]
|
||||
sources += [
|
||||
"//Ladybird/Qt/AudioCodecPluginQt.cpp",
|
||||
"//Ladybird/Qt/AudioThread.cpp",
|
||||
]
|
||||
}
|
||||
|
||||
sources += get_target_outputs(":generate_moc")
|
||||
deps += [ ":generate_moc" ]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ source_set("Platform") {
|
|||
configs += [ "//Userland/Libraries/LibWeb:configs" ]
|
||||
deps = [ "//Userland/Libraries/LibWeb:all_generated" ]
|
||||
sources = [
|
||||
"AudioCodecPlugin.cpp",
|
||||
"AudioCodecPluginAgnostic.cpp",
|
||||
"EventLoopPlugin.cpp",
|
||||
"EventLoopPluginSerenity.cpp",
|
||||
"FontPlugin.cpp",
|
||||
|
|
|
|||
|
|
@ -34,22 +34,7 @@ target_link_libraries(webcontentservice PUBLIC LibCore LibCrypto LibFileSystem L
|
|||
target_link_libraries(webcontentservice PRIVATE OpenSSL::Crypto OpenSSL::SSL)
|
||||
target_link_libraries(webcontentservice PRIVATE SDL3::SDL3)
|
||||
|
||||
if (ENABLE_QT AND NOT DEFINED LADYBIRD_AUDIO_BACKEND)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Multimedia)
|
||||
|
||||
qt_add_executable(WebContent main.cpp)
|
||||
target_link_libraries(WebContent PRIVATE Qt::Core)
|
||||
target_compile_definitions(WebContent PRIVATE HAVE_QT=1)
|
||||
|
||||
target_sources(WebContent PRIVATE
|
||||
${LADYBIRD_SOURCE_DIR}/UI/Qt/AudioCodecPluginQt.cpp
|
||||
${LADYBIRD_SOURCE_DIR}/UI/Qt/AudioThread.cpp
|
||||
)
|
||||
target_link_libraries(WebContent PRIVATE LibWebViewPlatform Qt::Multimedia)
|
||||
target_compile_definitions(WebContent PRIVATE HAVE_QT_MULTIMEDIA=1)
|
||||
else()
|
||||
add_executable(WebContent main.cpp)
|
||||
endif()
|
||||
add_executable(WebContent main.cpp)
|
||||
|
||||
target_link_libraries(WebContent PRIVATE webcontentservice LibURL)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
#include <LibIPC/ConnectionFromClient.h>
|
||||
#include <LibJS/Bytecode/Interpreter.h>
|
||||
#include <LibMain/Main.h>
|
||||
#include <LibMedia/Audio/Loader.h>
|
||||
#include <LibRequests/RequestClient.h>
|
||||
#include <LibUnicode/TimeZone.h>
|
||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||
|
|
@ -28,7 +27,6 @@
|
|||
#include <LibWeb/Loader/ResourceLoader.h>
|
||||
#include <LibWeb/Painting/BackingStoreManager.h>
|
||||
#include <LibWeb/Painting/PaintableBox.h>
|
||||
#include <LibWeb/Platform/AudioCodecPluginAgnostic.h>
|
||||
#include <LibWeb/Platform/EventLoopPluginSerenity.h>
|
||||
#include <LibWeb/WebIDL/Tracing.h>
|
||||
#include <LibWebView/Plugins/FontPlugin.h>
|
||||
|
|
@ -39,12 +37,6 @@
|
|||
#include <WebContent/PageClient.h>
|
||||
#include <WebContent/WebDriverConnection.h>
|
||||
|
||||
#if defined(HAVE_QT_MULTIMEDIA)
|
||||
# include <LibWebView/EventLoop/EventLoopImplementationQt.h>
|
||||
# include <QCoreApplication>
|
||||
# include <UI/Qt/AudioCodecPluginQt.h>
|
||||
#endif
|
||||
|
||||
#if defined(AK_OS_MACOS)
|
||||
# include <LibCore/Platform/ProcessStatisticsMach.h>
|
||||
#endif
|
||||
|
|
@ -69,25 +61,12 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
return -1;
|
||||
}
|
||||
|
||||
#if defined(HAVE_QT_MULTIMEDIA)
|
||||
QCoreApplication app(arguments.argc, arguments.argv);
|
||||
|
||||
Core::EventLoopManager::install(*new WebView::EventLoopManagerQt);
|
||||
#endif
|
||||
Core::EventLoop event_loop;
|
||||
|
||||
WebView::platform_init();
|
||||
|
||||
Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPluginSerenity);
|
||||
|
||||
Web::Platform::AudioCodecPlugin::install_creation_hook([](auto loader) {
|
||||
#if defined(HAVE_QT_MULTIMEDIA)
|
||||
return Ladybird::AudioCodecPluginQt::create(move(loader));
|
||||
#else
|
||||
return Web::Platform::AudioCodecPluginAgnostic::create(move(loader));
|
||||
#endif
|
||||
});
|
||||
|
||||
StringView command_line {};
|
||||
StringView executable_path {};
|
||||
auto config_path = ByteString::formatted("{}/ladybird/default-config", WebView::s_ladybird_resource_root);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#include <LibWeb/Loader/GeneratedPagesLoader.h>
|
||||
#include <LibWeb/Loader/ResourceLoader.h>
|
||||
#include <LibWeb/PermissionsPolicy/AutoplayAllowlist.h>
|
||||
#include <LibWeb/Platform/AudioCodecPluginAgnostic.h>
|
||||
#include <LibWeb/Platform/EventLoopPluginSerenity.h>
|
||||
#include <LibWebView/HelperProcess.h>
|
||||
#include <LibWebView/Plugins/FontPlugin.h>
|
||||
|
|
@ -55,10 +54,6 @@ ErrorOr<int> service_main(int ipc_socket)
|
|||
auto image_decoder_client = TRY(bind_image_decoder_service());
|
||||
Web::Platform::ImageCodecPlugin::install(*new WebView::ImageCodecPlugin(move(image_decoder_client)));
|
||||
|
||||
Web::Platform::AudioCodecPlugin::install_creation_hook([](auto loader) {
|
||||
return Web::Platform::AudioCodecPluginAgnostic::create(move(loader));
|
||||
});
|
||||
|
||||
Web::Bindings::initialize_main_thread_vm(Web::Bindings::AgentType::SimilarOriginWindow);
|
||||
|
||||
auto request_server_client = TRY(bind_request_server_service());
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibMedia/Audio/Loader.h>
|
||||
#include <UI/Qt/AudioCodecPluginQt.h>
|
||||
#include <UI/Qt/AudioThread.h>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
ErrorOr<NonnullOwnPtr<AudioCodecPluginQt>> AudioCodecPluginQt::create(NonnullRefPtr<Audio::Loader> loader)
|
||||
{
|
||||
auto audio_thread = TRY(AudioThread::create(move(loader)));
|
||||
audio_thread->start();
|
||||
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) AudioCodecPluginQt(move(audio_thread)));
|
||||
}
|
||||
|
||||
AudioCodecPluginQt::AudioCodecPluginQt(NonnullOwnPtr<AudioThread> audio_thread)
|
||||
: m_audio_thread(move(audio_thread))
|
||||
{
|
||||
connect(m_audio_thread, &AudioThread::playback_position_updated, this, [this](auto position) {
|
||||
if (on_playback_position_updated)
|
||||
on_playback_position_updated(position);
|
||||
});
|
||||
}
|
||||
|
||||
AudioCodecPluginQt::~AudioCodecPluginQt()
|
||||
{
|
||||
m_audio_thread->stop().release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
void AudioCodecPluginQt::resume_playback()
|
||||
{
|
||||
m_audio_thread->queue_task({ AudioTask::Type::Play }).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
void AudioCodecPluginQt::pause_playback()
|
||||
{
|
||||
m_audio_thread->queue_task({ AudioTask::Type::Pause }).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
void AudioCodecPluginQt::set_volume(double volume)
|
||||
{
|
||||
|
||||
AudioTask task { AudioTask::Type::Volume };
|
||||
task.data = volume;
|
||||
|
||||
m_audio_thread->queue_task(move(task)).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
void AudioCodecPluginQt::seek(double position)
|
||||
{
|
||||
AudioTask task { AudioTask::Type::Seek };
|
||||
task.data = position;
|
||||
|
||||
m_audio_thread->queue_task(move(task)).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
AK::Duration AudioCodecPluginQt::duration()
|
||||
{
|
||||
return m_audio_thread->duration();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibMedia/Audio/Forward.h>
|
||||
#include <LibWeb/Platform/AudioCodecPlugin.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
class AudioThread;
|
||||
|
||||
class AudioCodecPluginQt final
|
||||
: public QObject
|
||||
, public Web::Platform::AudioCodecPlugin {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<AudioCodecPluginQt>> create(NonnullRefPtr<Audio::Loader>);
|
||||
virtual ~AudioCodecPluginQt() override;
|
||||
|
||||
virtual void resume_playback() override;
|
||||
virtual void pause_playback() override;
|
||||
virtual void set_volume(double) override;
|
||||
virtual void seek(double) override;
|
||||
|
||||
virtual AK::Duration duration() override;
|
||||
|
||||
private:
|
||||
explicit AudioCodecPluginQt(NonnullOwnPtr<AudioThread>);
|
||||
|
||||
NonnullOwnPtr<AudioThread> m_audio_thread;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Platform/AudioCodecPlugin.h>
|
||||
#include <UI/Qt/AudioThread.h>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
struct AudioDevice {
|
||||
static AudioDevice create(Audio::Loader const& loader)
|
||||
{
|
||||
auto const& device_info = QMediaDevices::defaultAudioOutput();
|
||||
|
||||
auto format = device_info.preferredFormat();
|
||||
format.setSampleRate(static_cast<int>(loader.sample_rate()));
|
||||
format.setChannelCount(2);
|
||||
|
||||
auto audio_output = make<QAudioSink>(device_info, format);
|
||||
return AudioDevice { move(audio_output) };
|
||||
}
|
||||
|
||||
AudioDevice(AudioDevice&&) = default;
|
||||
|
||||
AudioDevice& operator=(AudioDevice&& device)
|
||||
{
|
||||
if (audio_output) {
|
||||
audio_output->stop();
|
||||
io_device = nullptr;
|
||||
}
|
||||
|
||||
swap(audio_output, device.audio_output);
|
||||
swap(io_device, device.io_device);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~AudioDevice()
|
||||
{
|
||||
if (audio_output)
|
||||
audio_output->stop();
|
||||
}
|
||||
|
||||
OwnPtr<QAudioSink> audio_output;
|
||||
QIODevice* io_device { nullptr };
|
||||
|
||||
private:
|
||||
explicit AudioDevice(NonnullOwnPtr<QAudioSink> output)
|
||||
: audio_output(move(output))
|
||||
{
|
||||
io_device = audio_output->start();
|
||||
}
|
||||
};
|
||||
|
||||
ErrorOr<NonnullOwnPtr<AudioThread>> AudioThread::create(NonnullRefPtr<Audio::Loader> loader)
|
||||
{
|
||||
auto task_queue = TRY(AudioTaskQueue::create());
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) AudioThread(move(loader), move(task_queue)));
|
||||
}
|
||||
|
||||
ErrorOr<void> AudioThread::stop()
|
||||
{
|
||||
TRY(queue_task({ AudioTask::Type::Stop }));
|
||||
wait();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> AudioThread::queue_task(AudioTask task)
|
||||
{
|
||||
return m_task_queue.blocking_enqueue(move(task), []() {
|
||||
usleep(UPDATE_RATE_MS * 1000);
|
||||
});
|
||||
}
|
||||
|
||||
AudioThread::AudioThread(NonnullRefPtr<Audio::Loader> loader, AudioTaskQueue task_queue)
|
||||
: m_loader(move(loader))
|
||||
, m_task_queue(move(task_queue))
|
||||
{
|
||||
auto duration = static_cast<double>(m_loader->total_samples()) / static_cast<double>(m_loader->sample_rate());
|
||||
m_duration = AK::Duration::from_milliseconds(static_cast<i64>(duration * 1000.0));
|
||||
}
|
||||
|
||||
void AudioThread::run()
|
||||
{
|
||||
auto devices = make<QMediaDevices>();
|
||||
auto audio_device = AudioDevice::create(m_loader);
|
||||
|
||||
connect(devices, &QMediaDevices::audioOutputsChanged, this, [this]() {
|
||||
queue_task({ AudioTask::Type::RecreateAudioDevice }).release_value_but_fixme_should_propagate_errors();
|
||||
});
|
||||
|
||||
auto paused = Paused::Yes;
|
||||
|
||||
while (true) {
|
||||
auto& audio_output = audio_device.audio_output;
|
||||
auto* io_device = audio_device.io_device;
|
||||
|
||||
if (auto result = m_task_queue.dequeue(); result.is_error()) {
|
||||
VERIFY(result.error() == AudioTaskQueue::QueueStatus::Empty);
|
||||
} else {
|
||||
auto task = result.release_value();
|
||||
|
||||
switch (task.type) {
|
||||
case AudioTask::Type::Stop:
|
||||
return;
|
||||
|
||||
case AudioTask::Type::Play:
|
||||
audio_output->resume();
|
||||
paused = Paused::No;
|
||||
break;
|
||||
|
||||
case AudioTask::Type::Pause:
|
||||
audio_output->suspend();
|
||||
paused = Paused::Yes;
|
||||
break;
|
||||
|
||||
case AudioTask::Type::Seek:
|
||||
VERIFY(task.data.has_value());
|
||||
m_position = Web::Platform::AudioCodecPlugin::set_loader_position(m_loader, *task.data, m_duration);
|
||||
|
||||
if (paused == Paused::Yes)
|
||||
Q_EMIT playback_position_updated(m_position);
|
||||
|
||||
break;
|
||||
|
||||
case AudioTask::Type::Volume:
|
||||
VERIFY(task.data.has_value());
|
||||
audio_output->setVolume(*task.data);
|
||||
break;
|
||||
|
||||
case AudioTask::Type::RecreateAudioDevice:
|
||||
audio_device = AudioDevice::create(m_loader);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (paused == Paused::No) {
|
||||
if (auto result = play_next_samples(*audio_output, *io_device); result.is_error()) {
|
||||
// FIXME: Propagate the error to the HTMLMediaElement.
|
||||
} else {
|
||||
Q_EMIT playback_position_updated(m_position);
|
||||
paused = result.value();
|
||||
}
|
||||
}
|
||||
|
||||
usleep(UPDATE_RATE_MS * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<AudioThread::Paused> AudioThread::play_next_samples(QAudioSink& audio_output, QIODevice& io_device)
|
||||
{
|
||||
bool all_samples_loaded = m_loader->loaded_samples() >= m_loader->total_samples();
|
||||
|
||||
if (all_samples_loaded) {
|
||||
audio_output.suspend();
|
||||
(void)m_loader->reset();
|
||||
|
||||
m_position = m_duration;
|
||||
return Paused::Yes;
|
||||
}
|
||||
|
||||
auto bytes_available = audio_output.bytesFree();
|
||||
auto bytes_per_sample = audio_output.format().bytesPerSample();
|
||||
auto channel_count = audio_output.format().channelCount();
|
||||
auto samples_to_load = bytes_available / bytes_per_sample / channel_count;
|
||||
|
||||
auto samples = TRY(Web::Platform::AudioCodecPlugin::read_samples_from_loader(*m_loader, samples_to_load));
|
||||
enqueue_samples(audio_output, io_device, move(samples));
|
||||
|
||||
m_position = Web::Platform::AudioCodecPlugin::current_loader_position(m_loader);
|
||||
return Paused::No;
|
||||
}
|
||||
|
||||
void AudioThread::enqueue_samples(QAudioSink const& audio_output, QIODevice& io_device, FixedArray<Audio::Sample> samples)
|
||||
{
|
||||
auto buffer_size = samples.size() * audio_output.format().bytesPerSample() * audio_output.format().channelCount();
|
||||
|
||||
if (buffer_size > static_cast<size_t>(m_sample_buffer.size()))
|
||||
m_sample_buffer.resize(buffer_size);
|
||||
|
||||
FixedMemoryStream stream { Bytes { m_sample_buffer.data(), buffer_size } };
|
||||
|
||||
for (auto const& sample : samples) {
|
||||
switch (audio_output.format().sampleFormat()) {
|
||||
case QAudioFormat::UInt8:
|
||||
write_sample<u8>(stream, sample.left);
|
||||
write_sample<u8>(stream, sample.right);
|
||||
break;
|
||||
case QAudioFormat::Int16:
|
||||
write_sample<i16>(stream, sample.left);
|
||||
write_sample<i16>(stream, sample.right);
|
||||
break;
|
||||
case QAudioFormat::Int32:
|
||||
write_sample<i32>(stream, sample.left);
|
||||
write_sample<i32>(stream, sample.right);
|
||||
break;
|
||||
case QAudioFormat::Float:
|
||||
write_sample<float>(stream, sample.left);
|
||||
write_sample<float>(stream, sample.right);
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
io_device.write(m_sample_buffer.data(), buffer_size);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Time.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCore/SharedCircularQueue.h>
|
||||
#include <LibMedia/Audio/Loader.h>
|
||||
#include <LibMedia/Audio/Sample.h>
|
||||
|
||||
#include <QAudioFormat>
|
||||
#include <QAudioSink>
|
||||
#include <QByteArray>
|
||||
#include <QMediaDevices>
|
||||
#include <QThread>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
static constexpr u32 UPDATE_RATE_MS = 10;
|
||||
|
||||
struct AudioTask {
|
||||
enum class Type {
|
||||
Stop,
|
||||
Play,
|
||||
Pause,
|
||||
Seek,
|
||||
Volume,
|
||||
RecreateAudioDevice,
|
||||
};
|
||||
|
||||
Type type;
|
||||
Optional<double> data {};
|
||||
};
|
||||
|
||||
using AudioTaskQueue = Core::SharedSingleProducerCircularQueue<AudioTask>;
|
||||
|
||||
class AudioThread final : public QThread { // We have to use QThread, otherwise internal Qt media QTimer objects do not work.
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<AudioThread>> create(NonnullRefPtr<Audio::Loader> loader);
|
||||
|
||||
ErrorOr<void> stop();
|
||||
|
||||
AK::Duration duration() const { return m_duration; }
|
||||
|
||||
ErrorOr<void> queue_task(AudioTask task);
|
||||
|
||||
Q_SIGNALS:
|
||||
void playback_position_updated(AK::Duration);
|
||||
|
||||
private:
|
||||
AudioThread(NonnullRefPtr<Audio::Loader> loader, AudioTaskQueue task_queue);
|
||||
|
||||
enum class Paused {
|
||||
Yes,
|
||||
No,
|
||||
};
|
||||
|
||||
void run() override;
|
||||
|
||||
ErrorOr<Paused> play_next_samples(QAudioSink& audio_output, QIODevice& io_device);
|
||||
|
||||
void enqueue_samples(QAudioSink const& audio_output, QIODevice& io_device, FixedArray<Audio::Sample> samples);
|
||||
|
||||
template<typename T>
|
||||
void write_sample(FixedMemoryStream& stream, float sample)
|
||||
{
|
||||
// The values that need to be written to the stream vary depending on the output channel format, and isn't
|
||||
// particularly well documented. The value derivations performed below were adapted from a Qt example:
|
||||
// https://code.qt.io/cgit/qt/qtmultimedia.git/tree/examples/multimedia/audiooutput/audiooutput.cpp?h=6.4.2#n46
|
||||
LittleEndian<T> pcm;
|
||||
|
||||
if constexpr (IsSame<T, u8>)
|
||||
pcm = static_cast<u8>((sample + 1.0f) / 2 * NumericLimits<u8>::max());
|
||||
else if constexpr (IsSame<T, i16>)
|
||||
pcm = static_cast<i16>(sample * NumericLimits<i16>::max());
|
||||
else if constexpr (IsSame<T, i32>)
|
||||
pcm = static_cast<i32>(sample * NumericLimits<i32>::max());
|
||||
else if constexpr (IsSame<T, float>)
|
||||
pcm = sample;
|
||||
else
|
||||
static_assert(DependentFalse<T>);
|
||||
|
||||
MUST(stream.write_value(pcm));
|
||||
}
|
||||
|
||||
NonnullRefPtr<Audio::Loader> m_loader;
|
||||
AudioTaskQueue m_task_queue;
|
||||
|
||||
QByteArray m_sample_buffer;
|
||||
|
||||
AK::Duration m_duration;
|
||||
AK::Duration m_position;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -154,10 +154,6 @@
|
|||
"zstd"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "qtmultimedia",
|
||||
"platform": "windows | freebsd"
|
||||
},
|
||||
{
|
||||
"name": "pthread",
|
||||
"platform": "windows"
|
||||
|
|
@ -284,10 +280,6 @@
|
|||
"name": "qtbase",
|
||||
"version": "6.8.3#2"
|
||||
},
|
||||
{
|
||||
"name": "qtmultimedia",
|
||||
"version": "6.8.3#0"
|
||||
},
|
||||
{
|
||||
"name": "sdl3",
|
||||
"version": "3.2.22#0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user