Serenity Operating System
at master 159 lines 4.8 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/Atomic.h> 9#include <AK/Format.h> 10#include <AK/OwnPtr.h> 11#include <AK/Time.h> 12#include <AK/Types.h> 13#include <LibAudio/ConnectionToServer.h> 14#include <LibAudio/Queue.h> 15#include <LibAudio/UserSampleQueue.h> 16#include <LibCore/Event.h> 17#include <LibThreading/Mutex.h> 18#include <sched.h> 19#include <time.h> 20 21namespace Audio { 22 23ConnectionToServer::ConnectionToServer(NonnullOwnPtr<Core::LocalSocket> socket) 24 : IPC::ConnectionToServer<AudioClientEndpoint, AudioServerEndpoint>(*this, move(socket)) 25 , m_buffer(make<AudioQueue>(MUST(AudioQueue::create()))) 26 , m_user_queue(make<UserSampleQueue>()) 27 , m_background_audio_enqueuer(Threading::Thread::construct([this]() { 28 // All the background thread does is run an event loop. 29 Core::EventLoop enqueuer_loop; 30 m_enqueuer_loop = &enqueuer_loop; 31 enqueuer_loop.exec(); 32 { 33 Threading::MutexLocker const locker(m_enqueuer_loop_destruction); 34 m_enqueuer_loop = nullptr; 35 } 36 return (intptr_t) nullptr; 37 })) 38{ 39 async_pause_playback(); 40 set_buffer(*m_buffer); 41} 42 43ConnectionToServer::~ConnectionToServer() 44{ 45 die(); 46} 47 48void ConnectionToServer::die() 49{ 50 { 51 Threading::MutexLocker const locker(m_enqueuer_loop_destruction); 52 // We're sometimes getting here after the other thread has already exited and its event loop does no longer exist. 53 if (m_enqueuer_loop != nullptr) { 54 m_enqueuer_loop->wake(); 55 m_enqueuer_loop->quit(0); 56 } 57 } 58 if (m_background_audio_enqueuer->is_started()) 59 (void)m_background_audio_enqueuer->join(); 60} 61 62ErrorOr<void> ConnectionToServer::async_enqueue(FixedArray<Sample>&& samples) 63{ 64 if (!m_background_audio_enqueuer->is_started()) { 65 m_background_audio_enqueuer->start(); 66 TRY(m_background_audio_enqueuer->set_priority(THREAD_PRIORITY_MAX)); 67 } 68 69 update_good_sleep_time(); 70 m_user_queue->append(move(samples)); 71 // Wake the background thread to make sure it starts enqueuing audio. 72 m_enqueuer_loop->wake_once(*this, 0); 73 async_start_playback(); 74 75 return {}; 76} 77 78void ConnectionToServer::clear_client_buffer() 79{ 80 m_user_queue->clear(); 81} 82 83void ConnectionToServer::update_good_sleep_time() 84{ 85 auto sample_rate = static_cast<double>(get_sample_rate()); 86 auto buffer_play_time_ns = 1'000'000'000.0 / (sample_rate / static_cast<double>(AUDIO_BUFFER_SIZE)); 87 // A factor of 1 should be good for now. 88 m_good_sleep_time = Time::from_nanoseconds(static_cast<unsigned>(buffer_play_time_ns)).to_timespec(); 89} 90 91// Non-realtime audio writing loop 92void ConnectionToServer::custom_event(Core::CustomEvent&) 93{ 94 Array<Sample, AUDIO_BUFFER_SIZE> next_chunk; 95 while (true) { 96 if (m_user_queue->is_empty()) { 97 dbgln("Reached end of provided audio data, going to sleep"); 98 break; 99 } 100 101 auto available_samples = min(AUDIO_BUFFER_SIZE, m_user_queue->size()); 102 for (size_t i = 0; i < available_samples; ++i) 103 next_chunk[i] = (*m_user_queue)[i]; 104 105 m_user_queue->discard_samples(available_samples); 106 107 // FIXME: Could we receive interrupts in a good non-IPC way instead? 108 auto result = m_buffer->blocking_enqueue(next_chunk, [this]() { 109 nanosleep(&m_good_sleep_time, nullptr); 110 }); 111 if (result.is_error()) 112 dbgln("Error while writing samples to shared buffer: {}", result.error()); 113 } 114} 115 116ErrorOr<void, AudioQueue::QueueStatus> ConnectionToServer::realtime_enqueue(Array<Sample, AUDIO_BUFFER_SIZE> samples) 117{ 118 return m_buffer->enqueue(samples); 119} 120 121ErrorOr<void> ConnectionToServer::blocking_realtime_enqueue(Array<Sample, AUDIO_BUFFER_SIZE> samples, Function<void()> wait_function) 122{ 123 return m_buffer->blocking_enqueue(samples, move(wait_function)); 124} 125 126unsigned ConnectionToServer::total_played_samples() const 127{ 128 return m_buffer->weak_tail() * AUDIO_BUFFER_SIZE; 129} 130 131unsigned ConnectionToServer::remaining_samples() 132{ 133 return static_cast<unsigned>(m_user_queue->remaining_samples()); 134} 135 136size_t ConnectionToServer::remaining_buffers() const 137{ 138 return m_buffer->size() - m_buffer->weak_remaining_capacity(); 139} 140 141void ConnectionToServer::main_mix_muted_state_changed(bool muted) 142{ 143 if (on_main_mix_muted_state_change) 144 on_main_mix_muted_state_change(muted); 145} 146 147void ConnectionToServer::main_mix_volume_changed(double volume) 148{ 149 if (on_main_mix_volume_change) 150 on_main_mix_volume_change(volume); 151} 152 153void ConnectionToServer::client_volume_changed(double volume) 154{ 155 if (on_client_volume_change) 156 on_client_volume_change(volume); 157} 158 159}