1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-10 18:10:56 +09:00

Piano: Allow per-track controls (again)

This makes Piano exactly as usable as when I started the large refactor
some years ago, which *sounds* like I'm a terrible person but now it (1)
looks nicer and (2) has a flexible backend that can already deal with
aribtrary kinds of processors on any track.
This commit is contained in:
kleines Filmröllchen 2023-02-27 15:31:43 +01:00 committed by Jelle Raaijmakers
parent d757027638
commit 0ab19dc4cd
Notes: sideshowbarker 2024-07-17 02:29:45 +09:00
9 changed files with 92 additions and 45 deletions

View file

@ -18,6 +18,7 @@
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Menu.h>
#include <LibGUI/Slider.h>
#include <LibGUI/StackWidget.h>
#include <LibGUI/TabWidget.h>
ErrorOr<NonnullRefPtr<MainWidget>> MainWidget::try_create(TrackManager& manager, AudioPlayerLoop& loop)
@ -47,7 +48,7 @@ ErrorOr<void> MainWidget::initialize()
m_roll_widget->set_fixed_height(300);
(void)TRY(m_tab_widget->try_add_tab<SamplerWidget>("Sampler"_short_string, m_track_manager));
m_player_widget = TRY(try_add<PlayerWidget>(m_track_manager, m_audio_loop));
m_player_widget = TRY(try_add<PlayerWidget>(m_track_manager, *this, m_audio_loop));
m_keys_and_knobs_container = TRY(try_add<GUI::Widget>());
TRY(m_keys_and_knobs_container->try_set_layout<GUI::HorizontalBoxLayout>(GUI::Margins {}, 2));
@ -78,7 +79,11 @@ ErrorOr<void> MainWidget::initialize()
m_octave_value->set_text(String::number(new_octave).release_value_but_fixme_should_propagate_errors());
};
m_knobs_widget = TRY(m_keys_and_knobs_container->try_add<TrackControlsWidget>(m_track_manager, *this));
m_knobs_widget = TRY(m_keys_and_knobs_container->try_add<GUI::StackWidget>());
for (auto track : m_track_manager.tracks())
TRY(m_track_controls.try_append(TRY(m_knobs_widget->try_add<TrackControlsWidget>(TRY(track->try_make_weak_ptr())))));
update_selected_track();
m_roll_widget->set_keys_widget(m_keys_widget);
@ -100,6 +105,20 @@ ErrorOr<void> MainWidget::add_track_actions(GUI::Menu& menu)
return {};
}
void MainWidget::update_selected_track()
{
if (static_cast<size_t>(m_track_manager.track_count()) > m_track_controls.size())
MUST(add_controls_for_current_track());
m_knobs_widget->set_active_widget(m_track_controls.at(m_track_manager.current_track_index()).ptr());
}
ErrorOr<void> MainWidget::add_controls_for_current_track()
{
auto track = m_track_manager.current_track();
TRY(m_track_controls.try_append(TRY(m_knobs_widget->try_add<TrackControlsWidget>(TRY(track->try_make_weak_ptr())))));
return {};
}
// FIXME: There are some unnecessary calls to update() throughout this program,
// which are an easy target for optimization.

View file

@ -11,6 +11,7 @@
#include "Music.h"
#include <LibDSP/Keyboard.h>
#include <LibGUI/StackWidget.h>
#include <LibGUI/Widget.h>
class AudioPlayerLoop;
@ -32,6 +33,8 @@ public:
void change_octave_via_keys(DSP::Keyboard::Direction);
void set_octave_via_slider(int octave);
void update_selected_track();
ErrorOr<void> add_controls_for_current_track();
private:
explicit MainWidget(TrackManager&, AudioPlayerLoop&);
@ -57,7 +60,8 @@ private:
RefPtr<GUI::TabWidget> m_tab_widget;
RefPtr<GUI::Widget> m_keys_and_knobs_container;
RefPtr<KeysWidget> m_keys_widget;
RefPtr<TrackControlsWidget> m_knobs_widget;
RefPtr<GUI::StackWidget> m_knobs_widget;
Vector<NonnullRefPtr<TrackControlsWidget>> m_track_controls;
RefPtr<PlayerWidget> m_player_widget;
RefPtr<GUI::Widget> m_octave_container;

View file

@ -8,6 +8,7 @@
#include "PlayerWidget.h"
#include "AudioPlayerLoop.h"
#include "MainWidget.h"
#include "Music.h"
#include "TrackManager.h"
#include <LibGUI/BoxLayout.h>
@ -16,9 +17,9 @@
#include <LibGUI/ItemListModel.h>
#include <LibGUI/Label.h>
ErrorOr<NonnullRefPtr<PlayerWidget>> PlayerWidget::create(TrackManager& manager, AudioPlayerLoop& loop)
ErrorOr<NonnullRefPtr<PlayerWidget>> PlayerWidget::try_create(TrackManager& manager, MainWidget& main_widget, AudioPlayerLoop& loop)
{
auto widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PlayerWidget(manager, loop)));
auto widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PlayerWidget(manager, main_widget, loop)));
widget->m_play_icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png"sv));
widget->m_pause_icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/pause.png"sv));
@ -31,8 +32,9 @@ ErrorOr<NonnullRefPtr<PlayerWidget>> PlayerWidget::create(TrackManager& manager,
return widget;
}
PlayerWidget::PlayerWidget(TrackManager& manager, AudioPlayerLoop& loop)
PlayerWidget::PlayerWidget(TrackManager& manager, MainWidget& main_widget, AudioPlayerLoop& loop)
: m_track_manager(manager)
, m_main_widget(main_widget)
, m_audio_loop(loop)
{
}
@ -54,6 +56,7 @@ ErrorOr<void> PlayerWidget::initialize()
m_track_dropdown->set_selected_index(0);
m_track_dropdown->on_change = [this]([[maybe_unused]] auto name, GUI::ModelIndex model_index) {
m_track_manager.set_current_track(static_cast<size_t>(model_index.row()));
m_main_widget.update_selected_track();
};
m_add_track_button = TRY(try_add<GUI::Button>());

View file

@ -11,11 +11,12 @@
class AudioPlayerLoop;
class TrackManager;
class MainWidget;
class PlayerWidget final : public GUI::Toolbar {
C_OBJECT(PlayerWidget)
C_OBJECT_ABSTRACT(PlayerWidget)
public:
static ErrorOr<NonnullRefPtr<PlayerWidget>> create(TrackManager&, AudioPlayerLoop&);
static ErrorOr<NonnullRefPtr<PlayerWidget>> try_create(TrackManager&, MainWidget&, AudioPlayerLoop&);
virtual ~PlayerWidget() override = default;
void add_track();
@ -23,11 +24,12 @@ public:
void toggle_paused();
private:
explicit PlayerWidget(TrackManager&, AudioPlayerLoop&);
explicit PlayerWidget(TrackManager&, MainWidget&, AudioPlayerLoop&);
ErrorOr<void> initialize();
TrackManager& m_track_manager;
MainWidget& m_main_widget;
AudioPlayerLoop& m_audio_loop;
Vector<DeprecatedString> m_track_number_choices;

View file

@ -8,6 +8,7 @@
*/
#include "RollWidget.h"
#include "LibDSP/Music.h"
#include "TrackManager.h"
#include <AK/IntegralMath.h>
#include <LibGUI/Event.h>
@ -127,7 +128,7 @@ void RollWidget::paint_event(GUI::PaintEvent& event)
int distance_to_next_x = next_x_pos - x_pos;
Gfx::IntRect rect(x_pos, y_pos, distance_to_next_x, note_height);
if (m_track_manager.keyboard()->is_pressed(note))
if (static_cast<size_t>(note) < DSP::note_frequencies.size() && m_track_manager.keyboard()->is_pressed(note))
painter.fill_rect(rect, note_pressed_color.with_alpha(128));
}
}

View file

@ -7,28 +7,46 @@
*/
#include "TrackControlsWidget.h"
#include "MainWidget.h"
#include "ProcessorParameterWidget/ParameterWidget.h"
#include "TrackManager.h"
#include <LibDSP/ProcessorParameter.h>
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Label.h>
#include <LibGfx/Orientation.h>
#include <LibGUI/Frame.h>
#include <LibGUI/GroupBox.h>
#include <LibGUI/Widget.h>
TrackControlsWidget::TrackControlsWidget(TrackManager& track_manager, MainWidget& main_widget)
: m_track_manager(track_manager)
, m_main_widget(main_widget)
TrackControlsWidget::TrackControlsWidget(WeakPtr<DSP::Track> track)
: m_track(move(track))
{
set_layout<GUI::HorizontalBoxLayout>();
set_preferred_width(GUI::SpecialDimension::Grow);
set_fill_with_background_color(true);
for (auto& parameter : m_track_manager.current_track()->track_mastering()->parameters())
m_parameter_widgets.append(add<ProcessorParameterWidget>(parameter));
for (auto& parameter : m_track_manager.current_track()->synth()->parameters())
m_parameter_widgets.append(add<ProcessorParameterWidget>(parameter));
for (auto& parameter : m_track_manager.current_track()->delay()->parameters())
m_parameter_widgets.append(add<ProcessorParameterWidget>(parameter));
}
ErrorOr<NonnullRefPtr<TrackControlsWidget>> TrackControlsWidget::try_create(WeakPtr<DSP::Track> track)
{
auto widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) TrackControlsWidget(move(track))));
TRY(widget->try_set_layout<GUI::HorizontalBoxLayout>());
widget->set_preferred_width(GUI::SpecialDimension::Grow);
widget->set_fill_with_background_color(true);
auto mastering_parameters = TRY(widget->try_add<GUI::GroupBox>());
TRY(mastering_parameters->try_set_layout<GUI::HorizontalBoxLayout>());
auto strong_track = widget->m_track.value();
for (auto& parameter : strong_track->track_mastering()->parameters())
(void)TRY(mastering_parameters->try_add<ProcessorParameterWidget>(parameter));
TRY(widget->m_processor_groups.try_append(mastering_parameters));
TRY(widget->add_spacer());
for (auto& processor : strong_track->processor_chain()) {
auto processor_parameters = TRY(widget->try_add<GUI::GroupBox>());
TRY(processor_parameters->try_set_layout<GUI::HorizontalBoxLayout>());
for (auto& parameter : processor->parameters())
(void)TRY(processor_parameters->try_add<ProcessorParameterWidget>(parameter));
TRY(widget->m_processor_groups.try_append(processor_parameters));
}
return widget;
}

View file

@ -9,25 +9,21 @@
#pragma once
#include "ProcessorParameterWidget/ParameterWidget.h"
#include <LibDSP/ProcessorParameter.h>
#include <LibDSP/Synthesizers.h>
#include <LibGUI/Frame.h>
#include <LibGUI/Label.h>
#include <LibGUI/Widget.h>
class TrackManager;
class MainWidget;
#include <AK/Vector.h>
#include <AK/WeakPtr.h>
#include <LibDSP/Track.h>
#include <LibGUI/Forward.h>
class TrackControlsWidget final : public GUI::Frame {
C_OBJECT(TrackControlsWidget)
C_OBJECT_ABSTRACT(TrackControlsWidget)
public:
virtual ~TrackControlsWidget() override = default;
static ErrorOr<NonnullRefPtr<TrackControlsWidget>> try_create(WeakPtr<DSP::Track>);
private:
TrackControlsWidget(TrackManager&, MainWidget&);
TrackControlsWidget(WeakPtr<DSP::Track>);
TrackManager& m_track_manager;
MainWidget& m_main_widget;
Vector<NonnullRefPtr<ProcessorParameterWidget>> m_parameter_widgets;
WeakPtr<DSP::Track> m_track;
Vector<NonnullRefPtr<GUI::GroupBox>> m_processor_groups;
};

View file

@ -28,11 +28,13 @@ public:
NonnullRefPtr<DSP::NoteTrack> current_track() { return *m_tracks[m_current_track]; }
size_t track_count() { return m_tracks.size(); };
size_t current_track_index() const { return m_current_track; }
void set_current_track(size_t track_index)
{
VERIFY(track_index < track_count());
m_current_track = track_index;
}
Span<NonnullRefPtr<DSP::NoteTrack>> tracks() { return m_tracks.span(); }
NonnullRefPtr<DSP::Transport> transport() const { return m_transport; }
NonnullRefPtr<DSP::Keyboard> keyboard() const { return m_keyboard; }

View file

@ -9,6 +9,7 @@
#include <AK/DisjointChunks.h>
#include <AK/NonnullRefPtr.h>
#include <AK/RefCounted.h>
#include <AK/Weakable.h>
#include <LibDSP/Clip.h>
#include <LibDSP/Effects.h>
#include <LibDSP/Keyboard.h>
@ -19,7 +20,8 @@
namespace DSP {
// A track is also known as a channel and serves as a container for the audio pipeline: clips -> processors -> mixing & output
class Track : public RefCounted<Track> {
class Track : public RefCounted<Track>
, public Weakable<Track> {
public:
virtual ~Track() = default;