From a9f3afc29bb60a736486e7bdfef76895ae29c650 Mon Sep 17 00:00:00 2001 From: circl Date: Sun, 9 Jun 2024 18:33:03 +0200 Subject: [PATCH] LibAudio: Remove support for Serenity's AudioServer --- Userland/Libraries/LibAudio/CMakeLists.txt | 12 -- .../LibAudio/ConnectionToManagerServer.cpp | 41 ----- .../LibAudio/ConnectionToManagerServer.h | 36 ---- .../Libraries/LibAudio/ConnectionToServer.cpp | 164 ------------------ .../Libraries/LibAudio/ConnectionToServer.h | 92 ---------- .../LibAudio/PlaybackStreamSerenity.cpp | 116 ------------- .../LibAudio/PlaybackStreamSerenity.h | 41 ----- 7 files changed, 502 deletions(-) delete mode 100644 Userland/Libraries/LibAudio/ConnectionToManagerServer.cpp delete mode 100644 Userland/Libraries/LibAudio/ConnectionToManagerServer.h delete mode 100644 Userland/Libraries/LibAudio/ConnectionToServer.cpp delete mode 100644 Userland/Libraries/LibAudio/ConnectionToServer.h delete mode 100644 Userland/Libraries/LibAudio/PlaybackStreamSerenity.cpp delete mode 100644 Userland/Libraries/LibAudio/PlaybackStreamSerenity.h diff --git a/Userland/Libraries/LibAudio/CMakeLists.txt b/Userland/Libraries/LibAudio/CMakeLists.txt index f19100d2fc8..f6e1fdcf9af 100644 --- a/Userland/Libraries/LibAudio/CMakeLists.txt +++ b/Userland/Libraries/LibAudio/CMakeLists.txt @@ -15,18 +15,6 @@ set(SOURCES VorbisComment.cpp ) -if (SERENITYOS) - list(APPEND SOURCES ConnectionToServer.cpp) - list(APPEND SOURCES ConnectionToManagerServer.cpp) - list(APPEND SOURCES PlaybackStreamSerenity.cpp) - set(GENERATED_SOURCES - ../../Services/AudioServer/AudioClientEndpoint.h - ../../Services/AudioServer/AudioServerEndpoint.h - ../../Services/AudioServer/AudioManagerClientEndpoint.h - ../../Services/AudioServer/AudioManagerServerEndpoint.h - ) -endif() - if (HAVE_PULSEAUDIO) list(APPEND SOURCES PlaybackStreamPulseAudio.cpp diff --git a/Userland/Libraries/LibAudio/ConnectionToManagerServer.cpp b/Userland/Libraries/LibAudio/ConnectionToManagerServer.cpp deleted file mode 100644 index 01b3cf2e275..00000000000 --- a/Userland/Libraries/LibAudio/ConnectionToManagerServer.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "ConnectionToManagerServer.h" - -namespace Audio { - -ConnectionToManagerServer::ConnectionToManagerServer(NonnullOwnPtr socket) - : IPC::ConnectionToServer(*this, move(socket)) -{ -} - -ConnectionToManagerServer::~ConnectionToManagerServer() -{ - die(); -} - -void ConnectionToManagerServer::die() { } - -void ConnectionToManagerServer::main_mix_muted_state_changed(bool muted) -{ - if (on_main_mix_muted_state_change) - on_main_mix_muted_state_change(muted); -} - -void ConnectionToManagerServer::main_mix_volume_changed(double volume) -{ - if (on_main_mix_volume_change) - on_main_mix_volume_change(volume); -} - -void ConnectionToManagerServer::device_sample_rate_changed(u32 sample_rate) -{ - if (on_device_sample_rate_change) - on_device_sample_rate_change(sample_rate); -} - -} diff --git a/Userland/Libraries/LibAudio/ConnectionToManagerServer.h b/Userland/Libraries/LibAudio/ConnectionToManagerServer.h deleted file mode 100644 index d9c4786f154..00000000000 --- a/Userland/Libraries/LibAudio/ConnectionToManagerServer.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2023, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Audio { - -class ConnectionToManagerServer final - : public IPC::ConnectionToServer - , public AudioManagerClientEndpoint { - IPC_CLIENT_CONNECTION(ConnectionToManagerServer, "/tmp/session/%sid/portal/audiomanager"sv) -public: - virtual ~ConnectionToManagerServer() override; - virtual void die() override; - - virtual void main_mix_volume_changed(double volume) override; - virtual void main_mix_muted_state_changed(bool muted) override; - virtual void device_sample_rate_changed(u32 sample_rate) override; - - Function on_main_mix_muted_state_change; - Function on_main_mix_volume_change; - Function on_device_sample_rate_change; - -private: - ConnectionToManagerServer(NonnullOwnPtr); -}; - -} diff --git a/Userland/Libraries/LibAudio/ConnectionToServer.cpp b/Userland/Libraries/LibAudio/ConnectionToServer.cpp deleted file mode 100644 index 1634db6233a..00000000000 --- a/Userland/Libraries/LibAudio/ConnectionToServer.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Audio { - -ConnectionToServer::ConnectionToServer(NonnullOwnPtr socket) - : IPC::ConnectionToServer(*this, move(socket)) - , m_buffer(make(MUST(AudioQueue::create()))) - , m_user_queue(make()) - , m_background_audio_enqueuer(Threading::Thread::construct([this]() { - // All the background thread does is run an event loop. - Core::EventLoop enqueuer_loop; - m_enqueuer_loop = &enqueuer_loop; - enqueuer_loop.exec(); - { - Threading::MutexLocker const locker(m_enqueuer_loop_destruction); - m_enqueuer_loop = nullptr; - } - return (intptr_t) nullptr; - })) -{ - update_good_sleep_time(); - async_pause_playback(); - set_buffer(*m_buffer); -} - -ConnectionToServer::~ConnectionToServer() -{ - die(); -} - -void ConnectionToServer::die() -{ - { - Threading::MutexLocker const locker(m_enqueuer_loop_destruction); - // We're sometimes getting here after the other thread has already exited and its event loop does no longer exist. - if (m_enqueuer_loop != nullptr) { - m_enqueuer_loop->wake(); - m_enqueuer_loop->quit(0); - } - } - if (m_background_audio_enqueuer->is_started()) - (void)m_background_audio_enqueuer->join(); -} - -ErrorOr ConnectionToServer::async_enqueue(FixedArray&& samples) -{ - if (!m_background_audio_enqueuer->is_started()) { - m_background_audio_enqueuer->start(); - // Wait until the enqueuer has constructed its loop. A pseudo-spinlock is fine since this happens as soon as the other thread gets scheduled. - while (!m_enqueuer_loop) - usleep(1); - TRY(m_background_audio_enqueuer->set_priority(THREAD_PRIORITY_MAX)); - } - - m_user_queue->append(move(samples)); - // Wake the background thread to make sure it starts enqueuing audio. - m_enqueuer_loop->post_event(*this, make(0)); - m_enqueuer_loop->wake(); - async_start_playback(); - - return {}; -} - -void ConnectionToServer::clear_client_buffer() -{ - m_user_queue->clear(); -} - -void ConnectionToServer::update_good_sleep_time() -{ - auto sample_rate = static_cast(get_self_sample_rate()); - auto buffer_play_time_ns = 1'000'000'000.0 / (sample_rate / static_cast(AUDIO_BUFFER_SIZE)); - // A factor of 1 should be good for now. - m_good_sleep_time = Duration::from_nanoseconds(static_cast(buffer_play_time_ns)).to_timespec(); -} - -void ConnectionToServer::set_self_sample_rate(u32 sample_rate) -{ - IPC::ConnectionToServer::set_self_sample_rate(sample_rate); - update_good_sleep_time(); -} - -// Non-realtime audio writing loop -void ConnectionToServer::custom_event(Core::CustomEvent&) -{ - Array next_chunk; - while (true) { - if (m_user_queue->is_empty()) { - dbgln_if(AUDIO_DEBUG, "Reached end of provided audio data, going to sleep"); - break; - } - - auto available_samples = min(AUDIO_BUFFER_SIZE, m_user_queue->size()); - for (size_t i = 0; i < available_samples; ++i) - next_chunk[i] = (*m_user_queue)[i]; - - m_user_queue->discard_samples(available_samples); - - // FIXME: Could we receive interrupts in a good non-IPC way instead? - auto result = m_buffer->blocking_enqueue(next_chunk, [this]() { - nanosleep(&m_good_sleep_time, nullptr); - }); - if (result.is_error()) - dbgln("Error while writing samples to shared buffer: {}", result.error()); - } -} - -ErrorOr ConnectionToServer::realtime_enqueue(Array samples) -{ - return m_buffer->enqueue(samples); -} - -ErrorOr ConnectionToServer::blocking_realtime_enqueue(Array samples, Function wait_function) -{ - return m_buffer->blocking_enqueue(samples, move(wait_function)); -} - -unsigned ConnectionToServer::total_played_samples() const -{ - return m_buffer->weak_tail() * AUDIO_BUFFER_SIZE; -} - -unsigned ConnectionToServer::remaining_samples() -{ - return static_cast(m_user_queue->remaining_samples()); -} - -size_t ConnectionToServer::remaining_buffers() const -{ - return m_buffer->size() - m_buffer->weak_remaining_capacity(); -} - -bool ConnectionToServer::can_enqueue() const -{ - return m_buffer->can_enqueue(); -} - -void ConnectionToServer::client_volume_changed(double volume) -{ - if (on_client_volume_change) - on_client_volume_change(volume); -} - -} diff --git a/Userland/Libraries/LibAudio/ConnectionToServer.h b/Userland/Libraries/LibAudio/ConnectionToServer.h deleted file mode 100644 index 133620ab942..00000000000 --- a/Userland/Libraries/LibAudio/ConnectionToServer.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2022, kleines Filmröllchen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Audio { - -class ConnectionToServer final - : public IPC::ConnectionToServer - , public AudioClientEndpoint { - IPC_CLIENT_CONNECTION(ConnectionToServer, "/tmp/session/%sid/portal/audio"sv) -public: - virtual ~ConnectionToServer() override; - - // Both of these APIs are for convenience and when you don't care about real-time behavior. - // They will not work properly in conjunction with realtime_enqueue. - // If you don't refill the buffer in time with this API, the last shared buffer write is zero-padded to play all of the samples. - template Samples> - ErrorOr async_enqueue(Samples&& samples) - { - return async_enqueue(TRY(FixedArray::create(samples.span()))); - } - - ErrorOr async_enqueue(FixedArray&& samples); - - void clear_client_buffer(); - - // Returns immediately with the appropriate status if the buffer is full; use in conjunction with remaining_buffers to get low latency. - ErrorOr realtime_enqueue(Array samples); - ErrorOr blocking_realtime_enqueue(Array samples, Function wait_function); - - // This information can be deducted from the shared audio buffer. - unsigned total_played_samples() const; - // How many samples remain in m_enqueued_samples. - unsigned remaining_samples(); - // How many buffers (i.e. short sample arrays) the server hasn't played yet. - // Non-realtime code needn't worry about this. - size_t remaining_buffers() const; - // Whether there is room in the realtime audio queue for another sample buffer. - bool can_enqueue() const; - - void set_self_sample_rate(u32 sample_rate); - - virtual void die() override; - - Function on_client_volume_change; - -private: - ConnectionToServer(NonnullOwnPtr); - - virtual void client_volume_changed(double) override; - - // We use this to perform the audio enqueuing on the background thread's event loop - virtual void custom_event(Core::CustomEvent&) override; - - void update_good_sleep_time(); - - // Shared audio buffer: both server and client constantly read and write to/from this. - // This needn't be mutex protected: it's internally multi-threading aware. - OwnPtr m_buffer; - - // The queue of non-realtime audio provided by the user. - NonnullOwnPtr m_user_queue; - - NonnullRefPtr m_background_audio_enqueuer; - Core::EventLoop* m_enqueuer_loop { nullptr }; - Threading::Mutex m_enqueuer_loop_destruction; - - // A good amount of time to sleep when the queue is full. - // (Only used for non-realtime enqueues) - timespec m_good_sleep_time {}; -}; - -} diff --git a/Userland/Libraries/LibAudio/PlaybackStreamSerenity.cpp b/Userland/Libraries/LibAudio/PlaybackStreamSerenity.cpp deleted file mode 100644 index 4db05a32626..00000000000 --- a/Userland/Libraries/LibAudio/PlaybackStreamSerenity.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "PlaybackStreamSerenity.h" - -#include - -namespace Audio { - -ErrorOr> PlaybackStreamSerenity::create(OutputState initial_state, u32 sample_rate, u8 channels, [[maybe_unused]] u32 target_latency_ms, AudioDataRequestCallback&& data_request_callback) -{ - // ConnectionToServer can only handle stereo audio currently. If it is able to accept mono audio - // later, this can be removed. - VERIFY(channels == 2); - - VERIFY(data_request_callback); - auto connection = TRY(ConnectionToServer::try_create()); - if (auto result = connection->try_set_self_sample_rate(sample_rate); result.is_error()) - return Error::from_string_literal("Failed to set sample rate"); - - auto polling_timer = Core::Timer::create(); - auto implementation = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PlaybackStreamSerenity(connection, move(polling_timer), move(data_request_callback)))); - if (initial_state == OutputState::Playing) - connection->async_start_playback(); - return implementation; -} - -PlaybackStreamSerenity::PlaybackStreamSerenity(NonnullRefPtr stream, NonnullRefPtr polling_timer, AudioDataRequestCallback&& data_request_callback) - : m_connection(move(stream)) - , m_polling_timer(move(polling_timer)) - , m_data_request_callback(move(data_request_callback)) -{ - // Ensure that our audio buffers are filled when they are more than 3/4 empty. - // FIXME: Add an event to ConnectionToServer track the sample rate and update this interval, or - // implement the data request into ConnectionToServer so each client doesn't need to poll - // on a timer with an arbitrary interval. - m_polling_timer->set_interval(static_cast((AUDIO_BUFFERS_COUNT * 3 / 4) * AUDIO_BUFFER_SIZE * 1000 / m_connection->get_self_sample_rate())); - m_polling_timer->on_timeout = [this]() { - fill_buffers(); - }; - m_polling_timer->start(); -} - -void PlaybackStreamSerenity::fill_buffers() -{ - while (m_connection->can_enqueue()) { - Array buffer; - buffer.fill({ 0.0f, 0.0f }); - auto written_data = m_data_request_callback(Bytes { reinterpret_cast(buffer.data()), sizeof(buffer) }, PcmSampleFormat::Float32, AUDIO_BUFFER_SIZE); - // FIXME: The buffer we are enqueuing here is a fixed size, meaning that the server will not be - // aware of exactly how many samples we have written here. We should allow the server to - // consume sized buffers to allow us to obtain sample-accurate timing information even - // when we run out of samples on a sample count that is not a multiple of AUDIO_BUFFER_SIZE. - m_number_of_samples_enqueued += written_data.size() / sizeof(Sample); - MUST(m_connection->realtime_enqueue(buffer)); - } -} - -void PlaybackStreamSerenity::set_underrun_callback(Function callback) -{ - // FIXME: Implement underrun callback in AudioServer - (void)callback; -} - -NonnullRefPtr> PlaybackStreamSerenity::resume() -{ - auto promise = Core::ThreadedPromise::create(); - // FIXME: We need to get the time played at the correct time from the server. If a message to - // start playback is sent while there is any other message being processed, this may end - // up being inaccurate. - auto time = MUST(total_time_played()); - fill_buffers(); - m_connection->async_start_playback(); - m_polling_timer->start(); - promise->resolve(move(time)); - return promise; -} - -NonnullRefPtr> PlaybackStreamSerenity::drain_buffer_and_suspend() -{ - // FIXME: Play back all samples on the server before pausing. This can be achieved by stopping - // enqueuing samples and receiving a message that a buffer underrun has occurred. - auto promise = Core::ThreadedPromise::create(); - m_connection->async_pause_playback(); - m_polling_timer->stop(); - promise->resolve(); - return promise; -} - -NonnullRefPtr> PlaybackStreamSerenity::discard_buffer_and_suspend() -{ - auto promise = Core::ThreadedPromise::create(); - m_connection->async_clear_buffer(); - m_connection->async_pause_playback(); - m_polling_timer->stop(); - promise->resolve(); - return promise; -} - -ErrorOr PlaybackStreamSerenity::total_time_played() -{ - return Duration::from_milliseconds(m_number_of_samples_enqueued * 1000 / m_connection->get_self_sample_rate()); -} - -NonnullRefPtr> PlaybackStreamSerenity::set_volume(double volume) -{ - auto promise = Core::ThreadedPromise::create(); - m_connection->async_set_self_volume(volume); - promise->resolve(); - return promise; -} - -} diff --git a/Userland/Libraries/LibAudio/PlaybackStreamSerenity.h b/Userland/Libraries/LibAudio/PlaybackStreamSerenity.h deleted file mode 100644 index d8f65fb1c3b..00000000000 --- a/Userland/Libraries/LibAudio/PlaybackStreamSerenity.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2023, Gregory Bertilson - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Audio { - -class PlaybackStreamSerenity final - : public PlaybackStream { -public: - static ErrorOr> create(OutputState initial_state, u32 sample_rate, u8 channels, u32 target_latency_ms, AudioDataRequestCallback&& data_request_callback); - - virtual void set_underrun_callback(Function) override; - - virtual NonnullRefPtr> resume() override; - virtual NonnullRefPtr> drain_buffer_and_suspend() override; - virtual NonnullRefPtr> discard_buffer_and_suspend() override; - - virtual ErrorOr total_time_played() override; - - virtual NonnullRefPtr> set_volume(double) override; - -private: - PlaybackStreamSerenity(NonnullRefPtr, NonnullRefPtr polling_timer, AudioDataRequestCallback&& data_request_callback); - - void fill_buffers(); - - NonnullRefPtr m_connection; - size_t m_number_of_samples_enqueued { 0 }; - NonnullRefPtr m_polling_timer; - AudioDataRequestCallback m_data_request_callback; - bool m_paused { false }; -}; - -}