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:
parent
c169e43e13
commit
a9f3afc29b
Notes:
sideshowbarker
2024-07-17 07:25:39 +09:00
Author: https://github.com/circl-lastname
Commit: a9f3afc29b
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/115
7 changed files with 0 additions and 502 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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>);
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {};
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 };
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue