diff --git a/Applications/Piano/AudioEngine.cpp b/Applications/Piano/AudioEngine.cpp index 2af2fb30bd..a14db4b750 100644 --- a/Applications/Piano/AudioEngine.cpp +++ b/Applications/Piano/AudioEngine.cpp @@ -34,6 +34,7 @@ AudioEngine::AudioEngine() set_sustain_impl(0); set_attack(0); set_decay(0); + set_release(0); } AudioEngine::~AudioEngine() @@ -62,6 +63,12 @@ void AudioEngine::fill_buffer(FixedArray& buffer) m_power[note] = m_sustain_level; break; case Release: + m_power[note] -= m_release_step[note]; + if (m_power[note] <= 0) { + m_power[note] = 0; + m_envelope[note] = Done; + continue; + } break; default: ASSERT_NOT_REACHED(); @@ -158,6 +165,17 @@ double AudioEngine::noise() const return w; } +static inline double calculate_step(double distance, int milliseconds) +{ + if (milliseconds == 0) + return distance; + + constexpr double samples_per_millisecond = sample_rate / 1000.0; + double samples = milliseconds * samples_per_millisecond; + double step = distance / samples; + return step; +} + void AudioEngine::set_note(int note, Switch switch_note) { ASSERT(note >= 0 && note < note_count); @@ -171,8 +189,8 @@ void AudioEngine::set_note(int note, Switch switch_note) } else { if (m_note_on[note] >= 1) { if (m_note_on[note] == 1) { - m_power[note] = 0; - m_envelope[note] = Done; + m_release_step[note] = calculate_step(m_power[note], m_release); + m_envelope[note] = Release; } --m_note_on[note]; } @@ -215,17 +233,6 @@ void AudioEngine::set_wave(Direction direction) } } -static inline double calculate_step(double distance, int milliseconds) -{ - if (milliseconds == 0) - return distance; - - constexpr double samples_per_millisecond = sample_rate / 1000.0; - double samples = milliseconds * samples_per_millisecond; - double step = distance / samples; - return step; -} - void AudioEngine::set_attack(int attack) { ASSERT(attack >= 0); @@ -253,6 +260,12 @@ void AudioEngine::set_sustain(int sustain) set_decay(m_decay); } +void AudioEngine::set_release(int release) +{ + ASSERT(release >= 0); + m_release = release; +} + void AudioEngine::set_delay(int delay) { ASSERT(delay >= 0); diff --git a/Applications/Piano/AudioEngine.h b/Applications/Piano/AudioEngine.h index 430745f007..8e1a048234 100644 --- a/Applications/Piano/AudioEngine.h +++ b/Applications/Piano/AudioEngine.h @@ -46,6 +46,7 @@ public: int attack() const { return m_attack; } int decay() const { return m_decay; } int sustain() const { return m_sustain; } + int release() const { return m_release; } int delay() const { return m_delay; } int time() const { return m_time; } int tick() const { return m_tick; } @@ -59,6 +60,7 @@ public: void set_attack(int attack); void set_decay(int decay); void set_sustain(int sustain); + void set_release(int release); void set_delay(int delay); private: @@ -90,6 +92,8 @@ private: double m_decay_step; int m_sustain; double m_sustain_level; + int m_release; + double m_release_step[note_count]; int m_delay { 0 }; int m_time { 0 }; diff --git a/Applications/Piano/KnobsWidget.cpp b/Applications/Piano/KnobsWidget.cpp index d2d9324bfb..f35f7869a3 100644 --- a/Applications/Piano/KnobsWidget.cpp +++ b/Applications/Piano/KnobsWidget.cpp @@ -53,6 +53,7 @@ KnobsWidget::KnobsWidget(GUI::Widget* parent, AudioEngine& audio_engine, MainWid m_attack_label = GUI::Label::construct("Attack", m_labels_container); m_decay_label = GUI::Label::construct("Decay", m_labels_container); m_sustain_label = GUI::Label::construct("Sustain", m_labels_container); + m_release_label = GUI::Label::construct("Release", m_labels_container); m_delay_label = GUI::Label::construct("Delay", m_labels_container); m_values_container = GUI::Widget::construct(this); @@ -65,6 +66,7 @@ KnobsWidget::KnobsWidget(GUI::Widget* parent, AudioEngine& audio_engine, MainWid m_attack_value = GUI::Label::construct(String::number(m_audio_engine.attack()), m_values_container); m_decay_value = GUI::Label::construct(String::number(m_audio_engine.decay()), m_values_container); m_sustain_value = GUI::Label::construct(String::number(m_audio_engine.sustain()), m_values_container); + m_release_value = GUI::Label::construct(String::number(m_audio_engine.release()), m_values_container); m_delay_value = GUI::Label::construct(String::number(m_audio_engine.delay() / m_audio_engine.tick()), m_values_container); m_knobs_container = GUI::Widget::construct(this); @@ -128,6 +130,17 @@ KnobsWidget::KnobsWidget(GUI::Widget* parent, AudioEngine& audio_engine, MainWid m_sustain_value->set_text(String::number(new_sustain)); }; + constexpr int max_release = 1000; + m_release_knob = GUI::Slider::construct(Orientation::Vertical, m_knobs_container); + m_release_knob->set_range(0, max_release); + m_release_knob->set_value(max_release - m_audio_engine.release()); + m_release_knob->on_value_changed = [this](int value) { + int new_release = max_release - value; + m_audio_engine.set_release(new_release); + ASSERT(new_release == m_audio_engine.release()); + m_release_value->set_text(String::number(new_release)); + }; + constexpr int max_delay = 8; m_delay_knob = GUI::Slider::construct(Orientation::Vertical, m_knobs_container); m_delay_knob->set_range(0, max_delay); diff --git a/Applications/Piano/KnobsWidget.h b/Applications/Piano/KnobsWidget.h index 2eca83982f..6adca4368b 100644 --- a/Applications/Piano/KnobsWidget.h +++ b/Applications/Piano/KnobsWidget.h @@ -56,6 +56,7 @@ private: RefPtr m_attack_label; RefPtr m_decay_label; RefPtr m_sustain_label; + RefPtr m_release_label; RefPtr m_delay_label; RefPtr m_values_container; @@ -64,6 +65,7 @@ private: RefPtr m_attack_value; RefPtr m_decay_value; RefPtr m_sustain_value; + RefPtr m_release_value; RefPtr m_delay_value; RefPtr m_knobs_container; @@ -72,6 +74,7 @@ private: RefPtr m_attack_knob; RefPtr m_decay_knob; RefPtr m_sustain_knob; + RefPtr m_release_knob; RefPtr m_delay_knob; bool m_change_octave { true }; diff --git a/Applications/Piano/MainWidget.cpp b/Applications/Piano/MainWidget.cpp index 9d2003763d..2e25ef543b 100644 --- a/Applications/Piano/MainWidget.cpp +++ b/Applications/Piano/MainWidget.cpp @@ -60,7 +60,7 @@ MainWidget::MainWidget(AudioEngine& audio_engine) m_knobs_widget = KnobsWidget::construct(m_keys_and_knobs_container, audio_engine, *this); m_knobs_widget->set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill); - m_knobs_widget->set_preferred_size(300, 0); + m_knobs_widget->set_preferred_size(350, 0); } MainWidget::~MainWidget()