📜 ⬆️ ⬇️

Creating audio plugin, part 9

All posts series:
Part 1. Introduction and setup
Part 2. Learning Code
Part 3. VST and AU
Part 4. Digital Distortion
Part 5. Presets and GUI
Part 6. Signal synthesis
Part 7. Receive MIDI Messages
Part 8. Virtual Keyboard
Part 9. Envelopes
Part 10. Refinement GUI
Part 11. Filter
Part 12. Low-frequency oscillator
Part 13. Redesign
Part 14. Polyphony 1
Part 15. Polyphony 2
Part 16. Antialiasing



The sound is interesting when there are any changes in it. Let's make an envelope generator that will change the volume of the sound.


Envelopes


')

Basic principles



If you are not familiar with the abbreviation ADSR ( Attack, Decay, Sustain, Release ), read this little article on Wikipedia before continuing.
In essence, the generator will be a finite state machine with the states Off, Attack, Decay, Sustain and Release . This is such an abstruse way to say that at any given time the generator will be in only one of these five possible states. In the terminology of the envelopes, these states are called stages. The transition from one stage to another will be done by calling the enterStage member enterStage .
A couple of key points regarding the stages:



For each signal sample, the generator will produce a double value between zero and one. We will multiply the current value received from the envelope generator with the output signal of the oscillator. This way the signal level will be determined by the envelope. We will be able to do a variety of things with sound: it will float up slowly, subside abruptly, or be even as a log.

In general, ADSR is the first approach to more advanced sound modeling. On many modern soft and iron synthesizers, one can find envelopes with a large number of stages. All of them, in fact, are simply an extension of the model that we will create now. The number of states of such state machines is just a little more than five.

Class EnvelopeGenerator



Create a new EnvelopeGenerator class and add it to all the targets (on Mac) and projects (on Windows). Paste the class declaration between #define and #endif into EnvelopeGenerator.h :

 #include <cmath> class EnvelopeGenerator { public: enum EnvelopeStage { ENVELOPE_STAGE_OFF = 0, ENVELOPE_STAGE_ATTACK, ENVELOPE_STAGE_DECAY, ENVELOPE_STAGE_SUSTAIN, ENVELOPE_STAGE_RELEASE, kNumEnvelopeStages }; void enterStage(EnvelopeStage newStage); double nextSample(); void setSampleRate(double newSampleRate); inline EnvelopeStage getCurrentStage() const { return currentStage; }; const double minimumLevel; EnvelopeGenerator() : minimumLevel(0.0001), currentStage(ENVELOPE_STAGE_OFF), currentLevel(minimumLevel), multiplier(1.0), sampleRate(44100.0), currentSampleIndex(0), nextStageSampleIndex(0) { stageValue[ENVELOPE_STAGE_OFF] = 0.0; stageValue[ENVELOPE_STAGE_ATTACK] = 0.01; stageValue[ENVELOPE_STAGE_DECAY] = 0.5; stageValue[ENVELOPE_STAGE_SUSTAIN] = 0.1; stageValue[ENVELOPE_STAGE_RELEASE] = 1.0; }; private: EnvelopeStage currentStage; double currentLevel; double multiplier; double sampleRate; double stageValue[kNumEnvelopeStages]; void calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples); unsigned long long currentSampleIndex; unsigned long long nextStageSampleIndex; }; 


To begin with, we create an enum with all the stages. kNumEnvelopeStages at the end tells us how many stages there are. Please note that this enum is within the boundaries of the envelope class namespace, and will not be accessible from the outside, in the global namespace.
We will look at member functions in more detail when we implement them. The minimum level minimumLevel required, since the signal level calculations will not work with zero. We initialize this variable with a very small value of 0.001 .
In the initialization list, the envelope defaults to OFF . The stageValue array stageValue also initialized with the preset values: the hundredth second of the attack, half a second of recession, a quiet level and a second for a complete attenuation.
In the private currentStage section indicates the stage at which the generator is now located. urrentLevel is the envelope volume value for this sample time. multiplyer provides exponential decay, as will be seen later.
At the ATTACK, DECAY and RELEASE stages, the generator must track its position in time to move to the next stage at the right moment. For this we will use the variable currentSampleIndex . In EnvelopeGenerator.cpp add the following function:

 double EnvelopeGenerator::nextSample() { if (currentStage != ENVELOPE_STAGE_OFF && currentStage != ENVELOPE_STAGE_SUSTAIN) { if (currentSampleIndex == nextStageSampleIndex) { EnvelopeStage newStage = static_cast<EnvelopeStage>( (currentStage + 1) % kNumEnvelopeStages ); enterStage(newStage); } currentLevel *= multiplier; currentSampleIndex++; } return currentLevel; } 


If the generator in the ATTACK, DECAY or RELEASE stages and currentSampleIndex reaches the value of the nextStageSampleIndex , we proceed to the next element in the enum EnvelopeStage . Due to the division with the remainder ( % ) after ENVELOPE_STAGE_RELEASE generator will immediately go to ENVELOPE_STAGE_OFF , which is what we need. This transition is made by calling enterStage .
Then we compute the currentLevel level and update the currentSampleIndex to keep track of the generator’s position over time. These calculations are not performed at the stages OFF and SUSTAIN, since the level should not change on them. The same goes for currentSampleIndex : these two stages are not limited in time.

Transitions in time



In ATTACK, DECAY and RELEASE, the generator transitions between two values ​​in a given time. The human ear perceives loudness on a logarithmic scale. Therefore, in order for a loudness change to be perceived by a linear ear, it must occur exponentially.
There are different ways to build an exponential curve between two points. The first thing that comes to mind is that each sample calls a relatively heavy exp function from the library. . , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes

. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes

. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes

. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes

. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes

. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes

. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
  . ,   :       ,    ,        . 
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes
. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes

. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes

. , : , , .
( ):

void EnvelopeGenerator::calculateMultiplier(double startLevel, double endLevel, unsigned long long lengthInSamples) { multiplier = 1.0 + (log(endLevel) - log(startLevel)) / (lengthInSamples); }


, . , startLevel endLevel lengthInSamples multiplier , . currentLevel . log() .



enterStage :

void EnvelopeGenerator::enterStage(EnvelopeStage newStage) { currentStage = newStage; currentSampleIndex = 0; if (currentStage == ENVELOPE_STAGE_OFF || currentStage == ENVELOPE_STAGE_SUSTAIN) { nextStageSampleIndex = 0; } else { nextStageSampleIndex = stageValue[currentStage] * sampleRate; } switch (newStage) { case ENVELOPE_STAGE_OFF: currentLevel = 0.0; multiplier = 1.0; break; case ENVELOPE_STAGE_ATTACK: currentLevel = minimumLevel; calculateMultiplier(currentLevel, 1.0, nextStageSampleIndex); break; case ENVELOPE_STAGE_DECAY: currentLevel = 1.0; calculateMultiplier(currentLevel, fmax(stageValue[ENVELOPE_STAGE_SUSTAIN], minimumLevel), nextStageSampleIndex); break; case ENVELOPE_STAGE_SUSTAIN: currentLevel = stageValue[ENVELOPE_STAGE_SUSTAIN]; multiplier = 1.0; break; case ENVELOPE_STAGE_RELEASE: // We could go from ATTACK/DECAY to RELEASE, // so we're not changing currentLevel here. calculateMultiplier(currentLevel, minimumLevel, nextStageSampleIndex); break; default: break; } }

currentStage currentSampleIndex . . , , . stageValue[currentStage] double ( ), , , . Switch . OFF ( , ). ATTACK minimumLevel , multiplier currentLevel 1.0 . DECAY stageValue[ENVELOPE_STAGE_SUSTAIN] , fmax , . RELEASE currentLevel , , minimumLevel . , RELEASE currentLevel , . . SUSTAIN, ATTACK DECAY. , SUSTAIN , stageValue[ENVELOPE_STAGE_SUSTAIN] , . currentLevel .



:

void EnvelopeGenerator::setSampleRate(double newSampleRate) { sampleRate = newSampleRate; }

private Synthesis ( ) :

EnvelopeGenerator mEnvelopeGenerator;

#include "EnvelopeGenerator.h" .
, ProcessDoubleReplacing Synthesis.cpp leftOutput[i] :

// leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * velocity / 127.0; if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_OFF) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); } if (mEnvelopeGenerator.getCurrentStage() == EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); } leftOutput[i] = rightOutput[i] = mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0;

. : OFF ATTACK, SUSTAIN RELEASE. .
mEnvelopeGenerator . Synthesis::Reset() :

mEnvelopeGenerator.setSampleRate(GetSampleRate());

. - — ! .

Note On/Off

, (, - , ), : — ATTACK, — RELEASE. mMIDIReceiver note on/off, , - .

#include EnvelopeGenerator.h MIDIReceiver.h . , Synthesis.h mEnvelopeGenerator mMIDIReceiver , . MIDIReceiver enterStage note on/off.
, . . MIDIReceiver EnvelopeGenerator . , MIDIReceiver , EnvelopeGenerator .

. Qt. , , , . , . , setText() , . , , , . , , . ( Oscillator , EnvelopeGenerator , MIDIReceiver ). , . . Synthesis .

Qt . Signals . . Signal.h GallantSignal.h , . Delegate.h GallantSignal.h ( , , “Copy items into destination group's folder”) Xcode. VisualStudio , Solution Explorer.

, MIDIReceiver , . MIDIReceiver.h :

#include "GallantSignal.h" using Gallant::Signal2;

Signal2 , . — Signal0 Signal8 , .
public :

Signal2< int, int > noteOn; Signal2< int, int > noteOff;

, int . MIDIReceiver.cpp advance :

// A key pressed later overrides any previously pressed key: if (noteNumber != mLastNoteNumber) { mLastNoteNumber = noteNumber; mLastFrequency = noteNumberToFrequency(mLastNoteNumber); mLastVelocity = velocity; // Emit a "note on" signal: noteOn(noteNumber, velocity); }

// If the last note was released, nothing should play: if (noteNumber == mLastNoteNumber) { mLastNoteNumber = -1; noteOff(noteNumber, mLastVelocity); }

— , — . mLastFrequency -1 , : RELEASE. mLastVelocity : , .
, , , . . .

mEnvelopeGenerator . - onNoteOn onNoteOff EnvelopeGenerator . , EnvelopeGenerator . , . enterStage - . - private Synthesis :

inline void onNoteOn(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK); }; inline void onNoteOff(const int noteNumber, const int velocity) { mEnvelopeGenerator.enterStage(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE); };

.
, Synthesis.cpp :

mMIDIReceiver.noteOn.Connect(this, &Synthesis::onNoteOn); mMIDIReceiver.noteOff.Connect(this, &Synthesis::onNoteOff);

— , — -.
ProcessDoubleReplacing . if , , .

!

. . DECAY, RELEASE, . stageValues .
, , . GUI - :



!

.

:
martin-finke.de/blog/articles/audio-plugins-011-envelopes

Source: https://habr.com/ru/post/227475/


All Articles