From c63db4fa07ef191ec2ca7a87c4f1118e13833726 Mon Sep 17 00:00:00 2001 From: HiveBeats Date: Wed, 9 Aug 2023 01:38:40 +0400 Subject: [PATCH] wip: ADSR --- docs/ADSR.md | 1 + inc/ADSR.h | 31 +++++++++++++++++++++++++ inc/Effect.h | 11 +++++++++ inc/Synth.h | 4 ++++ src/ADSR.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/Synth.cpp | 23 +++++++++++++++++-- 6 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 inc/ADSR.h create mode 100644 inc/Effect.h create mode 100644 src/ADSR.cpp diff --git a/docs/ADSR.md b/docs/ADSR.md index 211b7d2..be4c98f 100644 --- a/docs/ADSR.md +++ b/docs/ADSR.md @@ -26,6 +26,7 @@ release_samples = int(release_time * sample_rate) # Attack phase envelope[:attack_samples] = np.linspace(0, 1, num=attack_samples) +# 1/n * count; # Decay phase decay_slope = (1 - sustain_level) / decay_samples diff --git a/inc/ADSR.h b/inc/ADSR.h new file mode 100644 index 0000000..3af24d7 --- /dev/null +++ b/inc/ADSR.h @@ -0,0 +1,31 @@ +#pragma once +#include "Effect.h" +#include + +struct ADSRParameters { + float attack_time; // Attack time in seconds + float decay_time; // Decay time in seconds + float sustain_level; // Sustain level (0 to 1) + float release_time; +}; + +enum ADSRState { Attack, Decay, Sustain, Release }; + +class ADSR : public Effect { + private: + ADSRParameters m_parameters; + ADSRState m_state; + std::size_t m_counter; + void set_state(std::size_t attack_samples, std::size_t decay_samples, + std::size_t release_samples); + void process_sample(float* sample, std::size_t attack_samples, + std::size_t decay_samples, std::size_t release_samples); + + public: + ADSR(/* args */); + ADSR(ADSRParameters param); + ~ADSR(); + void RetriggerState() override; + void Process(std::vector& samples) override; + void Reset(); +}; diff --git a/inc/Effect.h b/inc/Effect.h new file mode 100644 index 0000000..5065b60 --- /dev/null +++ b/inc/Effect.h @@ -0,0 +1,11 @@ +#pragma once +#include +class Effect { + private: + /* data */ + public: + Effect(/* args */){}; + ~Effect(){}; + virtual void RetriggerState(){}; + virtual void Process(std::vector& samples){}; +}; diff --git a/inc/Synth.h b/inc/Synth.h index f322421..625d4c8 100644 --- a/inc/Synth.h +++ b/inc/Synth.h @@ -1,6 +1,7 @@ #pragma once #include "Adder.h" +#include "Effect.h" #include "Note.h" #include "Oscillator.h" #include "Settings.h" @@ -10,16 +11,19 @@ class Synth { private: std::vector m_oscillators; Adder m_adder; + std::vector m_effects; // OscillatorUI* ui_oscillators; // Note m_current_note; std::vector m_out_signal; std::vector& get_note(int semitone, float beats); + void apply_effects(); public: Synth(/* args */); ~Synth(); void ProduceNoteSound(Note input); void AddOscillator(); + void AddEffect(Effect* fx); const std::vector& GetOutSignal() { return m_out_signal; } const std::vector& GetOscillators() { return m_oscillators; } }; \ No newline at end of file diff --git a/src/ADSR.cpp b/src/ADSR.cpp new file mode 100644 index 0000000..55054cd --- /dev/null +++ b/src/ADSR.cpp @@ -0,0 +1,63 @@ +#include "ADSR.h" +#include "Settings.h" +#include "Logger.h" + +ADSR::ADSR(/* args */) { + m_parameters.attack_time = 0.025f; + m_parameters.decay_time = 0.3f; + m_parameters.sustain_level = 0.6f; + m_parameters.release_time = 1.0f; + m_counter = 0; +} + +ADSR::ADSR(ADSRParameters param) { + m_parameters = param; + m_counter = 0; +} + +ADSR::~ADSR() {} + +void ADSR::set_state(std::size_t attack_samples, std::size_t decay_samples, + std::size_t release_samples) { + if (m_counter < attack_samples) { + m_state = Attack; + } else if (m_counter >= attack_samples && + m_counter < attack_samples + decay_samples) { + m_state = Decay; + } else if (m_counter >= attack_samples + decay_samples) { + m_state = Sustain; + } +} + +void ADSR::process_sample(float* sample, std::size_t attack_samples, + std::size_t decay_samples, + std::size_t release_samples) { + + set_state(attack_samples, decay_samples, release_samples); + if (m_state == Attack) { + (*sample) = (float)(1.f / attack_samples) * m_counter; + } else if (m_state == Decay) { + } + m_counter++; + // todo: release state on note off (in reset function?) +} + +void ADSR::RetriggerState() { + m_counter = 0; + m_state = Attack; +} + +void ADSR::Process(std::vector& samples) { + const std::size_t attack_samples = + (std::size_t)(m_parameters.attack_time * SAMPLE_RATE); + const std::size_t decay_samples = + (std::size_t)(m_parameters.decay_time * SAMPLE_RATE); + const std::size_t release_samples = + (std::size_t)(m_parameters.release_time * SAMPLE_RATE); + write_log("Attack samples: %zu \n", attack_samples); + for (std::size_t i = 0; i < samples.size(); i++) { + process_sample(&samples[i], attack_samples, decay_samples, + release_samples); + } + write_log("Processed samples: %zu \n", m_counter); +} \ No newline at end of file diff --git a/src/Synth.cpp b/src/Synth.cpp index b56d33b..4df2755 100644 --- a/src/Synth.cpp +++ b/src/Synth.cpp @@ -1,11 +1,19 @@ #include "Synth.h" +#include "ADSR.h" #include "KeyBoard.h" #include "OscillatorType.h" #include "Settings.h" -Synth::Synth(/* args */) { AddOscillator(); } +Synth::Synth(/* args */) { + AddOscillator(); + AddEffect(new ADSR()); +} -Synth::~Synth() {} +Synth::~Synth() { + m_oscillators.clear(); + m_effects.clear(); + m_out_signal.clear(); +} std::vector& Synth::get_note(int semitone, float beats) { float hz = KeyBoard::GetHzBySemitone(semitone); @@ -20,13 +28,24 @@ std::vector& Synth::get_note(int semitone, float beats) { return m_adder.SumOscillators(m_oscillators, duration); } +void Synth::apply_effects() { + for (Effect* effect : m_effects) { + // maybe not here + effect->RetriggerState(); + effect->Process(m_out_signal); + } +} + void Synth::ProduceNoteSound(Note input) { float length = 1.f / input.length; int semitone_shift = KeyBoard::GetSemitoneShift(input.name); m_out_signal = get_note(semitone_shift, length); + apply_effects(); } void Synth::AddOscillator() { m_oscillators.push_back( new Oscillator(OscillatorType::Sine, 440.f, VOLUME)); } + +void Synth::AddEffect(Effect* fx) { m_effects.push_back(fx); }