From 59bde64ba6c7c78695da78cbe925b3a4d38fc5b2 Mon Sep 17 00:00:00 2001 From: William McPherson Date: Wed, 5 Feb 2020 18:00:16 +1100 Subject: [PATCH] Piano: Add release Notice that we are calculating release time according to the level when the note is turned off rather than the sustain level. Naively using the sustain level gives very long release times if you turn the note off during attack, whereas this deterministically gives the same release time. --- Applications/Piano/AudioEngine.cpp | 39 ++++++++++++++++++++---------- Applications/Piano/AudioEngine.h | 4 +++ Applications/Piano/KnobsWidget.cpp | 13 ++++++++++ Applications/Piano/KnobsWidget.h | 3 +++ Applications/Piano/MainWidget.cpp | 2 +- 5 files changed, 47 insertions(+), 14 deletions(-) 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()