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-envelopes
Source: https://habr.com/ru/post/227475/
All Articles