diff --git a/Userland/Applications/Piano/CMakeLists.txt b/Userland/Applications/Piano/CMakeLists.txt index 431219a698..9b86a99345 100644 --- a/Userland/Applications/Piano/CMakeLists.txt +++ b/Userland/Applications/Piano/CMakeLists.txt @@ -17,7 +17,8 @@ set(SOURCES RollWidget.cpp SamplerWidget.cpp WaveWidget.cpp + ProcessorParameterSlider.cpp ) serenity_app(Piano ICON app-piano) -target_link_libraries(Piano LibAudio LibGUI) +target_link_libraries(Piano LibAudio LibDSP LibGUI) diff --git a/Userland/Applications/Piano/KnobsWidget.cpp b/Userland/Applications/Piano/KnobsWidget.cpp index ca9b74b4f3..ed13dfe851 100644 --- a/Userland/Applications/Piano/KnobsWidget.cpp +++ b/Userland/Applications/Piano/KnobsWidget.cpp @@ -11,6 +11,7 @@ #include #include #include +#include KnobsWidget::KnobsWidget(TrackManager& track_manager, MainWidget& main_widget) : m_track_manager(track_manager) @@ -21,7 +22,7 @@ KnobsWidget::KnobsWidget(TrackManager& track_manager, MainWidget& main_widget) m_labels_container = add(); m_labels_container->set_layout(); - m_labels_container->set_fixed_height(20); + m_labels_container->set_fixed_height(45); m_volume_label = m_labels_container->add("Volume"); m_octave_label = m_labels_container->add("Octave"); @@ -30,7 +31,6 @@ KnobsWidget::KnobsWidget(TrackManager& track_manager, MainWidget& main_widget) m_decay_label = m_labels_container->add("Decay"); m_sustain_label = m_labels_container->add("Sustain"); m_release_label = m_labels_container->add("Release"); - m_delay_label = m_labels_container->add("Delay"); m_values_container = add(); m_values_container->set_layout(); @@ -43,7 +43,6 @@ KnobsWidget::KnobsWidget(TrackManager& track_manager, MainWidget& main_widget) m_decay_value = m_values_container->add(String::number(m_track_manager.current_track().decay())); m_sustain_value = m_values_container->add(String::number(m_track_manager.current_track().sustain())); m_release_value = m_values_container->add(String::number(m_track_manager.current_track().release())); - m_delay_value = m_values_container->add(String::number(m_track_manager.current_track().delay())); m_knobs_container = add(); m_knobs_container->set_layout(); @@ -140,18 +139,14 @@ KnobsWidget::KnobsWidget(TrackManager& track_manager, MainWidget& main_widget) m_release_value->set_text(String::number(new_release)); }; - m_delay_knob = m_knobs_container->add(); - m_delay_knob->set_tooltip("Delay speed, 0 = off"); - m_delay_knob->set_range(0, delay_max); - m_delay_knob->set_value(delay_max - m_track_manager.current_track().delay()); - m_release_knob->set_step(1); - m_delay_knob->on_change = [this](int value) { - int new_delay = delay_max - value; - if (m_change_underlying) - m_track_manager.current_track().set_delay(new_delay); - VERIFY(new_delay == m_track_manager.current_track().delay()); - m_delay_value->set_text(String::number(new_delay)); - }; + for (auto& raw_parameter : m_track_manager.current_track().delay()->parameters()) { + // FIXME: We shouldn't do that, but we know the effect and it's nice. + auto& parameter = static_cast(raw_parameter); + m_delay_values.append(m_values_container->add(String::number(static_cast(parameter.value())))); + auto& parameter_knob_value = m_delay_values.last(); + m_delay_labels.append(m_labels_container->add(String::formatted("Delay: {}", parameter.name()))); + m_delay_knobs.append(m_knobs_container->add(Orientation::Vertical, parameter, parameter_knob_value)); + } } KnobsWidget::~KnobsWidget() @@ -174,7 +169,6 @@ void KnobsWidget::update_knobs() m_decay_knob->set_value(decay_max - m_track_manager.current_track().decay()); m_sustain_knob->set_value(sustain_max - m_track_manager.current_track().sustain()); m_release_knob->set_value(release_max - m_track_manager.current_track().release()); - m_delay_knob->set_value(delay_max - m_track_manager.current_track().delay()); m_change_underlying = true; } diff --git a/Userland/Applications/Piano/KnobsWidget.h b/Userland/Applications/Piano/KnobsWidget.h index aa3710d8d0..7fa6b6b8e7 100644 --- a/Userland/Applications/Piano/KnobsWidget.h +++ b/Userland/Applications/Piano/KnobsWidget.h @@ -7,6 +7,8 @@ #pragma once +#include "ProcessorParameterSlider.h" +#include #include class TrackManager; @@ -33,7 +35,7 @@ private: RefPtr m_decay_label; RefPtr m_sustain_label; RefPtr m_release_label; - RefPtr m_delay_label; + NonnullRefPtrVector m_delay_labels; RefPtr m_values_container; RefPtr m_volume_value; @@ -43,7 +45,7 @@ private: RefPtr m_decay_value; RefPtr m_sustain_value; RefPtr m_release_value; - RefPtr m_delay_value; + NonnullRefPtrVector m_delay_values; RefPtr m_knobs_container; RefPtr m_volume_knob; @@ -53,7 +55,7 @@ private: RefPtr m_decay_knob; RefPtr m_sustain_knob; RefPtr m_release_knob; - RefPtr m_delay_knob; + NonnullRefPtrVector m_delay_knobs; bool m_change_underlying { true }; }; diff --git a/Userland/Applications/Piano/MainWidget.cpp b/Userland/Applications/Piano/MainWidget.cpp index d9f95956ff..c789e2fad0 100644 --- a/Userland/Applications/Piano/MainWidget.cpp +++ b/Userland/Applications/Piano/MainWidget.cpp @@ -42,13 +42,12 @@ MainWidget::MainWidget(TrackManager& track_manager, AudioPlayerLoop& loop) m_keys_and_knobs_container = add(); m_keys_and_knobs_container->set_layout(); m_keys_and_knobs_container->layout()->set_spacing(2); - m_keys_and_knobs_container->set_fixed_height(100); + m_keys_and_knobs_container->set_fixed_height(130); m_keys_and_knobs_container->set_fill_with_background_color(true); m_keys_widget = m_keys_and_knobs_container->add(track_manager); m_knobs_widget = m_keys_and_knobs_container->add(track_manager, *this); - m_knobs_widget->set_fixed_width(350); m_roll_widget->set_keys_widget(m_keys_widget); } diff --git a/Userland/Applications/Piano/ProcessorParameterSlider.cpp b/Userland/Applications/Piano/ProcessorParameterSlider.cpp new file mode 100644 index 0000000000..fd449ccfb8 --- /dev/null +++ b/Userland/Applications/Piano/ProcessorParameterSlider.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, kleines Filmröllchen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "ProcessorParameterSlider.h" + +ProcessorParameterSlider::ProcessorParameterSlider(Orientation orientation, LibDSP::ProcessorRangeParameter& parameter, RefPtr value_label) + : Slider(orientation) + , m_parameter(parameter) + , m_value_label(move(value_label)) +{ + set_range(m_parameter.min_value().raw(), m_parameter.max_value().raw()); + set_value(m_parameter.value().raw()); + set_step((m_parameter.min_value() - m_parameter.max_value()).raw() / 128); + set_tooltip(m_parameter.name()); + + on_change = [this](auto value) { + LibDSP::ParameterFixedPoint real_value; + real_value.raw() = value; + m_parameter.set_value_sneaky(real_value, LibDSP::Detail::ProcessorParameterSetValueTag {}); + if (m_value_label) + m_value_label->set_text(String::formatted("{:.2f}", static_cast(m_parameter))); + }; + m_parameter.did_change_value = [this](auto value) { + set_value(value.raw()); + }; +} diff --git a/Userland/Applications/Piano/ProcessorParameterSlider.h b/Userland/Applications/Piano/ProcessorParameterSlider.h new file mode 100644 index 0000000000..22ed68341e --- /dev/null +++ b/Userland/Applications/Piano/ProcessorParameterSlider.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021, kleines Filmröllchen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +class ProcessorParameterSlider : public GUI::Slider { + C_OBJECT(ProcessorParameterSlider); + +public: + ProcessorParameterSlider(Orientation, LibDSP::ProcessorRangeParameter&, RefPtr); + RefPtr value_label() { return m_value_label; } + +private: + LibDSP::ProcessorRangeParameter& m_parameter; + RefPtr m_value_label; +}; diff --git a/Userland/Applications/Piano/Track.cpp b/Userland/Applications/Piano/Track.cpp index dea1bf4217..a51e7edeed 100644 --- a/Userland/Applications/Piano/Track.cpp +++ b/Userland/Applications/Piano/Track.cpp @@ -10,10 +10,13 @@ #include #include #include +#include #include Track::Track(const u32& time) : m_time(time) + , m_temporary_transport(create(120, 4)) + , m_delay(create(m_temporary_transport)) { set_volume(volume_max); set_sustain_impl(1000); @@ -96,14 +99,11 @@ void Track::fill_sample(Sample& sample) new_sample.right += note_sample.right * m_power[note] * volume_factor * (static_cast(volume()) / volume_max); } - if (m_delay) { - new_sample.left += m_delay_buffer[m_delay_index].left * 0.333333; - new_sample.right += m_delay_buffer[m_delay_index].right * 0.333333; - m_delay_buffer[m_delay_index].left = new_sample.left; - m_delay_buffer[m_delay_index].right = new_sample.right; - if (++m_delay_index >= m_delay_samples) - m_delay_index = 0; - } + auto new_sample_dsp = LibDSP::Signal(LibDSP::Sample { new_sample.left / NumericLimits::max(), new_sample.right / NumericLimits::max() }); + auto delayed_sample = m_delay->process(new_sample_dsp).get(); + + new_sample.left = delayed_sample.left * NumericLimits::max(); + new_sample.right = delayed_sample.right * NumericLimits::max(); sample.left += new_sample.left; sample.right += new_sample.right; @@ -111,8 +111,6 @@ void Track::fill_sample(Sample& sample) void Track::reset() { - memset(m_delay_buffer.data(), 0, m_delay_buffer.size() * sizeof(Sample)); - m_delay_index = 0; memset(m_note_on, 0, sizeof(m_note_on)); memset(m_power, 0, sizeof(m_power)); @@ -360,13 +358,3 @@ void Track::set_release(int release) VERIFY(release >= 0); m_release = release; } - -void Track::set_delay(int delay) -{ - VERIFY(delay >= 0); - m_delay = delay; - m_delay_samples = m_delay == 0 ? 0 : (sample_rate / (beats_per_minute / 60)) / m_delay; - m_delay_buffer.resize(m_delay_samples); - memset(m_delay_buffer.data(), 0, m_delay_buffer.size() * sizeof(Sample)); - m_delay_index = 0; -} diff --git a/Userland/Applications/Piano/Track.h b/Userland/Applications/Piano/Track.h index 696c22ca59..42cf32114d 100644 --- a/Userland/Applications/Piano/Track.h +++ b/Userland/Applications/Piano/Track.h @@ -12,6 +12,7 @@ #include #include #include +#include using RollIter = AK::SinglyLinkedListIterator, RollNote>; @@ -31,7 +32,7 @@ public: int decay() const { return m_decay; } int sustain() const { return m_sustain; } int release() const { return m_release; } - int delay() const { return m_delay; } + NonnullRefPtr delay() { return m_delay; } void fill_sample(Sample& sample); void reset(); @@ -45,7 +46,6 @@ public: void set_decay(int decay); void set_sustain(int sustain); void set_release(int release); - void set_delay(int delay); private: Audio::Frame sine(size_t note); @@ -58,8 +58,6 @@ private: void sync_roll(int note); void set_sustain_impl(int sustain); - Vector m_delay_buffer; - Vector m_recorded_sample; u8 m_note_on[note_count] { 0 }; @@ -79,12 +77,12 @@ private: double m_sustain_level; int m_release; double m_release_step[note_count]; - int m_delay { 0 }; - size_t m_delay_samples { 0 }; - size_t m_delay_index { 0 }; const u32& m_time; + NonnullRefPtr m_temporary_transport; + NonnullRefPtr m_delay; + SinglyLinkedList m_roll_notes[note_count]; RollIter m_roll_iterators[note_count]; };