オーディオプラグインの作成、パート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







All Articles