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:
parent
d757027638
commit
0ab19dc4cd
Notes:
sideshowbarker
2024-07-17 02:29:45 +09:00
Author: https://github.com/kleinesfilmroellchen
Commit: 0ab19dc4cd
Pull-request: https://github.com/SerenityOS/serenity/pull/17648
Reviewed-by: https://github.com/Hendiadyoin1
Reviewed-by: https://github.com/gmta ✅
9 changed files with 92 additions and 45 deletions
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>());
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue