enterStage member enterStage .enterStage for the transitiondouble 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.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; }; 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.minimumLevel required, since the signal level calculations will not work with zero. We initialize this variable with a very small value of 0.001 .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.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.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; } 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 .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.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-envelopesSource: https://habr.com/ru/post/227475/
All Articles