Make duplicate script compatible with Python 3
[wdl/wdl-ol.git] / IPlugExamples / IPlugPolySynth / IPlugPolySynth.cpp
blobcbbb9b40ec03037e6ec01e3f6e66117dee56ec40
1 #include "IPlugPolySynth.h"
2 #include "IPlug_include_in_plug_src.h"
3 #include "resource.h"
5 #include "IControl.h"
6 #include "IKeyboardControl.h"
9 const int kNumPrograms = 8;
11 #define PITCH 440.
12 #define TABLE_SIZE 512
14 #ifndef M_PI
15 #define M_PI 3.14159265
16 #endif
18 #define GAIN_FACTOR 0.2;
20 enum EParams
22 kAttack = 0,
23 kDecay,
24 kSustain,
25 kRelease,
26 kNumParams
29 IPlugPolySynth::IPlugPolySynth(IPlugInstanceInfo instanceInfo)
30 : IPLUG_CTOR(kNumParams, kNumPrograms, instanceInfo),
31 mSampleRate(44100.),
32 mNumHeldKeys(0),
33 mKey(-1),
34 mActiveVoices(0)
37 TRACE;
39 mTable = new double[TABLE_SIZE];
41 for (int i = 0; i < TABLE_SIZE; i++)
43 mTable[i] = sin( i/double(TABLE_SIZE) * 2. * M_PI);
44 //printf("mTable[%i] %f\n", i, mTable[i]);
47 mOsc = new CWTOsc(mTable, TABLE_SIZE);
48 mEnv = new CADSREnvL();
50 memset(mKeyStatus, 0, 128 * sizeof(bool));
52 //arguments are: name, defaultVal, minVal, maxVal, step, label
53 GetParam(kAttack)->InitDouble("Amp Attack", ATTACK_DEFAULT, TIME_MIN, TIME_MAX, 0.001);
54 GetParam(kDecay)->InitDouble("Amp Decay", DECAY_DEFAULT, TIME_MIN, TIME_MAX, 0.001);
55 GetParam(kSustain)->InitDouble("Amp Sustain", 1., 0., 1., 0.001);
56 GetParam(kRelease)->InitDouble("Amp Release", RELEASE_DEFAULT, TIME_MIN, TIME_MAX, 0.001);
58 IGraphics* pGraphics = MakeGraphics(this, kWidth, kHeight);
59 pGraphics->AttachBackground(BG_ID, BG_FN);
61 IBitmap knob = pGraphics->LoadIBitmap(KNOB_ID, KNOB_FN, kKnobFrames);
62 IText text = IText(14);
63 IBitmap regular = pGraphics->LoadIBitmap(WHITE_KEY_ID, WHITE_KEY_FN, 6);
64 IBitmap sharp = pGraphics->LoadIBitmap(BLACK_KEY_ID, BLACK_KEY_FN);
66 // C# D# F# G# A#
67 int coords[12] = { 0, 7, 12, 20, 24, 36, 43, 48, 56, 60, 69, 72 };
68 mKeyboard = new IKeyboardControl(this, kKeybX, kKeybY, 48, 5, &regular, &sharp, coords);
70 pGraphics->AttachControl(mKeyboard);
72 IBitmap about = pGraphics->LoadIBitmap(ABOUTBOX_ID, ABOUTBOX_FN);
73 mAboutBox = new IBitmapOverlayControl(this, 100, 100, &about, IRECT(540, 250, 680, 290));
74 pGraphics->AttachControl(mAboutBox);
75 AttachGraphics(pGraphics);
77 //MakePreset("preset 1", ... );
78 MakeDefaultPreset((char *) "-", kNumPrograms);
81 IPlugPolySynth::~IPlugPolySynth()
83 delete mOsc;
84 delete mEnv;
85 delete [] mTable;
88 int IPlugPolySynth::FindFreeVoice()
90 int v;
92 for(v = 0; v < MAX_VOICES; v++)
94 if(!mVS[v].GetBusy())
95 return v;
98 int quietestVoice = 0;
99 double level = 2.;
101 for(v = 0; v < MAX_VOICES; v++)
103 double summed = mVS[v].mEnv_ctx.mPrev;
105 if (summed < level)
107 level = summed;
108 quietestVoice = v;
113 DBGMSG("stealing voice %i\n", quietestVoice);
114 return quietestVoice;
117 void IPlugPolySynth::NoteOnOff(IMidiMsg* pMsg)
119 int v;
121 int status = pMsg->StatusMsg();
122 int velocity = pMsg->Velocity();
123 int note = pMsg->NoteNumber();
125 if (status == IMidiMsg::kNoteOn && velocity) // Note on
127 v = FindFreeVoice(); // or quietest
128 mVS[v].mKey = note;
129 mVS[v].mOsc_ctx.mPhaseIncr = (1./mSampleRate) * midi2CPS(note);
130 mVS[v].mEnv_ctx.mLevel = (double) velocity / 127.;
131 mVS[v].mEnv_ctx.mStage = kStageAttack;
133 mActiveVoices++;
135 else // Note off
137 for (v = 0; v < MAX_VOICES; v++)
139 if (mVS[v].mKey == note)
141 if (mVS[v].GetBusy())
143 mVS[v].mKey = -1;
144 mVS[v].mEnv_ctx.mStage = kStageRelease;
145 mVS[v].mEnv_ctx.mReleaseLevel = mVS[v].mEnv_ctx.mPrev;
147 return;
155 void IPlugPolySynth::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames)
157 // Mutex is already locked for us
158 IKeyboardControl* pKeyboard = (IKeyboardControl*) mKeyboard;
160 if (pKeyboard->GetKey() != mKey)
162 IMidiMsg msg;
164 if (mKey >= 0)
166 msg.MakeNoteOffMsg(mKey + 48, 0, 0);
167 mMidiQueue.Add(&msg);
170 mKey = pKeyboard->GetKey();
172 if (mKey >= 0)
174 msg.MakeNoteOnMsg(mKey + 48, pKeyboard->GetVelocity(), 0, 0);
175 mMidiQueue.Add(&msg);
179 if (mActiveVoices > 0 || !mMidiQueue.Empty()) // block not empty
181 double* out1 = outputs[0];
182 double* out2 = outputs[1];
184 double output;
185 CVoiceState* vs;
187 for (int s = 0; s < nFrames; ++s)
189 while (!mMidiQueue.Empty())
191 IMidiMsg* pMsg = mMidiQueue.Peek();
193 if (pMsg->mOffset > s) break;
195 int status = pMsg->StatusMsg(); // get the MIDI status byte
197 switch (status)
199 case IMidiMsg::kNoteOn:
200 case IMidiMsg::kNoteOff:
202 NoteOnOff(pMsg);
203 break;
205 case IMidiMsg::kPitchWheel:
207 //TODO
208 break;
212 mMidiQueue.Remove();
215 output = 0.;
217 for(int v = 0; v < MAX_VOICES; v++) // for each vs
219 vs = &mVS[v];
221 if (vs->GetBusy())
223 output += mOsc->process(&vs->mOsc_ctx) * mEnv->process(&vs->mEnv_ctx);
227 output *= GAIN_FACTOR;
229 *out1++ = output;
230 *out2++ = output;
233 mMidiQueue.Flush(nFrames);
235 // else // empty block
236 // {
237 // }
240 void IPlugPolySynth::Reset()
242 TRACE;
243 IMutexLock lock(this);
245 mSampleRate = GetSampleRate();
246 mMidiQueue.Resize(GetBlockSize());
247 mEnv->setSampleRate(mSampleRate);
250 void IPlugPolySynth::OnParamChange(int paramIdx)
252 IMutexLock lock(this);
254 switch (paramIdx)
256 case kAttack:
257 mEnv->setStageTime(kStageAttack, GetParam(kAttack)->Value());
258 break;
259 case kDecay:
260 mEnv->setStageTime(kStageDecay, GetParam(kDecay)->Value());
261 break;
262 case kSustain:
263 mEnv->setSustainLevel( GetParam(kSustain)->Value() );
264 break;
265 case kRelease:
266 mEnv->setStageTime(kStageRelease, GetParam(kRelease)->Value());
267 break;
268 default:
269 break;
273 void IPlugPolySynth::ProcessMidiMsg(IMidiMsg* pMsg)
275 int status = pMsg->StatusMsg();
276 int velocity = pMsg->Velocity();
278 switch (status)
280 case IMidiMsg::kNoteOn:
281 case IMidiMsg::kNoteOff:
282 // filter only note messages
283 if (status == IMidiMsg::kNoteOn && velocity)
285 mKeyStatus[pMsg->NoteNumber()] = true;
286 mNumHeldKeys += 1;
288 else
290 mKeyStatus[pMsg->NoteNumber()] = false;
291 mNumHeldKeys -= 1;
293 break;
294 default:
295 return; // if !note message, nothing gets added to the queue
299 mKeyboard->SetDirty();
300 mMidiQueue.Add(pMsg);
303 // Should return non-zero if one or more keys are playing.
304 int IPlugPolySynth::GetNumKeys()
306 IMutexLock lock(this);
307 return mNumHeldKeys;
310 // Should return true if the specified key is playing.
311 bool IPlugPolySynth::GetKeyStatus(int key)
313 IMutexLock lock(this);
314 return mKeyStatus[key];
317 //Called by the standalone wrapper if someone clicks about
318 bool IPlugPolySynth::HostRequestingAboutBox()
320 IMutexLock lock(this);
321 if(GetGUI())
323 // get the IBitmapOverlay to show
324 mAboutBox->SetValueFromPlug(1.);
325 mAboutBox->Hide(false);
327 return true;