mirror of
https://github.com/RGBCube/serenity
synced 2025-06-01 02:58:11 +00:00
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.
This commit is contained in:
parent
ab9475a3f3
commit
59bde64ba6
5 changed files with 47 additions and 14 deletions
|
@ -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<Sample>& 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);
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -56,6 +56,7 @@ private:
|
|||
RefPtr<GUI::Label> m_attack_label;
|
||||
RefPtr<GUI::Label> m_decay_label;
|
||||
RefPtr<GUI::Label> m_sustain_label;
|
||||
RefPtr<GUI::Label> m_release_label;
|
||||
RefPtr<GUI::Label> m_delay_label;
|
||||
|
||||
RefPtr<GUI::Widget> m_values_container;
|
||||
|
@ -64,6 +65,7 @@ private:
|
|||
RefPtr<GUI::Label> m_attack_value;
|
||||
RefPtr<GUI::Label> m_decay_value;
|
||||
RefPtr<GUI::Label> m_sustain_value;
|
||||
RefPtr<GUI::Label> m_release_value;
|
||||
RefPtr<GUI::Label> m_delay_value;
|
||||
|
||||
RefPtr<GUI::Widget> m_knobs_container;
|
||||
|
@ -72,6 +74,7 @@ private:
|
|||
RefPtr<GUI::Slider> m_attack_knob;
|
||||
RefPtr<GUI::Slider> m_decay_knob;
|
||||
RefPtr<GUI::Slider> m_sustain_knob;
|
||||
RefPtr<GUI::Slider> m_release_knob;
|
||||
RefPtr<GUI::Slider> m_delay_knob;
|
||||
|
||||
bool m_change_octave { true };
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue