diff --git a/Userland/Applications/SoundPlayer/PlaybackManager.cpp b/Userland/Applications/SoundPlayer/PlaybackManager.cpp index 0d7277de1a..9655741211 100644 --- a/Userland/Applications/SoundPlayer/PlaybackManager.cpp +++ b/Userland/Applications/SoundPlayer/PlaybackManager.cpp @@ -9,7 +9,7 @@ PlaybackManager::PlaybackManager(NonnullRefPtr connection) : m_connection(connection) { - m_timer = Core::Timer::construct(100, [&]() { + m_timer = Core::Timer::construct(PlaybackManager::update_rate_ms, [&]() { if (!m_loader) return; next_buffer(); @@ -27,8 +27,10 @@ void PlaybackManager::set_loader(NonnullRefPtr&& loader) m_loader = loader; if (m_loader) { m_total_length = m_loader->total_samples() / static_cast(m_loader->sample_rate()); + m_device_samples_per_buffer = PlaybackManager::buffer_size_ms / 1000.0f * m_device_sample_rate; + u32 source_samples_per_buffer = PlaybackManager::buffer_size_ms / 1000.0f * m_loader->sample_rate(); + m_source_buffer_size_bytes = source_samples_per_buffer * m_loader->num_channels() * m_loader->bits_per_sample() / 8; m_timer->start(); - load_next_buffer(); } else { m_timer->stop(); } @@ -38,11 +40,8 @@ void PlaybackManager::stop() { set_paused(true); m_connection->clear_buffer(true); - m_buffers.clear(); m_last_seek = 0; - m_next_buffer = nullptr; m_current_buffer = nullptr; - m_next_ptr = 0; if (m_loader) m_loader->reset(); @@ -68,10 +67,7 @@ void PlaybackManager::seek(const int position) set_paused(true); m_connection->clear_buffer(true); - m_next_buffer = nullptr; m_current_buffer = nullptr; - m_next_ptr = 0; - m_buffers.clear(); m_loader->seek(position); if (!paused_state) @@ -83,51 +79,8 @@ void PlaybackManager::pause() set_paused(true); } -void PlaybackManager::remove_dead_buffers() -{ - int id = m_connection->get_playing_buffer(); - int current_id = -1; - if (m_current_buffer) - current_id = m_current_buffer->id(); - - if (id >= 0 && id != current_id) { - while (!m_buffers.is_empty()) { - --m_next_ptr; - auto buffer = m_buffers.take_first(); - - if (buffer->id() == id) { - m_current_buffer = buffer; - break; - } - } - } -} - -void PlaybackManager::load_next_buffer() -{ - if (m_buffers.size() < 10) { - for (int i = 0; i < 20 && m_loader->loaded_samples() < m_loader->total_samples(); i++) { - auto buffer = m_loader->get_more_samples(PLAYBACK_MANAGER_BUFFER_SIZE); - if (buffer) { - m_buffers.append(buffer); - } - } - } - - if (m_next_ptr < m_buffers.size()) { - m_next_buffer = m_buffers.at(m_next_ptr++); - if (on_load_sample_buffer) - on_load_sample_buffer(*m_next_buffer); - } else { - m_next_buffer = nullptr; - } -} - void PlaybackManager::set_paused(bool paused) { - if (!m_next_buffer && m_loader) - load_next_buffer(); - m_paused = paused; m_connection->set_paused(paused); } @@ -146,22 +99,24 @@ void PlaybackManager::next_buffer() { if (on_update) on_update(); + if (m_paused) return; - remove_dead_buffers(); - if (!m_next_buffer) { - if (!m_connection->get_remaining_samples() && !m_paused) { - stop(); - if (on_finished_playing) - on_finished_playing(); - } + u32 audio_server_remaining_samples = m_connection->get_remaining_samples(); + bool all_samples_loaded = (m_loader->loaded_samples() >= m_loader->total_samples()); + bool audio_server_done = (audio_server_remaining_samples == 0); + + if (all_samples_loaded && audio_server_done) { + stop(); + if (on_finished_playing) + on_finished_playing(); return; } - bool enqueued = m_connection->try_enqueue(*m_next_buffer); - if (!enqueued) - return; - - load_next_buffer(); + if (audio_server_remaining_samples < m_device_samples_per_buffer) { + m_current_buffer = m_loader->get_more_samples(m_source_buffer_size_bytes); + if (m_current_buffer) + m_connection->enqueue(*m_current_buffer); + } } diff --git a/Userland/Applications/SoundPlayer/PlaybackManager.h b/Userland/Applications/SoundPlayer/PlaybackManager.h index 6b333e813a..35afc3fa2c 100644 --- a/Userland/Applications/SoundPlayer/PlaybackManager.h +++ b/Userland/Applications/SoundPlayer/PlaybackManager.h @@ -12,9 +12,6 @@ #include #include -#define PLAYBACK_MANAGER_BUFFER_SIZE 48 * KiB -#define PLAYBACK_MANAGER_RATE 44100 - class PlaybackManager final { public: PlaybackManager(NonnullRefPtr); @@ -27,6 +24,7 @@ public: void loop(bool); bool toggle_pause(); void set_loader(NonnullRefPtr&&); + size_t device_sample_rate() const { return m_device_sample_rate; } int last_seek() const { return m_last_seek; } bool is_paused() const { return m_paused; } @@ -42,18 +40,23 @@ public: private: void next_buffer(); void set_paused(bool); - void load_next_buffer(); - void remove_dead_buffers(); bool m_paused { true }; bool m_loop = { false }; - size_t m_next_ptr { 0 }; size_t m_last_seek { 0 }; float m_total_length { 0 }; + // FIXME: Get this from the audio server + size_t m_device_sample_rate { 44100 }; + size_t m_device_samples_per_buffer { 0 }; + size_t m_source_buffer_size_bytes { 0 }; RefPtr m_loader { nullptr }; NonnullRefPtr m_connection; - RefPtr m_next_buffer; RefPtr m_current_buffer; - Vector> m_buffers; RefPtr m_timer; + + // Controls the GUI update rate. A smaller value makes the visualizations nicer. + static constexpr u32 update_rate_ms = 50; + + // Number of milliseconds of audio data contained in each audio buffer + static constexpr u32 buffer_size_ms = 100; }; diff --git a/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp b/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp index fc67aab216..b0f2cd4751 100644 --- a/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp +++ b/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp @@ -49,7 +49,7 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window // Set a temporary value for total samples. // This value will be set properly when we load a new file. - const int total_samples = this->manager().total_length() * 44100; + const int total_samples = this->manager().total_length() * this->manager().device_sample_rate(); m_playback_progress_slider = m_player_view->add(Orientation::Horizontal); m_playback_progress_slider->set_fixed_height(20); @@ -142,16 +142,20 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window set_nonlinear_volume_slider(false); manager().on_update = [&]() { - //TODO: make this program support other sample rates - int samples_played = client_connection().get_played_samples() + this->manager().last_seek(); - int current_second = samples_played / 44100; + // Determine how many of the source file samples have played. + int samples_played = client_connection().get_played_samples(); + float source_to_dest_ratio = static_cast(loaded_file_samplerate()) / manager().device_sample_rate(); + samples_played *= source_to_dest_ratio; + samples_played += this->manager().last_seek(); + + int current_second = samples_played / loaded_file_samplerate(); timestamp_label.set_text(String::formatted("Elapsed: {:02}:{:02}:{:02}", current_second / 3600, current_second / 60, current_second % 60)); if (!m_playback_progress_slider->mouse_is_down()) { m_playback_progress_slider->set_value(samples_played); } dynamic_cast(m_visualization.ptr())->set_buffer(this->manager().current_buffer()); - dynamic_cast(m_visualization.ptr())->set_samplerate(loaded_file_samplerate()); + dynamic_cast(m_visualization.ptr())->set_samplerate(manager().device_sample_rate()); }; manager().on_load_sample_buffer = [&](Audio::Buffer&) { @@ -176,6 +180,8 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window } else open_file((it + 1)->path); } + + m_stop_button->set_enabled(false); }; } diff --git a/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.h b/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.h index 1d4e4b2b38..feec4bbfdb 100644 --- a/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.h +++ b/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.h @@ -63,4 +63,5 @@ private: RefPtr m_volume_label; bool m_nonlinear_volume_slider; + size_t m_device_sample_rate { 44100 }; }; diff --git a/Userland/Libraries/LibAudio/ClientConnection.cpp b/Userland/Libraries/LibAudio/ClientConnection.cpp index 2947dfbeb9..633b09c20f 100644 --- a/Userland/Libraries/LibAudio/ClientConnection.cpp +++ b/Userland/Libraries/LibAudio/ClientConnection.cpp @@ -20,7 +20,7 @@ void ClientConnection::enqueue(const Buffer& buffer) auto success = enqueue_buffer(buffer.anonymous_buffer(), buffer.id(), buffer.sample_count()); if (success) break; - sleep(.1); + usleep(100000); } } diff --git a/Userland/Libraries/LibAudio/WavLoader.cpp b/Userland/Libraries/LibAudio/WavLoader.cpp index 0896b5d57c..3353549e20 100644 --- a/Userland/Libraries/LibAudio/WavLoader.cpp +++ b/Userland/Libraries/LibAudio/WavLoader.cpp @@ -30,7 +30,7 @@ WavLoaderPlugin::WavLoaderPlugin(const StringView& path) if (!valid) return; - m_resampler = make(m_sample_rate, 44100); + m_resampler = make(m_sample_rate, m_device_sample_rate); } WavLoaderPlugin::WavLoaderPlugin(const ByteBuffer& buffer) @@ -46,7 +46,7 @@ WavLoaderPlugin::WavLoaderPlugin(const ByteBuffer& buffer) if (!valid) return; - m_resampler = make(m_sample_rate, 44100); + m_resampler = make(m_sample_rate, m_device_sample_rate); } RefPtr WavLoaderPlugin::get_more_samples(size_t max_bytes_to_read_from_input) diff --git a/Userland/Libraries/LibAudio/WavLoader.h b/Userland/Libraries/LibAudio/WavLoader.h index 3fa84c6ff9..db333b571f 100644 --- a/Userland/Libraries/LibAudio/WavLoader.h +++ b/Userland/Libraries/LibAudio/WavLoader.h @@ -41,6 +41,8 @@ public: virtual bool has_error() override { return !m_error_string.is_null(); } virtual const char* error_string() override { return m_error_string.characters(); } + // The Buffer returned contains input data resampled at the + // destination audio device sample rate. virtual RefPtr get_more_samples(size_t max_bytes_to_read_from_input = 128 * KiB) override; virtual void reset() override { return seek(0); } @@ -64,6 +66,11 @@ private: OwnPtr m_stream; AK::InputMemoryStream* m_memory_stream; String m_error_string; + + // TODO: We should probably move resampling into the audio server. + // + // It would avoid duplicate resampling code and would allow clients + // to be agnostic of the destination audio device's sample rate. OwnPtr m_resampler; u32 m_sample_rate { 0 }; @@ -71,6 +78,8 @@ private: PcmSampleFormat m_sample_format; size_t m_byte_offset_of_data_samples { 0 }; + // FIXME: Get this value from the audio server + int m_device_sample_rate { 44100 }; int m_loaded_samples { 0 }; int m_total_samples { 0 }; };