瀏覽代碼

AudioPlayer: Further decouple the player from the GUI

Cesar Torres 4 年之前
父節點
當前提交
e4d6a56a28

+ 40 - 3
Userland/Applications/SoundPlayer/Player.h

@@ -28,11 +28,48 @@
 
 #include "PlaybackManager.h"
 #include "VisualizationBase.h"
+#include <AK/RefPtr.h>
+
+struct PlayerState {
+    bool is_paused;
+    bool is_stopped;
+    bool has_loaded_file;
+    bool is_looping;
+    double volume;
+    Audio::ClientConnection& connection;
+    PlaybackManager& manager;
+    StringView loaded_filename;
+};
 
 class Player {
 public:
-    explicit Player() = default;
+    explicit Player(PlayerState& state)
+        : m_player_state(state) {};
     virtual void open_file(StringView path) = 0;
-    virtual Audio::ClientConnection& client_connection() = 0;
-    virtual PlaybackManager& playback_manager() = 0;
+    virtual void play() = 0;
+
+    PlayerState& get_player_state() { return m_player_state; }
+    bool is_stopped() const { return m_player_state.is_stopped; }
+    bool is_paused() const { return m_player_state.is_paused; }
+    bool has_loaded_file() const { return m_player_state.has_loaded_file; }
+    double volume() const { return m_player_state.volume; }
+    bool looping() const { return m_player_state.is_looping; }
+    StringView& loaded_filename() { return m_player_state.loaded_filename; }
+
+    virtual void set_stopped(bool stopped) { m_player_state.is_stopped = stopped; }
+    virtual void set_paused(bool paused) { m_player_state.is_paused = paused; }
+    virtual void set_has_loaded_file(bool loaded) { m_player_state.has_loaded_file = loaded; }
+    virtual void set_volume(double volume) { m_player_state.volume = volume; }
+    virtual void set_looping(bool loop)
+    {
+        m_player_state.is_looping = loop;
+        manager().loop(loop);
+    }
+    virtual void set_loaded_filename(StringView& filename) { m_player_state.loaded_filename = filename; }
+
+    Audio::ClientConnection& client_connection() { return m_player_state.connection; }
+    PlaybackManager& manager() { return m_player_state.manager; }
+
+protected:
+    PlayerState m_player_state;
 };

+ 32 - 19
Userland/Applications/SoundPlayer/SoundPlayerWidget.cpp

@@ -33,10 +33,9 @@
 #include <LibGUI/Label.h>
 #include <LibGUI/MessageBox.h>
 
-SoundPlayerWidget::SoundPlayerWidget(GUI::Window& window, Audio::ClientConnection& connection, PlaybackManager& manager)
-    : m_window(window)
-    , m_connection(connection)
-    , m_manager(manager)
+SoundPlayerWidget::SoundPlayerWidget(GUI::Window& window, PlayerState& state)
+    : Player(state)
+    , m_window(window)
 {
     window.set_resizable(false);
     window.resize(350, 140);
@@ -68,8 +67,8 @@ SoundPlayerWidget::SoundPlayerWidget(GUI::Window& window, Audio::ClientConnectio
 
     m_slider = add<Slider>(Orientation::Horizontal);
     m_slider->set_min(0);
-    m_slider->set_enabled(false);
-    m_slider->on_knob_released = [&](int value) { m_manager.seek(denormalize_rate(value)); };
+    m_slider->set_enabled(has_loaded_file());
+    m_slider->on_knob_released = [&](int value) { manager().seek(denormalize_rate(value)); };
 
     auto& control_widget = add<GUI::Widget>();
     control_widget.set_fill_with_background_color(true);
@@ -79,16 +78,21 @@ SoundPlayerWidget::SoundPlayerWidget(GUI::Window& window, Audio::ClientConnectio
     control_widget.layout()->set_spacing(10);
 
     m_play = control_widget.add<GUI::Button>();
-    m_play->set_icon(*m_pause_icon);
-    m_play->set_enabled(false);
+    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) {
-        m_play->set_icon(m_manager.toggle_pause() ? *m_play_icon : *m_pause_icon);
+        bool paused = manager().toggle_pause();
+        set_paused(paused);
+        m_play->set_icon(paused ? *m_play_icon : *m_pause_icon);
     };
 
     m_stop = control_widget.add<GUI::Button>();
-    m_stop->set_enabled(false);
+    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) { m_manager.stop(); };
+    m_stop->on_click = [this](auto) {
+        manager().stop();
+        set_stopped(true);
+    };
 
     m_status = add<GUI::Label>();
     m_status->set_frame_shape(Gfx::FrameShape::Box);
@@ -96,11 +100,11 @@ SoundPlayerWidget::SoundPlayerWidget(GUI::Window& window, Audio::ClientConnectio
     m_status->set_frame_thickness(4);
     m_status->set_text_alignment(Gfx::TextAlignment::CenterLeft);
     m_status->set_fixed_height(18);
-    m_status->set_text("No file open!");
+    m_status->set_text(has_loaded_file() ? loaded_filename() : "No file open!");
 
     update_position(0);
 
-    m_manager.on_update = [&]() { update_ui(); };
+    manager().on_update = [&]() { update_ui(); };
 }
 
 SoundPlayerWidget::~SoundPlayerWidget()
@@ -132,8 +136,10 @@ void SoundPlayerWidget::open_file(StringView path)
         loader->num_channels(),
         loader->bits_per_sample()));
 
-    m_manager.set_loader(move(loader));
+    manager().set_loader(move(loader));
     update_position(0);
+    set_has_loaded_file(true);
+    set_loaded_filename(path);
 }
 
 void SoundPlayerWidget::drop_event(GUI::DropEvent& event)
@@ -161,16 +167,16 @@ int SoundPlayerWidget::denormalize_rate(int rate) const
 
 void SoundPlayerWidget::update_ui()
 {
-    m_sample_widget->set_buffer(m_manager.current_buffer());
-    m_play->set_icon(m_manager.is_paused() ? *m_play_icon : *m_pause_icon);
-    update_position(m_manager.connection()->get_played_samples());
+    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(m_manager.last_seek());
+    int total_norm_samples = position + normalize_rate(manager().last_seek());
     float seconds = (total_norm_samples / static_cast<float>(PLAYBACK_MANAGER_RATE));
-    float remaining_seconds = m_manager.total_length() - seconds;
+    float remaining_seconds = manager().total_length() - seconds;
 
     m_elapsed->set_text(String::formatted(
         "Elapsed:\n{}:{:02}.{:02}",
@@ -191,3 +197,10 @@ void SoundPlayerWidget::hide_scope(bool hide)
 {
     m_sample_widget->set_visible(!hide);
 }
+
+void SoundPlayerWidget::play()
+{
+    manager().play();
+    set_paused(false);
+    set_stopped(false);
+}

+ 4 - 7
Userland/Applications/SoundPlayer/SoundPlayerWidget.h

@@ -40,16 +40,15 @@ class SoundPlayerWidget final : public GUI::Widget
     , public Player {
     C_OBJECT(SoundPlayerWidget)
 public:
-    virtual ~SoundPlayerWidget() override;
+    ~SoundPlayerWidget() override;
     void open_file(StringView path) override;
+    void play() override;
     void hide_scope(bool);
-    Audio::ClientConnection& client_connection() override { return m_connection; }
-    PlaybackManager& playback_manager() override { return m_manager; }
 
 private:
-    explicit SoundPlayerWidget(GUI::Window& window, Audio::ClientConnection& connection, PlaybackManager& manager);
+    explicit SoundPlayerWidget(GUI::Window& window, PlayerState& state);
 
-    virtual void drop_event(GUI::DropEvent&) override;
+    void drop_event(GUI::DropEvent&) override;
 
     void update_position(const int position);
     void update_ui();
@@ -57,8 +56,6 @@ private:
     int denormalize_rate(int) const;
 
     GUI::Window& m_window;
-    Audio::ClientConnection& m_connection;
-    PlaybackManager& m_manager;
 
     float m_sample_ratio { 1.0 };
     RefPtr<GUI::Label> m_status;

+ 42 - 32
Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp

@@ -41,12 +41,10 @@
 #include <LibGUI/Window.h>
 #include <LibGfx/Bitmap.h>
 
-SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window, Audio::ClientConnection& connection, PlaybackManager& manager)
-    : m_window(window)
-    , m_connection(connection)
-    , m_manager(manager)
+SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window, PlayerState& state)
+    : Player(state)
+    , m_window(window)
 {
-
     window.resize(455, 350);
     window.set_minimum_size(440, 130);
     window.set_resizable(true);
@@ -65,9 +63,9 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window
     m_playback_progress_slider = add<Slider>(Orientation::Horizontal);
     m_playback_progress_slider->set_fixed_height(20);
     m_playback_progress_slider->set_min(0);
-    m_playback_progress_slider->set_max(m_manager.total_length() * 44100); //this value should be set when we load a new file
+    m_playback_progress_slider->set_max(this->manager().total_length() * 44100); //this value should be set when we load a new file
     m_playback_progress_slider->on_knob_released = [&](int value) {
-        m_manager.seek(value);
+        this->manager().seek(value);
     };
 
     auto& toolbar_container = add<GUI::ToolBarContainer>();
@@ -75,20 +73,22 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window
     auto& menubar = toolbar_container.add<GUI::ToolBar>();
 
     m_play_button = menubar.add<GUI::Button>();
-    m_play_button->set_icon(*m_play_icon);
+    m_play_button->set_icon(is_paused() ? (!has_loaded_file() ? *m_play_icon : *m_pause_icon) : *m_pause_icon);
     m_play_button->set_fixed_width(50);
-
+    m_play_button->set_enabled(has_loaded_file());
     m_play_button->on_click = [&](unsigned) {
-        bool paused = m_manager.toggle_pause();
+        bool paused = this->manager().toggle_pause();
+        set_paused(paused);
         m_play_button->set_icon(paused ? *m_play_icon : *m_pause_icon);
-        m_stop_button->set_enabled(!paused);
     };
 
     m_stop_button = menubar.add<GUI::Button>();
     m_stop_button->set_icon(*m_stop_icon);
     m_stop_button->set_fixed_width(50);
+    m_stop_button->set_enabled(has_loaded_file());
     m_stop_button->on_click = [&](unsigned) {
-        m_manager.stop();
+        this->manager().stop();
+        set_stopped(true);
         m_play_button->set_icon(*m_play_icon);
         m_stop_button->set_enabled(false);
     };
@@ -100,13 +100,15 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window
     // filler_label
     menubar.add<GUI::Label>();
 
-    auto& back_button = menubar.add<GUI::Button>();
-    back_button.set_fixed_width(50);
-    back_button.set_icon(*m_back_icon);
+    m_back_button = menubar.add<GUI::Button>();
+    m_back_button->set_fixed_width(50);
+    m_back_button->set_icon(*m_back_icon);
+    m_back_button->set_enabled(has_loaded_file());
 
-    auto& next_button = menubar.add<GUI::Button>();
-    next_button.set_fixed_width(50);
-    next_button.set_icon(*m_next_icon);
+    m_next_button = menubar.add<GUI::Button>();
+    m_next_button->set_fixed_width(50);
+    m_next_button->set_icon(*m_next_icon);
+    m_next_button->set_enabled(has_loaded_file());
 
     m_volume_label = &menubar.add<GUI::Label>();
     m_volume_label->set_fixed_width(30);
@@ -127,41 +129,36 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window
     set_volume(1.);
     set_nonlinear_volume_slider(false);
 
-    m_manager.on_update = [&]() {
+    manager().on_update = [&]() {
         //TODO: make this program support other sample rates
-        int samples_played = m_connection.get_played_samples() + m_manager.last_seek();
+        int samples_played = client_connection().get_played_samples() + this->manager().last_seek();
         int current_second = samples_played / 44100;
         timestamp_label.set_text(String::formatted("Elapsed: {:02}:{:02}:{:02}", current_second / 3600, current_second / 60, current_second % 60));
         m_playback_progress_slider->set_value(samples_played);
 
-        dynamic_cast<Visualization*>(m_visualization.ptr())->set_buffer(m_manager.current_buffer());
+        dynamic_cast<Visualization*>(m_visualization.ptr())->set_buffer(this->manager().current_buffer());
     };
 
-    m_manager.on_load_sample_buffer = [&](Audio::Buffer& buffer) {
-        if (m_volume == 1.)
+    this->manager().on_load_sample_buffer = [&](Audio::Buffer& buffer) {
+        if (volume() == 1.)
             return;
         auto sample_count = buffer.sample_count();
         if (sample_count % 4 == 0) {
             const int total_iter = sample_count / (sizeof(AK::SIMD::f64x4) / sizeof(double) / 2);
             AK::SIMD::f64x4* sample_ptr = const_cast<AK::SIMD::f64x4*>(reinterpret_cast<const AK::SIMD::f64x4*>((buffer.data())));
             for (int i = 0; i < total_iter; ++i) {
-                sample_ptr[i] = sample_ptr[i] * m_volume;
+                sample_ptr[i] = sample_ptr[i] * volume();
             }
         } else {
             const int total_iter = sample_count / (sizeof(AK::SIMD::f64x2) / sizeof(double) / 2);
             AK::SIMD::f64x2* sample_ptr = const_cast<AK::SIMD::f64x2*>(reinterpret_cast<const AK::SIMD::f64x2*>((buffer.data())));
             for (int i = 0; i < total_iter; ++i) {
-                sample_ptr[i] = sample_ptr[i] * m_volume;
+                sample_ptr[i] = sample_ptr[i] * volume();
             }
         }
     };
 }
 
-void SoundPlayerWidgetAdvancedView::set_volume(double value)
-{
-    m_volume = value;
-}
-
 void SoundPlayerWidgetAdvancedView::open_file(StringView path)
 {
     NonnullRefPtr<Audio::Loader> loader = Audio::Loader::create(path);
@@ -173,7 +170,12 @@ void SoundPlayerWidgetAdvancedView::open_file(StringView path)
     }
     m_window.set_title(String::formatted("{} - SoundPlayer", loader->file()->filename()));
     m_playback_progress_slider->set_max(loader->total_samples());
-    m_manager.set_loader(move(loader));
+    m_playback_progress_slider->set_enabled(true);
+    m_play_button->set_enabled(true);
+    m_stop_button->set_enabled(true);
+    manager().set_loader(move(loader));
+    set_has_loaded_file(true);
+    set_loaded_filename(path);
 }
 
 void SoundPlayerWidgetAdvancedView::set_nonlinear_volume_slider(bool nonlinear)
@@ -196,5 +198,13 @@ void SoundPlayerWidgetAdvancedView::drop_event(GUI::DropEvent& event)
 
 SoundPlayerWidgetAdvancedView::~SoundPlayerWidgetAdvancedView()
 {
-    m_manager.on_load_sample_buffer = nullptr;
+    manager().on_load_sample_buffer = nullptr;
+    manager().on_update = nullptr;
+}
+
+void SoundPlayerWidgetAdvancedView::play()
+{
+    manager().play();
+    set_paused(false);
+    set_stopped(false);
 }

+ 4 - 9
Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.h

@@ -39,12 +39,11 @@ class SoundPlayerWidgetAdvancedView final : public GUI::Widget
     C_OBJECT(SoundPlayerWidgetAdvancedView)
 
 public:
-    explicit SoundPlayerWidgetAdvancedView(GUI::Window& window, Audio::ClientConnection& connection, PlaybackManager& manager);
+    explicit SoundPlayerWidgetAdvancedView(GUI::Window& window, PlayerState& state);
     ~SoundPlayerWidgetAdvancedView() override;
 
     void open_file(StringView path) override;
-    Audio::ClientConnection& client_connection() override { return m_connection; }
-    PlaybackManager& playback_manager() override { return m_manager; }
+    void play() override;
 
     template<typename T>
     void set_visualization()
@@ -58,14 +57,9 @@ public:
 
     void set_nonlinear_volume_slider(bool nonlinear);
 
-    void set_volume(double value);
-
 private:
     void drop_event(GUI::DropEvent& event) override;
-
     GUI::Window& m_window;
-    Audio::ClientConnection& m_connection;
-    PlaybackManager& m_manager;
 
     RefPtr<GUI::Widget> m_visualization;
 
@@ -77,9 +71,10 @@ private:
 
     RefPtr<GUI::Button> m_play_button;
     RefPtr<GUI::Button> m_stop_button;
+    RefPtr<GUI::Button> m_back_button;
+    RefPtr<GUI::Button> m_next_button;
     RefPtr<Slider> m_playback_progress_slider;
     RefPtr<GUI::Label> m_volume_label;
 
-    double m_volume;
     bool m_nonlinear_volume_slider;
 };

+ 14 - 6
Userland/Applications/SoundPlayer/main.cpp

@@ -54,8 +54,15 @@ 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");
@@ -72,11 +79,11 @@ int main(int argc, char** argv)
 
     auto& app_menu = menubar->add_menu("File");
     // start in simple view by default
-    Player* player = &window->set_main_widget<SoundPlayerWidget>(window, audio_client, playback_manager);
+    Player* player = &window->set_main_widget<SoundPlayerWidget>(window, initial_player_state);
     if (argc > 1) {
         String path = argv[1];
         player->open_file(path);
-        player->playback_manager().play();
+        player->play();
     }
 
     app_menu.add_action(GUI::CommonActions::make_open_action([&](auto&) {
@@ -89,12 +96,13 @@ int main(int argc, char** argv)
     RefPtr<GUI::Action> hide_scope;
 
     auto advanced_view_check = GUI::Action::create_checkable("Advanced view", { Mod_Ctrl, Key_A }, [&](auto& action) {
+        PlayerState state = player->get_player_state();
         window->close();
         if (action.is_checked()) {
-            player = &window->set_main_widget<SoundPlayerWidgetAdvancedView>(window, audio_client, playback_manager);
+            player = &window->set_main_widget<SoundPlayerWidgetAdvancedView>(window, state);
             hide_scope->set_checkable(false);
         } else {
-            player = &window->set_main_widget<SoundPlayerWidget>(window, audio_client, playback_manager);
+            player = &window->set_main_widget<SoundPlayerWidget>(window, state);
             hide_scope->set_checkable(true);
         }
         window->show();
@@ -123,7 +131,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->playback_manager().loop(action.is_checked());
+        player->set_looping(action.is_checked());
     });
 
     playback_menu.add_action(move(loop));