Serenity Operating System
at master 131 lines 3.6 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include "PlaybackManager.h" 9 10PlaybackManager::PlaybackManager(NonnullRefPtr<Audio::ConnectionToServer> connection) 11 : m_connection(connection) 12{ 13 // FIXME: The buffer enqueuing should happen on a wholly independent second thread. 14 m_timer = Core::Timer::create_repeating(PlaybackManager::update_rate_ms, [&]() { 15 if (!m_loader) 16 return; 17 next_buffer(); 18 }).release_value_but_fixme_should_propagate_errors(); 19 m_device_sample_rate = connection->get_sample_rate(); 20} 21 22void PlaybackManager::set_loader(NonnullRefPtr<Audio::Loader>&& loader) 23{ 24 stop(); 25 m_loader = loader; 26 if (m_loader) { 27 m_total_length = m_loader->total_samples() / static_cast<float>(m_loader->sample_rate()); 28 m_device_samples_per_buffer = PlaybackManager::buffer_size_ms / 1000.0f * m_device_sample_rate; 29 m_samples_to_load_per_buffer = PlaybackManager::buffer_size_ms / 1000.0f * m_loader->sample_rate(); 30 m_resampler = Audio::ResampleHelper<Audio::Sample>(m_loader->sample_rate(), m_device_sample_rate); 31 m_timer->start(); 32 } else { 33 m_timer->stop(); 34 } 35} 36 37void PlaybackManager::stop() 38{ 39 set_paused(true); 40 m_connection->async_clear_buffer(); 41 42 if (m_loader) 43 (void)m_loader->reset(); 44} 45 46void PlaybackManager::play() 47{ 48 set_paused(false); 49} 50 51void PlaybackManager::loop(bool loop) 52{ 53 m_loop = loop; 54} 55 56void PlaybackManager::seek(int const position) 57{ 58 if (!m_loader) 59 return; 60 61 bool paused_state = m_paused; 62 set_paused(true); 63 64 [[maybe_unused]] auto result = m_loader->seek(position); 65 66 m_connection->clear_client_buffer(); 67 m_connection->async_clear_buffer(); 68 if (!paused_state) 69 set_paused(false); 70} 71 72void PlaybackManager::pause() 73{ 74 set_paused(true); 75} 76 77void PlaybackManager::set_paused(bool paused) 78{ 79 m_paused = paused; 80 if (m_paused) 81 m_connection->async_pause_playback(); 82 else 83 m_connection->async_start_playback(); 84} 85 86bool PlaybackManager::toggle_pause() 87{ 88 if (m_paused) { 89 play(); 90 } else { 91 pause(); 92 } 93 return m_paused; 94} 95 96void PlaybackManager::next_buffer() 97{ 98 if (on_update) 99 on_update(); 100 101 if (m_paused) 102 return; 103 104 while (m_connection->remaining_samples() < m_device_samples_per_buffer * always_enqueued_buffer_count) { 105 bool all_samples_loaded = (m_loader->loaded_samples() >= m_loader->total_samples()); 106 bool audio_server_done = (m_connection->remaining_samples() == 0); 107 108 if (all_samples_loaded && audio_server_done) { 109 stop(); 110 if (on_finished_playing) 111 on_finished_playing(); 112 return; 113 } 114 115 auto buffer_or_error = m_loader->get_more_samples(m_samples_to_load_per_buffer); 116 if (buffer_or_error.is_error()) { 117 // FIXME: These errors should be shown to the user instead of being logged and then ignored 118 dbgln("Error while loading samples: {}", buffer_or_error.error().description); 119 return; 120 } 121 auto buffer = buffer_or_error.release_value(); 122 m_current_buffer.swap(buffer); 123 VERIFY(m_resampler.has_value()); 124 125 m_resampler->reset(); 126 // FIXME: Handle OOM better. 127 auto resampled = MUST(FixedArray<Audio::Sample>::create(m_resampler->resample(move(m_current_buffer)).span())); 128 m_current_buffer.swap(resampled); 129 MUST(m_connection->async_enqueue(m_current_buffer)); 130 } 131}