From fd126578d9be0b6d3c86227b491a270d091acb1b Mon Sep 17 00:00:00 2001 From: Cesar Torres Date: Fri, 26 Mar 2021 01:28:56 +0100 Subject: [PATCH] SoundPlayer: Add samplerate variable to visualizations also fix conflict --- .../SoundPlayer/BarsVisualizationWidget.cpp | 9 +- .../SoundPlayer/BarsVisualizationWidget.h | 5 +- .../Applications/SoundPlayer/CMakeLists.txt | 1 - Userland/Applications/SoundPlayer/Common.h | 8 +- .../SoundPlayer/PlaybackManager.cpp | 9 +- .../SoundPlayer/PlaybackManager.h | 1 + .../SoundPlayer/SoundPlayerWidget.cpp | 206 ------------------ .../SoundPlayer/SoundPlayerWidget.h | 70 ------ .../SoundPlayerWidgetAdvancedView.cpp | 5 + .../SoundPlayerWidgetAdvancedView.h | 12 +- .../SoundPlayer/VisualizationBase.h | 1 + Userland/Applications/SoundPlayer/main.cpp | 35 +-- 12 files changed, 51 insertions(+), 311 deletions(-) delete mode 100644 Userland/Applications/SoundPlayer/SoundPlayerWidget.cpp delete mode 100644 Userland/Applications/SoundPlayer/SoundPlayerWidget.h diff --git a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp b/Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp index bb197132412..5e42e0f75dc 100644 --- a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp +++ b/Userland/Applications/SoundPlayer/BarsVisualizationWidget.cpp @@ -49,8 +49,7 @@ void BarsVisualizationWidget::paint_event(GUI::PaintEvent& event) fft(m_sample_buffer, false); double max = sqrt(m_sample_count * 2); - //TODO: don't hardcode this! - double freq_bin = 44100 / m_sample_count; + double freq_bin = m_samplerate / m_sample_count; constexpr int group_count = 60; Vector groups; @@ -73,7 +72,7 @@ void BarsVisualizationWidget::paint_event(GUI::PaintEvent& event) const int horizontal_margin = 30; const int top_vertical_margin = 15; - const int pixels_inbetween_groups = 5; + const int pixels_inbetween_groups = frame_inner_rect().width() > 350 ? 5 : 2; int pixel_per_group_width = (frame_inner_rect().width() - horizontal_margin * 2 - pixels_inbetween_groups * (group_count - 1)) / group_count; int max_height = frame_inner_rect().height() - top_vertical_margin; int current_xpos = horizontal_margin; @@ -146,3 +145,7 @@ void BarsVisualizationWidget::mousedown_event(GUI::MouseEvent& event) } } +void BarsVisualizationWidget::set_samplerate(int samplerate) +{ + m_samplerate = samplerate; +} diff --git a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.h b/Userland/Applications/SoundPlayer/BarsVisualizationWidget.h index 914c2a115ca..4f90a381168 100644 --- a/Userland/Applications/SoundPlayer/BarsVisualizationWidget.h +++ b/Userland/Applications/SoundPlayer/BarsVisualizationWidget.h @@ -38,10 +38,12 @@ class BarsVisualizationWidget final : public GUI::Frame public: ~BarsVisualizationWidget() override; void set_buffer(RefPtr buffer) override; + void set_samplerate(int samplerate) override; private: - void set_buffer(RefPtr buffer, int samples_to_use); BarsVisualizationWidget(); + void set_buffer(RefPtr buffer, int samples_to_use); + void paint_event(GUI::PaintEvent&) override; void mousedown_event(GUI::MouseEvent& event) override; @@ -49,6 +51,7 @@ private: Vector m_gfx_falling_bars; int m_last_id; int m_sample_count; + int m_samplerate; bool m_is_using_last; bool m_adjust_frequencies; RefPtr m_context_menu; diff --git a/Userland/Applications/SoundPlayer/CMakeLists.txt b/Userland/Applications/SoundPlayer/CMakeLists.txt index 2596ea7fcc6..f0870bde575 100644 --- a/Userland/Applications/SoundPlayer/CMakeLists.txt +++ b/Userland/Applications/SoundPlayer/CMakeLists.txt @@ -2,7 +2,6 @@ set(SOURCES main.cpp PlaybackManager.cpp SampleWidget.cpp - SoundPlayerWidget.cpp SoundPlayerWidgetAdvancedView.cpp BarsVisualizationWidget.cpp AudioAlgorithms.cpp diff --git a/Userland/Applications/SoundPlayer/Common.h b/Userland/Applications/SoundPlayer/Common.h index 26f6ffdbba7..51b113e9067 100644 --- a/Userland/Applications/SoundPlayer/Common.h +++ b/Userland/Applications/SoundPlayer/Common.h @@ -28,10 +28,10 @@ #include -class Slider final : public GUI::Slider { - C_OBJECT(Slider) +class AutoSlider final : public GUI::Slider { + C_OBJECT(AutoSlider) public: - ~Slider() override = default; + ~AutoSlider() override = default; Function on_knob_released; void set_value(int value) { @@ -40,7 +40,7 @@ public: } protected: - Slider(Orientation orientation) + AutoSlider(Orientation orientation) : GUI::Slider(orientation) { } diff --git a/Userland/Applications/SoundPlayer/PlaybackManager.cpp b/Userland/Applications/SoundPlayer/PlaybackManager.cpp index 4e1c9c6fbb9..f6cdbf33de0 100644 --- a/Userland/Applications/SoundPlayer/PlaybackManager.cpp +++ b/Userland/Applications/SoundPlayer/PlaybackManager.cpp @@ -172,13 +172,10 @@ void PlaybackManager::next_buffer() remove_dead_buffers(); if (!m_next_buffer) { if (!m_connection->get_remaining_samples() && !m_paused) { - dbgln("Exhausted samples :^)"); - if (m_loop) - seek(0); - else - stop(); + stop(); + if (on_finished_playing) + on_finished_playing(); } - return; } diff --git a/Userland/Applications/SoundPlayer/PlaybackManager.h b/Userland/Applications/SoundPlayer/PlaybackManager.h index 8c851b093f9..49a72167e31 100644 --- a/Userland/Applications/SoundPlayer/PlaybackManager.h +++ b/Userland/Applications/SoundPlayer/PlaybackManager.h @@ -57,6 +57,7 @@ public: Function on_update; Function on_load_sample_buffer; + Function on_finished_playing; private: void next_buffer(); diff --git a/Userland/Applications/SoundPlayer/SoundPlayerWidget.cpp b/Userland/Applications/SoundPlayer/SoundPlayerWidget.cpp deleted file mode 100644 index a37c02d3888..00000000000 --- a/Userland/Applications/SoundPlayer/SoundPlayerWidget.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "SoundPlayerWidget.h" -#include "Common.h" -#include -#include -#include -#include -#include -#include - -SoundPlayerWidget::SoundPlayerWidget(GUI::Window& window, PlayerState& state) - : Player(state) - , m_window(window) -{ - window.set_resizable(false); - window.resize(350, 140); - - set_fill_with_background_color(true); - set_layout(); - layout()->set_margins({ 2, 2, 2, 2 }); - - auto& status_widget = add(); - status_widget.set_fill_with_background_color(true); - status_widget.set_layout(); - - m_elapsed = status_widget.add(); - m_elapsed->set_frame_shape(Gfx::FrameShape::Container); - m_elapsed->set_frame_shadow(Gfx::FrameShadow::Sunken); - m_elapsed->set_frame_thickness(2); - m_elapsed->set_fixed_width(80); - - auto& sample_widget_container = status_widget.add(); - sample_widget_container.set_layout(); - - m_sample_widget = sample_widget_container.add(); - - m_remaining = status_widget.add(); - m_remaining->set_frame_shape(Gfx::FrameShape::Container); - m_remaining->set_frame_shadow(Gfx::FrameShadow::Sunken); - m_remaining->set_frame_thickness(2); - m_remaining->set_fixed_width(80); - - m_slider = add(Orientation::Horizontal); - m_slider->set_min(0); - m_slider->set_enabled(has_loaded_file()); - m_slider->on_knob_released = [&](int value) { manager().seek(denormalize_rate(value)); }; - - auto& control_widget = add(); - control_widget.set_fill_with_background_color(true); - control_widget.set_layout(); - control_widget.set_fixed_height(30); - control_widget.layout()->set_margins({ 10, 2, 10, 2 }); - control_widget.layout()->set_spacing(10); - - m_play = control_widget.add(); - m_play->set_icon(has_loaded_file() ? *m_play_icon : *m_pause_icon); - m_play->set_enabled(has_loaded_file()); - m_play->on_click = [this](auto) { - bool paused = manager().toggle_pause(); - set_paused(paused); - m_play->set_icon(paused ? *m_play_icon : *m_pause_icon); - }; - - m_stop = control_widget.add(); - m_stop->set_enabled(has_loaded_file()); - m_stop->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/stop.png")); - m_stop->on_click = [this](auto) { - manager().stop(); - set_stopped(true); - }; - - m_status = add(); - m_status->set_frame_shape(Gfx::FrameShape::Box); - m_status->set_frame_shadow(Gfx::FrameShadow::Raised); - m_status->set_frame_thickness(4); - m_status->set_text_alignment(Gfx::TextAlignment::CenterLeft); - m_status->set_fixed_height(18); - m_status->set_text(has_loaded_file() ? loaded_filename() : "No file open!"); - - update_position(0); - - manager().on_update = [&]() { update_ui(); }; -} - -SoundPlayerWidget::~SoundPlayerWidget() -{ -} - -void SoundPlayerWidget::open_file(StringView path) -{ - NonnullRefPtr loader = Audio::Loader::create(path); - if (loader->has_error() || !loader->sample_rate()) { - const String error_string = loader->error_string(); - GUI::MessageBox::show(window(), - String::formatted("Failed to load audio file: {} ({})", path, error_string.is_null() ? "Unknown error" : error_string), - "Filetype error", GUI::MessageBox::Type::Error); - return; - } - - m_sample_ratio = PLAYBACK_MANAGER_RATE / static_cast(loader->sample_rate()); - - m_slider->set_max(normalize_rate(static_cast(loader->total_samples()))); - m_slider->set_enabled(true); - m_play->set_enabled(true); - m_stop->set_enabled(true); - - m_window.set_title(String::formatted("{} - SoundPlayer", loader->file()->filename())); - m_status->set_text(String::formatted( - "Sample rate {}Hz, {} channel(s), {} bits per sample", - loader->sample_rate(), - loader->num_channels(), - loader->bits_per_sample())); - - manager().set_loader(move(loader)); - update_position(0); - set_has_loaded_file(true); - set_loaded_filename(path); -} - -void SoundPlayerWidget::drop_event(GUI::DropEvent& event) -{ - event.accept(); - window()->move_to_front(); - - if (event.mime_data().has_urls()) { - auto urls = event.mime_data().urls(); - if (urls.is_empty()) - return; - open_file(urls.first().path()); - } -} - -int SoundPlayerWidget::normalize_rate(int rate) const -{ - return static_cast(rate * m_sample_ratio); -} - -int SoundPlayerWidget::denormalize_rate(int rate) const -{ - return static_cast(rate / m_sample_ratio); -} - -void SoundPlayerWidget::update_ui() -{ - m_sample_widget->set_buffer(manager().current_buffer()); - m_play->set_icon(manager().is_paused() ? *m_play_icon : *m_pause_icon); - update_position(manager().connection()->get_played_samples()); -} - -void SoundPlayerWidget::update_position(const int position) -{ - int total_norm_samples = position + normalize_rate(manager().last_seek()); - float seconds = (total_norm_samples / static_cast(PLAYBACK_MANAGER_RATE)); - float remaining_seconds = manager().total_length() - seconds; - - m_elapsed->set_text(String::formatted( - "Elapsed:\n{}:{:02}.{:02}", - static_cast(seconds / 60), - static_cast(seconds) % 60, - static_cast(seconds * 100) % 100)); - - m_remaining->set_text(String::formatted( - "Remaining:\n{}:{:02}.{:02}", - static_cast(remaining_seconds / 60), - static_cast(remaining_seconds) % 60, - static_cast(remaining_seconds * 100) % 100)); - - m_slider->set_value(total_norm_samples); -} - -void SoundPlayerWidget::hide_scope(bool hide) -{ - m_sample_widget->set_visible(!hide); -} - -void SoundPlayerWidget::play() -{ - manager().play(); - set_paused(false); - set_stopped(false); -} diff --git a/Userland/Applications/SoundPlayer/SoundPlayerWidget.h b/Userland/Applications/SoundPlayer/SoundPlayerWidget.h deleted file mode 100644 index 18f55176a8b..00000000000 --- a/Userland/Applications/SoundPlayer/SoundPlayerWidget.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include "Common.h" -#include "PlaybackManager.h" -#include "Player.h" -#include "SampleWidget.h" -#include -#include -#include -#include -#include - -class SoundPlayerWidget final : public GUI::Widget - , public Player { - C_OBJECT(SoundPlayerWidget) -public: - ~SoundPlayerWidget() override; - void open_file(StringView path) override; - void play() override; - void hide_scope(bool); - -private: - explicit SoundPlayerWidget(GUI::Window& window, PlayerState& state); - - void drop_event(GUI::DropEvent&) override; - - void update_position(const int position); - void update_ui(); - int normalize_rate(int) const; - int denormalize_rate(int) const; - - GUI::Window& m_window; - - float m_sample_ratio { 1.0 }; - RefPtr m_status; - RefPtr m_elapsed; - RefPtr m_remaining; - RefPtr m_slider; - RefPtr m_sample_widget; - RefPtr m_play_icon { Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png") }; - RefPtr m_pause_icon { Gfx::Bitmap::load_from_file("/res/icons/16x16/pause.png") }; - RefPtr m_play; - RefPtr m_stop; -}; diff --git a/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp b/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp index ee9accbc26c..bb9d73845b7 100644 --- a/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp +++ b/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -212,10 +213,14 @@ void SoundPlayerWidgetAdvancedView::open_file(StringView path) m_playback_progress_slider->set_max(loader->total_samples()); m_playback_progress_slider->set_enabled(true); m_play_button->set_enabled(true); + m_play_button->set_icon(*m_pause_icon); m_stop_button->set_enabled(true); + m_playback_progress_slider->set_max(loader->total_samples()); manager().set_loader(move(loader)); set_has_loaded_file(true); + set_loaded_file_samplerate(loader->sample_rate()); set_loaded_filename(path); + play(); } void SoundPlayerWidgetAdvancedView::set_nonlinear_volume_slider(bool nonlinear) diff --git a/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.h b/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.h index 128a352650d..26dd7e33b13 100644 --- a/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.h +++ b/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.h @@ -27,11 +27,12 @@ #pragma once #include "BarsVisualizationWidget.h" +#include "Common.h" #include "PlaybackManager.h" #include "Player.h" -#include "SoundPlayerWidget.h" #include #include +#include #include class SoundPlayerWidgetAdvancedView final : public GUI::Widget @@ -45,6 +46,9 @@ public: void open_file(StringView path) override; void read_playlist(StringView path); void play() override; + void set_nonlinear_volume_slider(bool nonlinear); + void set_playlist_visible(bool visible); + void try_fill_missing_info(Vector& entries, StringView playlist_p); template void set_visualization() @@ -52,12 +56,10 @@ public: m_visualization->remove_from_parent(); update(); auto new_visualization = T::construct(); - insert_child_before(new_visualization, *static_cast(m_playback_progress_slider.ptr())); + m_player_view->insert_child_before(new_visualization, *static_cast(m_playback_progress_slider.ptr())); m_visualization = new_visualization; } - void set_nonlinear_volume_slider(bool nonlinear); - private: void drop_event(GUI::DropEvent& event) override; GUI::Window& m_window; @@ -77,7 +79,7 @@ private: RefPtr m_stop_button; RefPtr m_back_button; RefPtr m_next_button; - RefPtr m_playback_progress_slider; + RefPtr m_playback_progress_slider; RefPtr m_volume_label; bool m_nonlinear_volume_slider; diff --git a/Userland/Applications/SoundPlayer/VisualizationBase.h b/Userland/Applications/SoundPlayer/VisualizationBase.h index 98d56a284d7..839a7d8b102 100644 --- a/Userland/Applications/SoundPlayer/VisualizationBase.h +++ b/Userland/Applications/SoundPlayer/VisualizationBase.h @@ -31,4 +31,5 @@ class Visualization { public: virtual void set_buffer(RefPtr buffer) = 0; + virtual void set_samplerate(int) { } }; diff --git a/Userland/Applications/SoundPlayer/main.cpp b/Userland/Applications/SoundPlayer/main.cpp index 3242a37f1c5..00ccd2bfcbe 100644 --- a/Userland/Applications/SoundPlayer/main.cpp +++ b/Userland/Applications/SoundPlayer/main.cpp @@ -26,7 +26,7 @@ #include "NoVisualizationWidget.h" #include "Player.h" -#include "SoundPlayerWidget.h" +#include "SampleWidget.h" #include "SoundPlayerWidgetAdvancedView.h" #include #include @@ -54,21 +54,24 @@ int main(int argc, char** argv) auto audio_client = Audio::ClientConnection::construct(); audio_client->handshake(); - PlaybackManager playback_manager(audio_client); - PlayerState initial_player_state { true, - true, - false, - false, - 1.0, - audio_client, - playback_manager, - "" }; if (pledge("stdio recvfd sendfd accept rpath thread", nullptr) < 0) { perror("pledge"); return 1; } + PlaybackManager playback_manager(audio_client); + PlayerState initial_player_state { true, + true, + false, + false, + false, + 44100, + 1.0, + audio_client, + playback_manager, + "" }; + auto app_icon = GUI::Icon::default_icon("app-sound-player"); auto window = GUI::Window::construct(); @@ -78,12 +81,14 @@ int main(int argc, char** argv) auto menubar = GUI::MenuBar::construct(); auto& app_menu = menubar->add_menu("File"); - // start in simple view by default - Player* player = &window->set_main_widget(window, initial_player_state); + + auto& playlist_menu = menubar->add_menu("Playlist"); + + String path = argv[1]; + // start in advanced view by default + Player* player = &window->set_main_widget(window, initial_player_state); if (argc > 1) { - String path = argv[1]; player->open_file(path); - player->play(); } app_menu.add_action(GUI::CommonActions::make_open_action([&](auto&) { @@ -119,7 +124,7 @@ int main(int argc, char** argv) auto& playback_menu = menubar->add_menu("Playback"); auto loop = GUI::Action::create_checkable("Loop", { Mod_Ctrl, Key_R }, [&](auto& action) { - player->set_looping(action.is_checked()); + player->set_looping_file(action.is_checked()); }); playback_menu.add_action(move(loop));