オーディオプラグインの作成、パート9

シリーズのすべての投稿:
パート1.紹介とセットアップ
パート2.コードの学習
パート3. VSTおよびAU
パート4.デジタル歪み
パート5.プリセットとGUI
パート6.信号合成
パート7. MIDIメッセージの受信
パート8.仮想キーボード
パート9.封筒
パート10. GUIの改善
パート11.フィルター
パート12.低周波発振器
パート13.再設計
パート14.ポリフォニー1
パート15.ポリフォニー2
パート16.アンチエイリアス



サウンドは、変化が生じたときに興味深いものになります。 音量を変えるエンベロープジェネレーターを作成しましょう。


封筒



基本原則



頭字語ADSRAttack、Decay、Sustain、Release )に慣れていない場合は、続行する前にこのウィキペディアの記事を読んでください。
本質的に、ジェネレーターはOff、Attack、Decay、Sustain、 Releaseの 状態を持つ状態マシンになります。 これは、ジェネレータがこれら5つの可能な状態のいずれか1つにしかならないことを示す、非常に簡潔な方法です。 エンベロープの用語では、これらの状態はステージと呼ばれます。 あるステージから別のステージへの遷移は、メンバー関数enterStage呼び出すことにより実行されenterStage
ステージに関する重要なポイント:



各サンプル信号に対して、ジェネレータはゼロと1の間のdouble値を生成します。 エンベロープジェネレーターから受信した現在の値は、オシレーターの出力信号と乗算されます。 したがって、信号レベルはエンベロープによって決まります。 音でさまざまなことができるようになります。音がゆっくりと浮かんだり、急激に落ちたり、丸太のように平らになったりします。

一般に、ADSRはより高度なサウンドモデリングへの最初のアプローチです。 多くの最新のソフトウェアおよび鉄製シンセサイザーには、多数のステージを備えたエンベロープがあります。 実際、それらはすべて、これから作成するモデルの単なる拡張です。 このようなステートマシンの状態の数は5を少し超えています。

クラスEnvelopeGenerator



新しいEnvelopeGeneratorクラスを作成し、すべてのターゲット(Mac)およびプロジェクト(Windows)に追加します。 EnvelopeGenerator.hの #define#endif間に、クラス宣言を挿入します。

 #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を作成します。 kNumEnvelopeStagesは、合計ステージ数を示します。 このenumはエンベロープクラスのネームスペースの境界内にあり、グローバルネームスペースでは外部からアクセスできないことに注意してください。
メンバー関数を実装する際に、より詳細に検討します。 信号レベルの計算はゼロでは機能しないminimumLevelの最小レベルminimumLevel必要です。 この変数を0.001という非常に小さな値で初期化します。
初期化リストでは、エンベロープはデフォルトでOFFステージにあります。 stageValue配列も定義済みの値で初期化されます:攻撃の100分の1秒、不況の0.5秒、静かなレベル、完全な減衰の1秒。
private currentStageセクションでは、 private currentStageはジェネレーターが現在どのステージにいるかを示します。 urrentLevelは、特定の時間サンプルにおけるエンベロープのボリュームの値です。 multiplyer器は、後で見られるように、指数関数的な減衰multiplyer提供します。
アタック、ディケイ、リリースの各段階では、適切なタイミングで次の段階に進むために、ジェネレーターは時間内にその位置を追跡する必要があります。 これを行うには、 currentSampleIndex変数を使用します。 EnvelopeGenerator.cppで次の関数を追加します。

 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; } 


ステージATTACK、DECAYまたはRELEASEおよびcurrentSampleIndex nextStageSampleIndexが値nextStageSampleIndexに達すると、 enum EnvelopeStage次の要素にnextStageSampleIndexます。 ENVELOPE_STAGE_RELEASE後の剰余( % )による除算によりENVELOPE_STAGE_RELEASEジェネレーターはすぐにENVELOPE_STAGE_OFFに移動します。これは必要なものです。 この遷移は、 enterStageを呼び出すことで実現されenterStage
次に、 currentLevelレベルの新しい値を計算し、 currentSampleIndexを更新して、ジェネレーターの位置を時間的に監視します。 これらの計算は、OFFおよびSUSTAINステージでは実行されません。レベルが変更されないためです。 currentSampleIndexと同じこと:これらの2つの段階は時間制限されていません。

時間の推移



ATTACK、DECAY、およびRELEASEでは、ジェネレーターは所定の時間内に2つの値を切り替えます。 人間の耳は、対数スケールでラウドネス知覚します。 したがって、音量の変化が耳で直線的に知覚されるためには、指数関数的に発生する必要があります。
2つのポイント間に指数曲線を作成する方法はいくつかあります。 最初に頭に浮かぶのは、各サンプルがライブラリから比較的重いexp関数を呼び出すことです . , : , , .
( ):

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/J227475/


All Articles