1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-09 09:34:57 +09:00

LibAudio: Remove support for Serenity's AudioServer

This commit is contained in:
circl 2024-06-09 18:33:03 +02:00 committed by Andreas Kling
parent c169e43e13
commit a9f3afc29b
Notes: sideshowbarker 2024-07-17 07:25:39 +09:00
7 changed files with 0 additions and 502 deletions

View file

@ -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

View file

@ -1,41 +0,0 @@
/*
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "ConnectionToManagerServer.h"
namespace Audio {
ConnectionToManagerServer::ConnectionToManagerServer(NonnullOwnPtr<Core::LocalSocket> socket)
: IPC::ConnectionToServer<AudioManagerClientEndpoint, AudioManagerServerEndpoint>(*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);
}
}

View file

@ -1,36 +0,0 @@
/*
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullOwnPtr.h>
#include <LibIPC/ConnectionToServer.h>
#include <Userland/Services/AudioServer/AudioManagerClientEndpoint.h>
#include <Userland/Services/AudioServer/AudioManagerServerEndpoint.h>
namespace Audio {
class ConnectionToManagerServer final
: public IPC::ConnectionToServer<AudioManagerClientEndpoint, AudioManagerServerEndpoint>
, 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<void(bool muted)> on_main_mix_muted_state_change;
Function<void(double volume)> on_main_mix_volume_change;
Function<void(u32 sample_rate)> on_device_sample_rate_change;
private:
ConnectionToManagerServer(NonnullOwnPtr<Core::LocalSocket>);
};
}

View file

@ -1,164 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Atomic.h>
#include <AK/Debug.h>
#include <AK/Format.h>
#include <AK/OwnPtr.h>
#include <AK/Time.h>
#include <AK/Types.h>
#include <LibAudio/ConnectionToServer.h>
#include <LibAudio/Queue.h>
#include <LibAudio/UserSampleQueue.h>
#include <LibCore/Event.h>
#include <LibThreading/Mutex.h>
#include <Userland/Services/AudioServer/AudioClientEndpoint.h>
#include <sched.h>
#include <time.h>
namespace Audio {
ConnectionToServer::ConnectionToServer(NonnullOwnPtr<Core::LocalSocket> socket)
: IPC::ConnectionToServer<AudioClientEndpoint, AudioServerEndpoint>(*this, move(socket))
, m_buffer(make<AudioQueue>(MUST(AudioQueue::create())))
, m_user_queue(make<UserSampleQueue>())
, 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<void> ConnectionToServer::async_enqueue(FixedArray<Sample>&& 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<Core::CustomEvent>(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<double>(get_self_sample_rate());
auto buffer_play_time_ns = 1'000'000'000.0 / (sample_rate / static_cast<double>(AUDIO_BUFFER_SIZE));
// A factor of 1 should be good for now.
m_good_sleep_time = Duration::from_nanoseconds(static_cast<unsigned>(buffer_play_time_ns)).to_timespec();
}
void ConnectionToServer::set_self_sample_rate(u32 sample_rate)
{
IPC::ConnectionToServer<AudioClientEndpoint, AudioServerEndpoint>::set_self_sample_rate(sample_rate);
update_good_sleep_time();
}
// Non-realtime audio writing loop
void ConnectionToServer::custom_event(Core::CustomEvent&)
{
Array<Sample, AUDIO_BUFFER_SIZE> 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<void, AudioQueue::QueueStatus> ConnectionToServer::realtime_enqueue(Array<Sample, AUDIO_BUFFER_SIZE> samples)
{
return m_buffer->enqueue(samples);
}
ErrorOr<void> ConnectionToServer::blocking_realtime_enqueue(Array<Sample, AUDIO_BUFFER_SIZE> samples, Function<void()> 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<unsigned>(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);
}
}

View file

@ -1,92 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Concepts.h>
#include <AK/FixedArray.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/OwnPtr.h>
#include <LibAudio/Queue.h>
#include <LibAudio/UserSampleQueue.h>
#include <LibCore/EventLoop.h>
#include <LibCore/EventReceiver.h>
#include <LibIPC/ConnectionToServer.h>
#include <LibThreading/Mutex.h>
#include <LibThreading/Thread.h>
#include <Userland/Services/AudioServer/AudioClientEndpoint.h>
#include <Userland/Services/AudioServer/AudioServerEndpoint.h>
namespace Audio {
class ConnectionToServer final
: public IPC::ConnectionToServer<AudioClientEndpoint, AudioServerEndpoint>
, 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<ArrayLike<Sample> Samples>
ErrorOr<void> async_enqueue(Samples&& samples)
{
return async_enqueue(TRY(FixedArray<Sample>::create(samples.span())));
}
ErrorOr<void> async_enqueue(FixedArray<Sample>&& 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<void, AudioQueue::QueueStatus> realtime_enqueue(Array<Sample, AUDIO_BUFFER_SIZE> samples);
ErrorOr<void> blocking_realtime_enqueue(Array<Sample, AUDIO_BUFFER_SIZE> samples, Function<void()> 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<void(double volume)> on_client_volume_change;
private:
ConnectionToServer(NonnullOwnPtr<Core::LocalSocket>);
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<AudioQueue> m_buffer;
// The queue of non-realtime audio provided by the user.
NonnullOwnPtr<UserSampleQueue> m_user_queue;
NonnullRefPtr<Threading::Thread> 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 {};
};
}

View file

@ -1,116 +0,0 @@
/*
* Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "PlaybackStreamSerenity.h"
#include <LibCore/ThreadedPromise.h>
namespace Audio {
ErrorOr<NonnullRefPtr<PlaybackStream>> 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<ConnectionToServer> stream, NonnullRefPtr<Core::Timer> 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<int>((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<Sample, AUDIO_BUFFER_SIZE> buffer;
buffer.fill({ 0.0f, 0.0f });
auto written_data = m_data_request_callback(Bytes { reinterpret_cast<u8*>(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<void()> callback)
{
// FIXME: Implement underrun callback in AudioServer
(void)callback;
}
NonnullRefPtr<Core::ThreadedPromise<Duration>> PlaybackStreamSerenity::resume()
{
auto promise = Core::ThreadedPromise<Duration>::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<Core::ThreadedPromise<void>> 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<void>::create();
m_connection->async_pause_playback();
m_polling_timer->stop();
promise->resolve();
return promise;
}
NonnullRefPtr<Core::ThreadedPromise<void>> PlaybackStreamSerenity::discard_buffer_and_suspend()
{
auto promise = Core::ThreadedPromise<void>::create();
m_connection->async_clear_buffer();
m_connection->async_pause_playback();
m_polling_timer->stop();
promise->resolve();
return promise;
}
ErrorOr<Duration> PlaybackStreamSerenity::total_time_played()
{
return Duration::from_milliseconds(m_number_of_samples_enqueued * 1000 / m_connection->get_self_sample_rate());
}
NonnullRefPtr<Core::ThreadedPromise<void>> PlaybackStreamSerenity::set_volume(double volume)
{
auto promise = Core::ThreadedPromise<void>::create();
m_connection->async_set_self_volume(volume);
promise->resolve();
return promise;
}
}

View file

@ -1,41 +0,0 @@
/*
* Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibAudio/ConnectionToServer.h>
#include <LibAudio/PlaybackStream.h>
namespace Audio {
class PlaybackStreamSerenity final
: public PlaybackStream {
public:
static ErrorOr<NonnullRefPtr<PlaybackStream>> create(OutputState initial_state, u32 sample_rate, u8 channels, u32 target_latency_ms, AudioDataRequestCallback&& data_request_callback);
virtual void set_underrun_callback(Function<void()>) override;
virtual NonnullRefPtr<Core::ThreadedPromise<Duration>> resume() override;
virtual NonnullRefPtr<Core::ThreadedPromise<void>> drain_buffer_and_suspend() override;
virtual NonnullRefPtr<Core::ThreadedPromise<void>> discard_buffer_and_suspend() override;
virtual ErrorOr<Duration> total_time_played() override;
virtual NonnullRefPtr<Core::ThreadedPromise<void>> set_volume(double) override;
private:
PlaybackStreamSerenity(NonnullRefPtr<ConnectionToServer>, NonnullRefPtr<Core::Timer> polling_timer, AudioDataRequestCallback&& data_request_callback);
void fill_buffers();
NonnullRefPtr<ConnectionToServer> m_connection;
size_t m_number_of_samples_enqueued { 0 };
NonnullRefPtr<Core::Timer> m_polling_timer;
AudioDataRequestCallback m_data_request_callback;
bool m_paused { false };
};
}