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

シリーズのすべての投稿:

パート1.紹介とセットアップ

パート2.コードの学習

パート3. VSTおよびAU

パート4.デジタル歪み

パート5.プリセットとGUI

パート6.信号合成

パート7. MIDIメッセージの受信

パート8.仮想キーボード

パート9.封筒

パート10. GUIの改善

パート11.フィルター

パート12.低周波発振器

パート13.再設計

パート14.ポリフォニー1

パート15.ポリフォニー2

パート16.アンチエイリアス






今日は、共振フィルターを作成します。 フィルタ設計は複雑な領域であり、世界中の多くのDSPエンジニアが困惑しています。 ジャングルに飛び込むのではなく Paul Kellet アルゴリズムに基づいて単純なローパスフィルターLow-Pass )、 バンドパスBand-Pass )、およびハイパスフィルターHigh-Pass )を作成します



ご想像のとおり、 Filter



クラスを作成することから始めましょう。 削除する
 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;



EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter












#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));



. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter












#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);



. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter












#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));



, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter












#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;



:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter












#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);



, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter












#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());



EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter












#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








 #include  Filter.h ( )     : 
      



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter








#include Filter.h ( ) :



class Filter { public: enum FilterMode { FILTER_MODE_LOWPASS = 0, FILTER_MODE_HIGHPASS, FILTER_MODE_BANDPASS, kNumFilterModes }; Filter() : cutoff(0.99), resonance(0.0), mode(FILTER_MODE_LOWPASS), buf0(0.0), buf1(0.0) { calculateFeedbackAmount(); }; double process(double inputValue); inline void setCutoff(double newCutoff) { cutoff = newCutoff; calculateFeedbackAmount(); }; inline void setResonance(double newResonance) { resonance = newResonance; calculateFeedbackAmount(); }; inline void setFilterMode(FilterMode newMode) { mode = newMode; } private: double cutoff; double resonance; FilterMode mode; double feedbackAmount; inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - cutoff); } double buf0; double buf1; };








private



cutoff



resonance



. Mode



(Lowpass, Highpass, Bandpass). feedbackAmount



, buf0



buf1



, .

( calculateFeedbackAmount()



). process



. feedbackAmount



cutoff



resonance



, calculateFeedbackAmount



.



Filter.cpp :



// By Paul Kellett // http://www.musicdsp.org/showone.php?id=29 double Filter::process(double inputValue) { buf0 += cutoff * (inputValue - buf0); buf1 += cutoff * (buf0 - buf1); switch (mode) { case FILTER_MODE_LOWPASS: return buf1; case FILTER_MODE_HIGHPASS: return inputValue - buf0; case FILTER_MODE_BANDPASS: return buf0 - buf1; default: return 0.0; } }







, ? , . « » , (. . 6 ). buf0



buf1



: . inputValue



, - buf0



( ). . 6 / 12 /. switch



, buf1



. , , buf0



, 12, 6 /, .



case



FILTER_MODE_HIGHPASS



. buf0



. inputValue



buf0



, . buf1



, .



case



FILTER_MODE_BANDPASS



, buf0 - buf1



. buf0



, buf1



. , .

: , .



buf1



. ( Infinite Impulse Response , IIR ). , . , , .





, , . , , , , . , NI Massive, « ». , , . , ( Massive ). «» , , , AAS Ultra Analog, , , , . , , , , NI Reaktor. , — , .. .





. GUI. bg.png ( “Move to trash” Xcode), :



filtermode.png knob_small.png ( , 50 50 ) bg.png ( )



ID resource.h :



// Unique IDs for each image resource. #define BG_ID 101 #define WHITE_KEY_ID 102 #define BLACK_KEY_ID 103 #define WAVEFORM_ID 104 #define KNOB_ID 105 #define KNOB_SMALL_ID 106 #define FILTERMODE_ID 107 // Image resource locations for this plug. #define BG_FN "resources/img/bg.png" #define WHITE_KEY_FN "resources/img/whitekey.png" #define BLACK_KEY_FN "resources/img/blackkey.png" #define WAVEFORM_FN "resources/img/waveform.png" #define KNOB_FN "resources/img/knob.png" #define KNOB_SMALL_FN "resources/img/knob_small.png" #define FILTERMODE_FN "resources/img/filtermode.png"







Synthesis.rc :



#include "resource.h" BG_ID PNG BG_FN WHITE_KEY_ID PNG WHITE_KEY_FN BLACK_KEY_ID PNG BLACK_KEY_FN WAVEFORM_ID PNG WAVEFORM_FN KNOB_ID PNG KNOB_FN KNOB_SMALL_ID PNG KNOB_SMALL_FN FILTERMODE_ID PNG FILTERMODE_FN







#include "Filter.h"



Synthesis.h private



:



Filter mFilter;







EParams



Synthesis.cpp :



enum EParams { mWaveform = 0, mAttack, mDecay, mSustain, mRelease, mFilterMode, mFilterCutoff, mFilterResonance, mFilterAttack, mFilterDecay, mFilterSustain, mFilterRelease, mFilterEnvelopeAmount, kNumParams };







:



pGraphics->AttachControl(new ISwitchControl(this, 24, 38, mWaveform, &waveformBitmap));







. (Lowpass, Highpass, Bandpass). AttachGraphics(pGraphics)



:



GetParam(mFilterMode)->InitEnum("Filter Mode", Filter::FILTER_MODE_LOWPASS, Filter::kNumFilterModes); IBitmap filtermodeBitmap = pGraphics->LoadIBitmap(FILTERMODE_ID, FILTERMODE_FN, 3); pGraphics->AttachControl(new ISwitchControl(this, 24, 123, mFilterMode, &filtermodeBitmap));







. . knob_small.png :



// Knobs for filter cutoff and resonance IBitmap smallKnobBitmap = pGraphics->LoadIBitmap(KNOB_SMALL_ID, KNOB_SMALL_FN, 64); // Cutoff knob: GetParam(mFilterCutoff)->InitDouble("Cutoff", 0.99, 0.01, 0.99, 0.001); GetParam(mFilterCutoff)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 5, 177, mFilterCutoff, &smallKnobBitmap)); // Resonance knob: GetParam(mFilterResonance)->InitDouble("Resonance", 0.01, 0.01, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 61, 177, mFilterResonance, &smallKnobBitmap));







, 1.0



, calculateFeedbackAmount



, .

ProcessDoubleReplacing



:



leftOutput[i] = rightOutput[i] = mFilter.process(mOscillator.nextSample() * mEnvelopeGenerator.nextSample() * velocity / 127.0);







. switch



Synthesis::OnParamChange



:



case mFilterCutoff: mFilter.setCutoff(GetParam(paramIdx)->Value()); break; case mFilterResonance: mFilter.setResonance(GetParam(paramIdx)->Value()); break; case mFilterMode: mFilter.setFilterMode(static_cast<Filter::FilterMode>(GetParam(paramIdx)->Int())); break;







. - .







— . , , .

:



buf0 += cutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1));







, , ( buf0 – buf1



), feedbackAmount



. calculateFeedbackAmount



feedbackAmount



resonance



, . . , .



, . , , . , , .



-12 / -24 /

! 24 . switch



Filter::process



:



buf2 += cutoff * (buf1 - buf2); buf3 += cutoff * (buf2 – buf3);







: , , . , , , ( ). switch



:



switch (mode) { case FILTER_MODE_LOWPASS: return buf3; case FILTER_MODE_HIGHPASS: return inputValue - buf3; case FILTER_MODE_BANDPASS: return buf0 - buf3; default: return 0.0; }







buf1



buf3



– . -24 /. buf2



buf3



, private



Filter.h :



double buf2; double buf3;







, buf0



c buf1



:



Filter() : // ... buf0(0.0), buf1(0.0), buf2(0.0), buf3(0.0) // ...







, , : . .

?





. , , .

- cutoff



. . cutoffMod



, , cutoff



. #include private



:



double cutoffMod;







:



Filter() : cutoff(0.99), resonance(0.01), cutoffMod(0.0), // ...







. private



:



inline double getCalculatedCutoff() const { return fmax(fmin(cutoff + cutoffMod, 0.99), 0.01); };







calculateFeedbackAmount



:



inline void calculateFeedbackAmount() { feedbackAmount = resonance + resonance/(1.0 - getCalculatedCutoff()); }







cutoffMod



. feedbackAmount



, :



inline void setCutoffMod(double newCutoffMod) { cutoffMod = newCutoffMod; calculateFeedbackAmount(); }







, . Filter::process



:



if (inputValue == 0.0) return inputValue; double calculatedCutoff = getCalculatedCutoff(); buf0 += calculatedCutoff * (inputValue - buf0 + feedbackAmount * (buf0 - buf1)); buf1 += calculatedCutoff * (buf0 - buf1); buf2 += calculatedCutoff * (buf1 - buf2); buf3 += calculatedCutoff * (buf2 – buf3);







, . , , . . cutoff



calculatedCutoff



-.



, ( setCutoffMod



), Synthesis



, , . , cutoffMod



. filterEnvelopeAmount



-1



+1



. GUI.



private



Synthesis.h :



EnvelopeGenerator mFilterEnvelopeGenerator; double filterEnvelopeAmount;







MIDI , onNoteOn



onNoteOff



:



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







. ProcessDoubleReplacing



:



mFilter.setCutoffMod(mFilterEnvelopeGenerator.nextSample() * filterEnvelopeAmount);







, nextSample



filterEnvelopeAmount



cutoffMod



. filterEnvelopeAmount



:



Synthesis::Synthesis(IPlugInstanceInfo instanceInfo) : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo), lastVirtualKeyboardNoteNumber(virtualKeyboardMinimumNoteNumber - 1), filterEnvelopeAmount(0.0) { // ... }







Synthesis::Reset



:



mFilterEnvelopeGenerator.setSampleRate(GetSampleRate());







EParams



, . AttachGraphics



:



// Knobs for filter envelope // Attack knob GetParam(mFilterAttack)->InitDouble("Filter Env Attack", 0.01, 0.01, 10.0, 0.001); GetParam(mFilterAttack)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 139, 178, mFilterAttack, &smallKnobBitmap)); // Decay knob: GetParam(mFilterDecay)->InitDouble("Filter Env Decay", 0.5, 0.01, 15.0, 0.001); GetParam(mFilterDecay)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 195, 178, mFilterDecay, &smallKnobBitmap)); // Sustain knob: GetParam(mFilterSustain)->InitDouble("Filter Env Sustain", 0.1, 0.001, 1.0, 0.001); GetParam(mFilterSustain)->SetShape(2); pGraphics->AttachControl(new IKnobMultiControl(this, 251, 178, mFilterSustain, &smallKnobBitmap)); // Release knob: GetParam(mFilterRelease)->InitDouble("Filter Env Release", 1.0, 0.001, 15.0, 0.001); GetParam(mFilterRelease)->SetShape(3); pGraphics->AttachControl(new IKnobMultiControl(this, 307, 178, mFilterRelease, &smallKnobBitmap)); // Filter envelope amount knob: GetParam(mFilterEnvelopeAmount)->InitDouble("Filter Env Amount", 0.0, -1.0, 1.0, 0.001); pGraphics->AttachControl(new IKnobMultiControl(this, 363, 178, mFilterEnvelopeAmount, &smallKnobBitmap));







, smallKnobBitmap



. . , . Synthesis::OnParamChange



switch



:



case mFilterAttack: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_ATTACK, GetParam(paramIdx)->Value()); break; case mFilterDecay: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_DECAY, GetParam(paramIdx)->Value()); break; case mFilterSustain: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_SUSTAIN, GetParam(paramIdx)->Value()); break; case mFilterRelease: mFilterEnvelopeGenerator.setStageValue(EnvelopeGenerator::ENVELOPE_STAGE_RELEASE, GetParam(paramIdx)->Value()); break; case mFilterEnvelopeAmount: filterEnvelopeAmount = GetParam(paramIdx)->Value(); break;







!

! (- C1), :







EnvelopeGenerator



. . !



.



:)



:

martin-finke.de/blog/articles/audio-plugins-013-filter











All Articles