tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_FourOscPlugin.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2018
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7*/
8
9
10namespace tracktion { inline namespace engine
11{
12
13//==============================================================================
14namespace Distortion
15{
16 inline float saturate (float input, float drive, float lowclip, float highclip)
17 {
18 input = juce::jlimit (lowclip, highclip, input);
19 return input - drive * input * std::fabs (input);
20 }
21
22 inline void distortion (float* data, int count, float drive, float lowclip, float highclip)
23 {
24 if (drive <= 0.f)
25 return;
26
27 float gain = drive < 0.5f ? 2.0f * drive + 1.0f : drive * 4.0f;
28
29 while (--count >= 0)
30 {
31 *data = saturate (*data, drive, lowclip, highclip) * gain;
32 data++;
33 }
34 }
35}
36
37inline void clip (float* data, int numSamples)
38{
39 while (--numSamples >= 0)
40 {
41 *data = juce::jlimit (-1.0f, 1.0f, *data);
42 data++;
43 }
44}
45
46//==============================================================================
48{
49public:
50 FODelayLine (float maximumDelay = 0.001f, float sr = 44100.0f)
51 {
52 resize (maximumDelay, sr);
53 }
54
55 void resize (float maximumDelay, float sr)
56 {
57 sampleRate = sr;
58 numSamples = (int) std::ceil (maximumDelay * sampleRate);
59 sampleBuffer.resize ((size_t) numSamples);
60 const auto num = (size_t) numSamples; // Workaround for a GCC warning
61 memset (sampleBuffer.data(), 0, sizeof(float) * num);
62 currentPos = 0;
63 }
64
65 void reset()
66 {
67 const auto num = (size_t) numSamples; // Workaround for a GCC warning
68 memset (sampleBuffer.data(), 0, sizeof(float) * num);
69 }
70
71 inline float samplesToSeconds (float numSamplesIn, float sampleRateIn)
72 {
73 return numSamplesIn / sampleRateIn;
74 }
75
76 inline float read (float atTime)
77 {
78 jassert (atTime >= 0.0f && atTime < samplesToSeconds (float (numSamples), sampleRate));
79
80 float pos = std::max (1.0f, atTime * (sampleRate - 1));
81
82 int intPos = (int) std::floor (pos);
83 float f = pos - intPos;
84
85 int n1 = currentPos - intPos;
86 while (n1 < 0)
87 n1 += numSamples;
88 while (n1 >= numSamples)
89 n1 -= numSamples;
90
91 int n2 = n1 - 1;
92 if (n2 < 0)
93 n2 += numSamples;
94
95 jassert (n1 >= 0 && n1 < numSamples);
96 jassert (n2 >= 0 && n2 < numSamples);
97
98 return (1.0f - f) * sampleBuffer[(size_t) n1] + f * sampleBuffer[(size_t) n2];
99 }
100
101 inline void write (const float input)
102 {
103 sampleBuffer[(size_t) currentPos] = input;
104 currentPos++;
105
106 if (currentPos >= numSamples)
107 currentPos = 0;
108 }
109
110protected:
111 int numSamples {0};
112 float sampleRate {44100};
113 int currentPos {0};
114 std::vector<float> sampleBuffer;
115};
116
117//==============================================================================
119{
120public:
121 void process (juce::AudioBuffer<float>& buffer, int numSamples)
122 {
123 float* lOut = buffer.getWritePointer (0);
124 float* rOut = buffer.getWritePointer (1);
125
126 AudioScratchBuffer scratchBuffer (buffer);
127
128 float* lWork = scratchBuffer.buffer.getWritePointer (0);
129 float* rWork = scratchBuffer.buffer.getWritePointer (1);
130
131 juce::FloatVectorOperations::copy (lWork, lOut, numSamples);
132 juce::FloatVectorOperations::copy (rWork, rOut, numSamples);
133
134 for (int i = 0; i < numSamples; i++)
135 {
136 const float lVal = leftDelay.read (delay);
137 const float rVal = rightDelay.read (delay);
138
139 leftDelay.write (lWork[i] + (feedback * lVal) + (crossfeed * rVal));
140 rightDelay.write (rWork[i] + (feedback * rVal) + (crossfeed * lVal));
141
142 lWork[i] = lVal;
143 rWork[i] = rVal;
144 }
145
146 // Wet/Dry Mix
147 for (int i = 0; i < numSamples; i++)
148 {
150
151 lOut[i] = (wetDry.gain2 * lOut[i]) + (wetDry.gain1 * lWork[i]);
152 rOut[i] = (wetDry.gain2 * rOut[i]) + (wetDry.gain1 * rWork[i]);
153 }
154 }
155
156 void setSampleRate (double sr)
157 {
158 leftDelay.resize (5.1f, float (sr));
159 rightDelay.resize (5.1f, float (sr));
160 }
161
162 void setParams (float delayIn, float feedbackIn, float crossfeedIn, float mixIn)
163 {
164 delay = delayIn;
165 feedback = std::min (0.99f, feedbackIn);
166 crossfeed = std::min (0.99f, crossfeedIn);
167 mix = mixIn;
168 }
169
170 void reset()
171 {
172 leftDelay.reset();
173 rightDelay.reset();
174 }
175
176private:
177 FODelayLine leftDelay, rightDelay;
178
179 float mix = 0, feedback = 0, delay = 0, crossfeed = 0;
180};
181
182//==============================================================================
184{
185public:
186 void process (juce::AudioBuffer<float>& buffer, int numSamples)
187 {
188 float ph = 0.0f;
189 int bufPos = 0;
190
191 const float delayMs = 20.0f;
192 const float minSweepSamples = (float) ((delayMs * sampleRate) / 1000.0);
193 const float maxSweepSamples = (float) (((delayMs + depthMs) * sampleRate) / 1000.0);
194 const float speed = (float)((juce::MathConstants<double>::pi * 2.0) / (sampleRate / speedHz));
195 const int maxLengthMs = 1 + juce::roundToInt (delayMs + depthMs);
196 const int lengthInSamples = juce::roundToInt ((maxLengthMs * sampleRate) / 1000.0);
197
198 delayBuffer.ensureMaxBufferSize (lengthInSamples);
199
200 const float lfoFactor = 0.5f * (maxSweepSamples - minSweepSamples);
201 const float lfoOffset = minSweepSamples + lfoFactor;
202
204
205 for (int chan = buffer.getNumChannels(); --chan >= 0;)
206 {
207 float* const d = buffer.getWritePointer (chan, 0);
208 float* const buf = (float*) delayBuffer.buffers[chan].getData();
209
210 ph = phase;
211 if (chan > 0)
212 ph += juce::MathConstants<float>::pi * width;
213
214 bufPos = delayBuffer.bufferPos;
215
216 for (int i = 0; i < numSamples; ++i)
217 {
218 const float in = d[i];
219
220 const float sweep = lfoOffset + lfoFactor * sinf (ph);
221 ph += speed;
222
223 int intSweepPos = juce::roundToInt (sweep);
224 const float interp = sweep - intSweepPos;
225 intSweepPos = bufPos + lengthInSamples - intSweepPos;
226
227 const float out = buf[(intSweepPos - 1) % lengthInSamples] * interp + buf[intSweepPos % lengthInSamples] * (1.0f - interp);
228
229 float n = in;
230
232
233 buf[bufPos] = n;
234 d[i] = out * wetDry.gain1 + in * wetDry.gain2;
235 bufPos = (bufPos + 1) % lengthInSamples;
236 }
237 }
238
239 jassert (! hasFloatingPointDenormaliseOccurred());
240 zeroDenormalisedValuesIfNeeded (buffer);
241
242 phase = ph;
243 if (phase >= juce::MathConstants<float>::pi * 2)
245
246 delayBuffer.bufferPos = bufPos;
247 }
248
249 void setSampleRate (double sr)
250 {
251 sampleRate = sr;
252
253 const float delayMs = 20.0f;
254 auto maxLengthMs = 1 + juce::roundToInt (delayMs + depthMs);
255 auto bufferSizeSamples = juce::roundToInt ((maxLengthMs * sr) / 1000.0);
256 delayBuffer.ensureMaxBufferSize (bufferSizeSamples);
257 delayBuffer.clearBuffer();
258 phase = 0.0f;
259 }
260
261 void setParams (float speedIn, float depthIn, float widthIn, float mixIn)
262 {
263 speedHz = speedIn;
264 depthMs = depthIn;
265 width = widthIn;
266 mix = mixIn;
267 }
268
269 void reset()
270 {
271 delayBuffer.clearBuffer();
272 }
273
274private:
275 DelayBufferBase delayBuffer;
276 double sampleRate = 0;
277
278 float phase = 0, speedHz = 1.0f, depthMs = 3.0f, width = 0.5f, mix = 0;
279};
280
281//==============================================================================
283{
284public:
285 FourOscVoice (FourOscPlugin& s) : synth (s)
286 {
287 for (auto p : synth.getAutomatableParameters())
288 smoothers[p] = {};
289 }
290
291 void noteStarted() override
292 {
293 if (isPlaying)
294 {
295 if (synth.isLegato())
296 {
297 activeNote.setTargetValue (currentlyPlayingNote.initialNote);
298
299 ampAdsr.noteOn();
300 filterAdsr.noteOn();
301 modAdsr1.noteOn();
302 modAdsr2.noteOn();
303 }
304 else
305 {
306 noteStopped (true);
307 retrigger = true;
308 isQuickStop = true;
309 }
310 }
311 else
312 {
313 activeNote.setCurrentAndTargetValue (currentlyPlayingNote.initialNote);
314
315 isPlaying = true;
316 isQuickStop = false;
317 retrigger = false;
318
319 ampAdsr.reset();
320 filterAdsr.reset();
321 modAdsr1.reset();
322 modAdsr2.reset();
323 lfo1.reset();
324 lfo2.reset();
325
326 juce::ScopedValueSetter<bool> svs (snapAllValues, true);
327 updateParams (0);
328
329 ampAdsr.noteOn();
330 filterAdsr.noteOn();
331 modAdsr1.noteOn();
332 modAdsr2.noteOn();
333 lfo1.reset();
334 lfo2.reset();
335
336 filterL1.reset();
337 filterR1.reset();
338 filterL2.reset();
339 filterR2.reset();
340
341 for (auto& o : oscillators)
342 o.start();
343
344 filterFrequencySmoother.snapToValue();
345
346 firstBlock = true;
347 }
348 }
349
350 void noteStopped (bool allowTailOff) override
351 {
352 if (allowTailOff)
353 {
354 ampAdsr.noteOff();
355 filterAdsr.noteOff();
356 modAdsr1.noteOff();
357 modAdsr2.noteOff();
358 }
359 else
360 {
361 ampAdsr.reset();
362 filterAdsr.reset();
363 modAdsr1.reset();
364 modAdsr2.reset();
366 isPlaying = false;
367 isQuickStop = false;
368 }
369 }
370
371 void setCurrentSampleRate (double newRate) override
372 {
373 if (newRate > 0)
374 {
375 MPESynthesiserVoice::setCurrentSampleRate (newRate);
376
377 for (auto& o : oscillators)
378 o.setSampleRate (newRate);
379
380 ampAdsr.setSampleRate (newRate);
381 filterAdsr.setSampleRate (newRate);
382 modAdsr1.setSampleRate (newRate);
383 modAdsr2.setSampleRate (newRate);
384 lfo1.setSampleRate (newRate);
385 lfo2.setSampleRate (newRate);
386
387 lastLegato = paramValue (synth.legato);
388 activeNote.reset (newRate, paramValue (synth.legato) / 1000.0f);
389 filterFrequencySmoother.reset (newRate, 0.05f);
390
391 for (auto& itr : smoothers)
392 itr.second.reset (newRate, 0.01f);
393 }
394 }
395
396 float velocityToGain (float velocity, float velocitySensitivity = 1.0f)
397 {
398 float v = velocity * velocitySensitivity + 1.0f - velocitySensitivity;
399 return v * std::pow (25.0f, v) * 0.04f;
400 }
401
402 using MPESynthesiserVoice::renderNextBlock;
403 void renderNextBlock (juce::AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override
404 {
405 juce::ScopedValueSetter<bool> svs (snapAllValues, firstBlock || snapAllValues);
406
407 updateParams (numSamples);
408
409 if (firstBlock)
410 {
411 filterFrequencySmoother.snapToValue();
412 firstBlock = false;
413 }
414
415 if (numSamples > renderBuffer.getNumSamples())
416 renderBuffer.setSize (2, numSamples, false, false, true);
417
418 renderBuffer.clear();
419
420 // Run oscillators
421 for (auto& o : oscillators)
422 o.process (renderBuffer, 0, numSamples);
423
424 // Apply velocity
425 float velocityGain = velocityToGain (currentlyPlayingNote.noteOnVelocity.asUnsignedFloat(), paramValue (synth.ampVelocity) / 100.0f);
426 velocityGain = juce::jlimit (0.0f, 1.0f, velocityGain);
427 renderBuffer.applyGain (velocityGain);
428
429 // Apply filter
430 if (synth.filterTypeValue != 0)
431 {
432 filterL1.processSamples (renderBuffer.getWritePointer (0), numSamples);
433 filterR1.processSamples (renderBuffer.getWritePointer (1), numSamples);
434
435 if (synth.filterSlopeValue == 24)
436 {
437 clip (renderBuffer.getWritePointer (0), numSamples);
438 clip (renderBuffer.getWritePointer (1), numSamples);
439
440 filterL2.processSamples (renderBuffer.getWritePointer (0), numSamples);
441 filterR2.processSamples (renderBuffer.getWritePointer (1), numSamples);
442 }
443 }
444
445 // Apply ADSR
446 ampAdsr.applyEnvelopeToBuffer (renderBuffer, 0, numSamples);
447
448 // Add to output
449 if (outputBuffer.getNumChannels() == 1)
450 {
451 outputBuffer.addFrom (0, startSample, renderBuffer, 0, 0, numSamples, 0.5f);
452 outputBuffer.addFrom (0, startSample, renderBuffer, 1, 0, numSamples, 0.5f);
453 }
454 else
455 {
456 outputBuffer.addFrom (0, startSample, renderBuffer, 0, 0, numSamples);
457 outputBuffer.addFrom (1, startSample, renderBuffer, 1, 0, numSamples);
458 }
459
460 if (! ampAdsr.isActive())
461 {
462 isPlaying = false;
463 if (retrigger)
464 {
465 noteStarted();
466 retrigger = false;
467 isQuickStop = false;
468 }
469 else
470 {
472 }
473 }
474
475 for (auto& itr : smoothers)
476 itr.second.process (numSamples);
477 }
478
479 void applyEnvelopeToBuffer (juce::ADSR& adsr, juce::AudioBuffer<float>& buffer, int startSample, int numSamples)
480 {
481 float* l = buffer.getWritePointer (0, startSample);
482 float* r = buffer.getWritePointer (1, startSample);
483
484 while (--numSamples >= 0)
485 {
486 float db = adsr.getNextSample() * 100.0f - 100.0f;
487 float gain = juce::Decibels::decibelsToGain (db);
488
489 *l++ *= gain;
490 *r++ *= gain;
491 }
492 }
493
494 void getLiveModulationPositions (AutomatableParameter::Ptr param, juce::Array<float>& positions)
495 {
496 if (isActive())
497 positions.add (param->valueRange.convertTo0to1 (paramValue (param)));
498 }
499
500 void getLiveFilterFrequency (juce::Array<float>& positions)
501 {
502 if (isActive())
503 positions.add ((12.0f * std::log2 (lastFilterFreq / 440.0f) + 69.0f) / 135.076232f);
504 }
505
506 void updateParams (int numSamples)
507 {
508 // Update mod values
509 currentModValue[(int)FourOscPlugin::lfo1] = lfo1.getCurrentValue();
510 currentModValue[(int)FourOscPlugin::lfo2] = lfo2.getCurrentValue();
511 currentModValue[(int)FourOscPlugin::env1] = modAdsr1.getEnvelopeValue();
512 currentModValue[(int)FourOscPlugin::env2] = modAdsr2.getEnvelopeValue();
513
514 currentModValue[FourOscPlugin::mpePressure] = currentlyPlayingNote.pressure.asUnsignedFloat();
515 currentModValue[FourOscPlugin::mpeTimbre] = currentlyPlayingNote.timbre.asUnsignedFloat();
516 currentModValue[FourOscPlugin::midiNoteNum] = currentlyPlayingNote.initialNote / 127.0f;
517 currentModValue[FourOscPlugin::midiVelocity] = currentlyPlayingNote.noteOnVelocity.asUnsignedFloat();
518
519 for (int i = 0; i <= 127; i++)
520 currentModValue[FourOscPlugin::ccBankSelect + i] = synth.controllerValues[i];
521
522 // Flush the LFOs and envelopes
523 lfo1.process (numSamples);
524 lfo2.process (numSamples);
525 for (int i = 0; i < numSamples; i++)
526 {
527 modAdsr1.getNextSample();
528 modAdsr2.getNextSample();
529 }
530
531 // Mod
532 modAdsr1.setParameters ({
533 paramValue (synth.modEnvParams[0]->modAttack),
534 paramValue (synth.modEnvParams[0]->modDecay),
535 paramValue (synth.modEnvParams[0]->modSustain) / 100.0f,
536 paramValue (synth.modEnvParams[0]->modRelease),
537 });
538
539 modAdsr2.setParameters ({
540 paramValue (synth.modEnvParams[1]->modAttack),
541 paramValue (synth.modEnvParams[1]->modDecay),
542 paramValue (synth.modEnvParams[1]->modSustain) / 100.0f,
543 paramValue (synth.modEnvParams[1]->modRelease),
544 });
545
546 float lfoFreq1;
547 if (synth.lfoParams[0]->syncValue)
548 lfoFreq1 = 1.0f / ((synth.lfoParams[0]->beatValue.get()) / (synth.getCurrentTempo() / 60.0f));
549 else
550 lfoFreq1 = paramValue (synth.lfoParams[0]->rate);
551
552 lfo1.setParameters ({
553 lfoFreq1,
554 0,
555 0,
556 paramValue (synth.lfoParams[0]->depth),
557 (SimpleLFO::WaveShape) synth.lfoParams[0]->waveShapeValue.get(),
558 0.5f
559 });
560
561 float lfoFreq2;
562 if (synth.lfoParams[1]->syncValue)
563 lfoFreq2 = 1.0f / ((synth.lfoParams[1]->beatValue.get()) / (synth.getCurrentTempo() / 60.0f));
564 else
565 lfoFreq2 = paramValue (synth.lfoParams[1]->rate);
566
567 lfo2.setParameters ({
568 lfoFreq2,
569 0,
570 0,
571 paramValue (synth.lfoParams[1]->depth) / 2,
572 (SimpleLFO::WaveShape) synth.lfoParams[1]->waveShapeValue.get(),
573 0.5f
574 });
575
576 // Amp
577 ampAdsr.setAnalog (synth.ampAnalogValue);
578
579 ampAdsr.setParameters ({
580 paramValue (synth.ampAttack),
581 paramValue (synth.ampDecay),
582 paramValue (synth.ampSustain) / 100.0f,
583 isQuickStop ? std::min (0.01f, paramValue (synth.ampRelease))
584 : paramValue (synth.ampRelease)
585 });
586
587 // Filter
588 filterAdsr.setParameters ({
589 paramValue (synth.filterAttack),
590 paramValue (synth.filterDecay),
591 paramValue (synth.filterSustain) / 100.0f,
592 paramValue (synth.filterRelease)
593 });
594
595 int type = synth.filterTypeValue;
596 float filterEnv = filterAdsr.getEnvelopeValue();
597 float filterSens = paramValue (synth.filterVelocity) / 100.0f;
598 filterSens = currentlyPlayingNote.noteOnVelocity.asUnsignedFloat() * filterSens + 1.0f - filterSens;
599 filterEnv *= filterSens;
600
601 for (int i = 0; i < numSamples; i++)
602 filterAdsr.getNextSample();
603
604 auto getMidiNoteInHertz = [](float noteNumber)
605 {
606 return 440.0f * std::pow (2.0f, (noteNumber - 69) / 12.0f);
607 };
608
609 float freqNote = paramValue (synth.filterFreq);
610 freqNote += (currentlyPlayingNote.initialNote - 60) * paramValue (synth.filterKey) / 100.0f;
611 freqNote += filterEnv * (paramValue (synth.filterAmount) * 137);
612
613 filterFrequencySmoother.setValue (freqNote / 135.076232f);
614 if (snapAllValues)
615 filterFrequencySmoother.snapToValue();
616
617 freqNote = filterFrequencySmoother.getCurrentValue() * 135.076232f;
618 filterFrequencySmoother.process (numSamples);
619
620 lastFilterFreq = juce::jlimit (8.0f,
621 std::min (20000.0f, float (currentSampleRate) / 2.0f),
622 getMidiNoteInHertz (freqNote));
623
624 float q = 0.70710678118655f / (1.0f - (paramValue (synth.filterResonance) / 100.0f) * 0.99f);
625
626 if (type != 0)
627 {
628 juce::IIRCoefficients coefs1, coefs2;
629
630 if (type == 1)
631 {
632 coefs1 = juce::IIRCoefficients::makeLowPass (currentSampleRate, lastFilterFreq, q);
633 coefs2 = juce::IIRCoefficients::makeLowPass (currentSampleRate, lastFilterFreq, 0.70710678118655f);
634 }
635 else if (type == 2)
636 {
637 coefs1 = juce::IIRCoefficients::makeHighPass (currentSampleRate, lastFilterFreq, q);
638 coefs2 = juce::IIRCoefficients::makeHighPass (currentSampleRate, lastFilterFreq, 0.70710678118655f);
639 }
640 else if (type == 3)
641 {
642 coefs1 = juce::IIRCoefficients::makeBandPass (currentSampleRate, lastFilterFreq, q);
643 coefs2 = juce::IIRCoefficients::makeBandPass (currentSampleRate, lastFilterFreq, 0.70710678118655f);
644 }
645 else if (type == 4)
646 {
647 coefs1 = juce::IIRCoefficients::makeNotchFilter (currentSampleRate, lastFilterFreq, q);
648 coefs2 = juce::IIRCoefficients::makeNotchFilter (currentSampleRate, lastFilterFreq, 0.70710678118655f);
649 }
650
651 filterL1.setCoefficients (coefs1);
652 filterR1.setCoefficients (coefs1);
653
654 filterL2.setCoefficients (coefs2);
655 filterR2.setCoefficients (coefs2);
656 }
657
658 // Oscillators
659 double activeNoteSmoothed = activeNote.getNextValue();
660 activeNote.skip (numSamples);
661
662 int idx = 0;
663 for (auto& o : oscillators)
664 {
665 double note = activeNoteSmoothed + currentlyPlayingNote.totalPitchbendInSemitones;
666 note += juce::roundToInt (paramValue (synth.oscParams[idx]->tune))
667 + paramValue (synth.oscParams[idx]->fineTune) / 100.0;
668
669 o.setNote (float (note));
670 o.setGain (juce::Decibels::decibelsToGain (paramValue (synth.oscParams[idx]->level)));
671 o.setWave ((Oscillator::Waves)(int (synth.oscParams[idx]->waveShapeValue.get())));
672 o.setPulseWidth (paramValue (synth.oscParams[idx]->pulseWidth));
673 o.setNumVoices (synth.oscParams[idx]->voicesValue);
674 o.setDetune (paramValue (synth.oscParams[idx]->detune));
675 o.setSpread (paramValue (synth.oscParams[idx]->spread) / 100.0f);
676 o.setPan (paramValue (synth.oscParams[idx]->pan));
677
678 idx++;
679 }
680
681 if (lastLegato != paramValue (synth.legato) && ! activeNote.isSmoothing())
682 {
683 lastLegato = paramValue (synth.legato);
684 activeNote.reset (currentSampleRate, lastLegato / 1000.0f);
685 }
686 }
687
688 void notePressureChanged() override {}
689 void notePitchbendChanged() override {}
690 void noteTimbreChanged() override {}
691 void noteKeyStateChanged() override {}
692
693private:
694 float paramValue (AutomatableParameter::Ptr param)
695 {
696 jassert (param != nullptr);
697 if (param == nullptr)
698 return 0.0f;
699
700 auto smoothItr = smoothers.find (param.get());
701 if (smoothItr == smoothers.end())
702 return param->getCurrentValue();
703
704 auto modItr = synth.modMatrix.find (param.get());
705 if (modItr == synth.modMatrix.end() || ! modItr->second.isModulated())
706 {
707 smoothItr->second.setValue (param->getCurrentNormalisedValue());
708
709 if (snapAllValues)
710 smoothItr->second.snapToValue();
711
712 return param->valueRange.convertFrom0to1 (smoothItr->second.getCurrentValue());
713 }
714 else
715 {
716 float val = param->getCurrentNormalisedValue();
717
718 auto& mod = modItr->second;
719
720 for (int i = mod.firstModIndex; i < juce::numElementsInArray (mod.depths) && i <= mod.lastModIndex; i++)
721 {
722 float d = mod.depths[i];
723
724 if (d > -1000.0f)
725 val += currentModValue[i] * d;
726 }
727
728 val = juce::jlimit (0.0f, 1.0f, val);
729
730 smoothItr->second.setValue (val);
731
732 if (snapAllValues)
733 smoothItr->second.snapToValue();
734
735 return param->valueRange.convertFrom0to1 (smoothItr->second.getCurrentValue());
736 }
737 }
738
739 FourOscPlugin& synth;
740
741 juce::AudioBuffer<float> renderBuffer {2, 512};
742 MultiVoiceOscillator oscillators[4];
743 ExpEnvelope ampAdsr;
744 LinEnvelope filterAdsr, modAdsr1, modAdsr2;
745 SimpleLFO lfo1, lfo2;
746 juce::IIRFilter filterL1, filterR1, filterL2, filterR2;
747
748 ValueSmoother<float> filterFrequencySmoother;
749
750 bool retrigger = false, isPlaying = false, isQuickStop = false, snapAllValues = false, firstBlock = false;
751 juce::LinearSmoothedValue<float> activeNote;
752 float lastLegato = -1.0f, lastFilterFreq = 0;
753
754 float currentModValue[FourOscPlugin::numModSources] = {0};
755
757};
758
759//==============================================================================
760FourOscPlugin::OscParams::OscParams (FourOscPlugin& plugin, int oscNum)
761{
762 auto um = plugin.getUndoManager();
763
764 auto oscID = [] (juce::Identifier i, int num)
765 {
766 return juce::Identifier (i.toString() + juce::String (num));
767 };
768
769 waveShapeValue.referTo (plugin.state, oscID (IDs::waveShape, oscNum), um, oscNum == 1 ? 1 : 0);
770 tuneValue.referTo (plugin.state, oscID (IDs::tune, oscNum), um, 0);
771 fineTuneValue.referTo (plugin.state, oscID (IDs::fineTune, oscNum), um, 0);
772 levelValue.referTo (plugin.state, oscID (IDs::level, oscNum), um, 0);
773 pulseWidthValue.referTo (plugin.state, oscID (IDs::pulseWidth, oscNum), um, 0.5);
774 voicesValue.referTo (plugin.state, oscID (IDs::voices, oscNum), um, 1);
775 detuneValue.referTo (plugin.state, oscID (IDs::detune, oscNum), um, 0);
776 spreadValue.referTo (plugin.state, oscID (IDs::spread, oscNum), um, 0);
777 panValue.referTo (plugin.state, oscID (IDs::pan, oscNum), um, 0);
778
779 auto paramID = [] (juce::Identifier i, int num)
780 {
781 return juce::Identifier (i.toString() + juce::String (num)).toString();
782 };
783
784 tune = plugin.addParam (paramID (IDs::tune, oscNum), TRANS("Tune") + " " + juce::String (oscNum), {-36.0f, 36.0f, 1.0f}, "st");
785 fineTune = plugin.addParam (paramID (IDs::fineTune, oscNum), TRANS("Fine Tune") + " " + juce::String (oscNum), {-100.0f, 100.0f});
786 level = plugin.addParam (paramID (IDs::level, oscNum), TRANS("Level") + " " + juce::String (oscNum), {-100.0f, 0.0f, 0.0f, 4.0f}, "dB");
787 pulseWidth = plugin.addParam (paramID (IDs::pulseWidth, oscNum), TRANS("Pulse Width") + " " + juce::String (oscNum), {0.01f, 0.99f});
788 detune = plugin.addParam (paramID (IDs::detune, oscNum), TRANS("Detune") + " " + juce::String (oscNum), {0.0f, 0.5f});
789 spread = plugin.addParam (paramID (IDs::spread, oscNum), TRANS("Spread") + " " + juce::String (oscNum), {-100.0f, 100.0f}, "%");
790 pan = plugin.addParam (paramID (IDs::pan, oscNum), TRANS("Pan") + " " + juce::String (oscNum), {-1.0f, 1.0f});
791}
792
793void FourOscPlugin::OscParams::attach()
794{
795 tune->attachToCurrentValue (tuneValue);
796 fineTune->attachToCurrentValue (fineTuneValue);
797 level->attachToCurrentValue (levelValue);
798 pulseWidth->attachToCurrentValue (pulseWidthValue);
799 detune->attachToCurrentValue (detuneValue);
800 spread->attachToCurrentValue (spreadValue);
801 pan->attachToCurrentValue (panValue);
802}
803
804void FourOscPlugin::OscParams::detach()
805{
806 tune->detachFromCurrentValue();
807 fineTune->detachFromCurrentValue();
808 level->detachFromCurrentValue();
809 pulseWidth->detachFromCurrentValue();
810 detune->detachFromCurrentValue();
811 spread->detachFromCurrentValue();
812 pan->detachFromCurrentValue();
813}
814
815//==============================================================================
816FourOscPlugin::LFOParams::LFOParams (FourOscPlugin& plugin, int lfoNum)
817{
818 auto um = plugin.getUndoManager();
819
820 auto lfoID = [] (juce::Identifier i, int num)
821 {
822 return juce::Identifier (i.toString() + juce::String (num));
823 };
824
825 waveShapeValue.referTo (plugin.state, lfoID (IDs::lfoWaveShape, lfoNum), um, lfoNum == 1 ? 1 : 0);
826 syncValue.referTo (plugin.state, lfoID (IDs::lfoSync, lfoNum), um, false);
827 rateValue.referTo (plugin.state, lfoID (IDs::lfoRate, lfoNum), um, 1);
828 depthValue.referTo (plugin.state, lfoID (IDs::lfoDepth, lfoNum), um, 1.0f);
829 beatValue.referTo (plugin.state, lfoID (IDs::lfoBeat, lfoNum), um, 1);
830
831 auto paramID = [] (juce::Identifier i, int num)
832 {
833 return juce::Identifier (i.toString() + juce::String (num)).toString();
834 };
835
836 rate = plugin.addParam (paramID (IDs::lfoRate, lfoNum), TRANS("Rate") + " " + juce::String (lfoNum), {0.0f, 500.0f, 0.0f, 0.3f}, "Hz");
837 depth = plugin.addParam (paramID (IDs::lfoDepth, lfoNum), TRANS("Depth") + " " + juce::String (lfoNum), {0.0f, 1.0f});
838}
839
840void FourOscPlugin::LFOParams::attach()
841{
842 depth->attachToCurrentValue (depthValue);
843 rate->attachToCurrentValue (rateValue);
844}
845
846void FourOscPlugin::LFOParams::detach()
847{
848 depth->detachFromCurrentValue();
849 rate->detachFromCurrentValue();
850}
851
852//==============================================================================
853FourOscPlugin::MODEnvParams::MODEnvParams (FourOscPlugin& plugin, int modNum)
854{
855 auto um = plugin.getUndoManager();
856
857 auto modID = [] (juce::Identifier i, int num)
858 {
859 return juce::Identifier (i.toString() + juce::String (num));
860 };
861
862 modAttackValue.referTo (plugin.state, modID (IDs::modAttack, modNum), um, 0.1f);
863 modDecayValue.referTo (plugin.state, modID (IDs::modDecay, modNum), um, 0.1f);
864 modSustainValue.referTo (plugin.state, modID (IDs::modSustain, modNum), um, 80.0f);
865 modReleaseValue.referTo (plugin.state, modID (IDs::modRelease, modNum), um, 0.1f);
866
867 auto paramID = [] (juce::Identifier i, int num)
868 {
869 return juce::Identifier (i.toString() + juce::String (num)).toString();
870 };
871
872 modAttack = plugin.addParam (paramID (IDs::modAttack, modNum), TRANS("Mod Attack") + " " + juce::String (modNum), {0.0f, 60.0f, 0.0f, 0.2f});
873 modDecay = plugin.addParam (paramID (IDs::modDecay, modNum), TRANS("Mod Decay") + " " + juce::String (modNum), {0.0f, 60.0f, 0.0f, 0.2f});
874 modSustain = plugin.addParam (paramID (IDs::modSustain, modNum), TRANS("Mod Sustain") + " " + juce::String (modNum), {0.0f, 100.0f}, "%");
875 modRelease = plugin.addParam (paramID (IDs::modRelease, modNum), TRANS("Mod Release") + " " + juce::String (modNum), {0.001f, 60.0f, 0.0f, 0.2f});
876}
877
878void FourOscPlugin::MODEnvParams::attach()
879{
880 modAttack->attachToCurrentValue (modAttackValue);
881 modDecay->attachToCurrentValue (modDecayValue);
882 modSustain->attachToCurrentValue (modSustainValue);
883 modRelease->attachToCurrentValue (modReleaseValue);
884}
885
886void FourOscPlugin::MODEnvParams::detach()
887{
888 modAttack->detachFromCurrentValue();
889 modDecay->detachFromCurrentValue();
890 modSustain->detachFromCurrentValue();
891 modRelease->detachFromCurrentValue();
892}
893
894//==============================================================================
895FourOscPlugin::FourOscPlugin (PluginCreationInfo info) : Plugin (info)
896{
897 auto um = getUndoManager();
898
899 levelMeasurer.addClient (*this);
900
901 instrument.enableLegacyMode();
902 setPitchbendTrackingMode (juce::MPEInstrument::allNotesOnChannel);
903
904 setVoiceStealingEnabled (true);
905
908
909 for (int i = 0; i < 4; i++) oscParams.add (new OscParams (*this, i + 1));
910 for (int i = 0; i < 2; i++) lfoParams.add (new LFOParams (*this, i + 1));
911 for (int i = 0; i < 2; i++) modEnvParams.add (new MODEnvParams (*this, i + 1));
912
913 // Amp
914 ampAttackValue.referTo (state, IDs::ampAttack, um, 0.1f);
915 ampDecayValue.referTo (state, IDs::ampDecay, um, 0.1f);
916 ampSustainValue.referTo (state, IDs::ampSustain, um, 80.0f);
917 ampReleaseValue.referTo (state, IDs::ampRelease, um, 0.1f);
918 ampVelocityValue.referTo (state, IDs::ampVelocity, um, 100.0f);
919 ampAnalogValue.referTo (state, IDs::ampAnalog, um, true);
920
921 ampAttack = addParam ("ampAttack", TRANS("Amp Attack"), {0.001f, 60.0f, 0.0f, 0.2f});
922 ampDecay = addParam ("ampDecay", TRANS("Amp Decay"), {0.001f, 60.0f, 0.0f, 0.2f});
923 ampSustain = addParam ("ampSustain", TRANS("Amp Sustain"), {0.0f, 100.0f}, "%");
924 ampRelease = addParam ("ampRelease", TRANS("Amp Release"), {0.001f, 60.0f, 0.0f, 0.2f});
925 ampVelocity = addParam ("ampVelocity", TRANS("Amp Velocity"), {0.0f, 100.0f}, "%");
926
927 ampAttack->attachToCurrentValue (ampAttackValue);
928 ampDecay->attachToCurrentValue (ampDecayValue);
929 ampSustain->attachToCurrentValue (ampSustainValue);
930 ampRelease->attachToCurrentValue (ampReleaseValue);
931 ampVelocity->attachToCurrentValue (ampVelocityValue);
932
933 // Filter
934 filterAttackValue.referTo (state, IDs::filterAttack, um, 0.1f);
935 filterDecayValue.referTo (state, IDs::filterDecay, um, 0.1f);
936 filterSustainValue.referTo (state, IDs::filterSustain, um, 80.0f);
937 filterReleaseValue.referTo (state, IDs::filterRelease, um, 0.1f);
938 filterFreqValue.referTo (state, IDs::filterFreq, um, 69.0f);
939 filterResonanceValue.referTo (state, IDs::filterResonance, um, 0.5f);
940 filterAmountValue.referTo (state, IDs::filterAmount, um, 0.0f);
941 filterKeyValue.referTo (state, IDs::filterKey, um, 0.0f);
942 filterVelocityValue.referTo (state, IDs::filterVelocity, um, 0.0f);
943 filterTypeValue.referTo (state, IDs::filterType, um, 0);
944 filterSlopeValue.referTo (state, IDs::filterSlope, um, 12);
945
946 filterAttack = addParam ("filterAttack", TRANS("Filter Attack"), {0.0f, 60.0f, 0.0f, 0.2f});
947 filterDecay = addParam ("filterDecay", TRANS("Filter Decay"), {0.0f, 60.0f, 0.0f, 0.2f});
948 filterSustain = addParam ("filterSustain", TRANS("Filter Sustain"), {0.0f, 100.0f}, "%");
949 filterRelease = addParam ("filterRelease", TRANS("Filter Release"), {0.0f, 60.0f, 0.0f, 0.2f});
950 filterFreq = addParam ("filterFreq", TRANS("Filter Freq"), {0.0f, 135.076232f});
951 filterResonance = addParam ("filterResonance", TRANS("Filter Resonance"), {0.0f, 100.0f}, "%");
952 filterAmount = addParam ("filterAmount", TRANS("Filter Amount"), {-1.0f, 1.0f});
953 filterKey = addParam ("filterKey", TRANS("Filter Key"), {0.0f, 100.0f}, "%");
954 filterVelocity = addParam ("filterVelocity", TRANS("Filter Velocity"), {0.0f, 100.0f}, "%");
955
956 filterAttack->attachToCurrentValue (filterAttackValue);
957 filterDecay->attachToCurrentValue (filterDecayValue);
958 filterSustain->attachToCurrentValue (filterSustainValue);
959 filterRelease->attachToCurrentValue (filterReleaseValue);
960 filterFreq->attachToCurrentValue (filterFreqValue);
961 filterResonance->attachToCurrentValue (filterResonanceValue);
962 filterAmount->attachToCurrentValue (filterAmountValue);
963 filterKey->attachToCurrentValue (filterKeyValue);
964 filterVelocity->attachToCurrentValue (filterVelocityValue);
965
966 // Build the mod matrix before we add any global params
967 for (auto p : getAutomatableParameters())
968 modMatrix[p] = ModAssign();
969
970 // Effects: Distortion
971 distortionOnValue.referTo (state, IDs::distortionOn, um);
972 distortionValue.referTo (state, IDs::distortion, um, 0.0f);
973
974 distortion = addParam ("distortion", TRANS("Distortion"), {0.0f, 1.0f});
975
976 distortion->attachToCurrentValue (distortionValue);
977
978 // Effects: Reverb
979 reverbOnValue.referTo (state, IDs::reverbOn, um);
980 reverbSizeValue.referTo (state, IDs::reverbSize, um, 0.0f);
981 reverbDampingValue.referTo (state, IDs::reverbDamping, um, 0.0f);
982 reverbWidthValue.referTo (state, IDs::reverbWidth, um, 0.0f);
983 reverbMixValue.referTo (state, IDs::reverbMix, um, 0.0);
984
985 reverbSize = addParam ("reverbSize", TRANS("Size"), {0.0f, 1.0f});
986 reverbDamping = addParam ("reverbDamping", TRANS("Damping"), {0.0f, 1.0f});
987 reverbWidth = addParam ("reverbWidth", TRANS("Width"), {0.0f, 1.0f});
988 reverbMix = addParam ("reverbMix", TRANS("Mix"), {0.0f, 1.0f});
989
990 reverbSize->attachToCurrentValue (reverbSizeValue);
991 reverbDamping->attachToCurrentValue (reverbDampingValue);
992 reverbWidth->attachToCurrentValue (reverbWidthValue);
993 reverbMix->attachToCurrentValue (reverbMixValue);
994
995 // Effects: Delay
996 delayOnValue.referTo (state, IDs::delayOn, um);
997 delayFeedbackValue.referTo (state, IDs::delayFeedback, um, -10.0f);
998 delayCrossfeedValue.referTo (state, IDs::delayCrossfeed, um, -100.0f);
999 delayMixValue.referTo (state, IDs::delayMix, um, 0.0f);
1000 delayValue.referTo (state, IDs::delay, um, 1.0f);
1001
1002 delayFeedback = addParam ("delayFeedback", TRANS("Feedback"), {-100.0f, 0.0f, 0.0f, 4.0f}, "dB");
1003 delayCrossfeed = addParam ("delayCrossfeed", TRANS("Crossfeed"), {-100.0f, 0.0f, 0.0f, 4.0f}, "dB");
1004 delayMix = addParam ("delayMix", TRANS("Mix"), {0.0f, 1.0f});
1005
1006 delayFeedback->attachToCurrentValue (delayFeedbackValue);
1007 delayCrossfeed->attachToCurrentValue (delayCrossfeedValue);
1008 delayMix->attachToCurrentValue (delayMixValue);
1009
1010 // Effects: Chorus
1011 chorusOnValue.referTo (state, IDs::chorusOn, um);
1012 chorusSpeedValue.referTo (state, IDs::chosusSpeed, um, 1.0f);
1013 chorusDepthValue.referTo (state, IDs::chorusDepth, um, 3.0f);
1014 chorusWidthValue.referTo (state, IDs::chrousWidth, um, 0.5f);
1015 chorusMixValue.referTo (state, IDs::chorusMix, um, 0.0f);
1016
1017 chorusSpeed = addParam ("chorusSpeed", TRANS("Speed"), {0.1f, 10.0f}, "Hz");
1018 chorusDepth = addParam ("chorusDepth", TRANS("Depth"), {0.1f, 20.0f}, "ms");
1019 chorusWidth = addParam ("chorusWidth", TRANS("Width"), {0.0f, 1.0f});
1020 chorusMix = addParam ("chorusMix", TRANS("Mix"), {0.0f, 1.0f});
1021
1022 chorusSpeed->attachToCurrentValue (chorusSpeedValue);
1023 chorusDepth->attachToCurrentValue (chorusDepthValue);
1024 chorusWidth->attachToCurrentValue (chorusWidthValue);
1025 chorusMix->attachToCurrentValue (chorusMixValue);
1026
1027 // Master
1028 voiceModeValue.referTo (state, IDs::voiceMode, um, 2);
1029 voicesValue.referTo (state, IDs::voices, um, 32);
1030 legatoValue.referTo (state, IDs::legato, um, 0);
1031 masterLevelValue.referTo (state, IDs::masterLevel, um, 0);
1032
1033 legato = addParam ("legato", TRANS("Legato"), {0.0f, 500.0f}, "ms");
1034 masterLevel = addParam ("masterLevel", TRANS("Level"), {-100.0f, 0.0f, 0.0f, 4.0f});
1035
1036 legato->attachToCurrentValue (legatoValue);
1037 masterLevel->attachToCurrentValue (masterLevelValue);
1038
1039 // Oscillators
1040 for (auto o : oscParams)
1041 o->attach();
1042
1043 // Mod
1044 for (auto l : lfoParams)
1045 l->attach();
1046
1047 for (auto e : modEnvParams)
1048 e->attach();
1049
1050 for (auto p : getAutomatableParameters())
1051 smoothers[p] = {};
1052
1053 // Setup text functions
1054 setupTextFunctions();
1055
1056 valueTreePropertyChanged (state, IDs::voiceMode);
1057 valueTreePropertyChanged (state, IDs::mpe);
1058
1059 loadModMatrix();
1060}
1061
1062FourOscPlugin::~FourOscPlugin()
1063{
1064 notifyListenersOfDeletion();
1065
1066 // Oscillators
1067 for (auto o : oscParams)
1068 o->detach();
1069
1070 // Mod
1071 for (auto l : lfoParams)
1072 l->detach();
1073
1074 for (auto e : modEnvParams)
1075 e->detach();
1076
1077 // Amp
1078 ampAttack->detachFromCurrentValue();
1079 ampDecay->detachFromCurrentValue();
1080 ampSustain->detachFromCurrentValue();
1081 ampRelease->detachFromCurrentValue();
1082 ampVelocity->detachFromCurrentValue();
1083
1084 // Filter
1085 filterAttack->detachFromCurrentValue();
1086 filterDecay->detachFromCurrentValue();
1087 filterSustain->detachFromCurrentValue();
1088 filterRelease->detachFromCurrentValue();
1089 filterFreq->detachFromCurrentValue();
1090 filterResonance->detachFromCurrentValue();
1091 filterAmount->detachFromCurrentValue();
1092 filterKey->detachFromCurrentValue();
1093 filterVelocity->detachFromCurrentValue();
1094
1095 // Effects: Distortion
1096 distortion->detachFromCurrentValue();
1097
1098 // Effects: Reverb
1099 reverbSize->detachFromCurrentValue();
1100 reverbDamping->detachFromCurrentValue();
1101 reverbWidth->detachFromCurrentValue();
1102 reverbMix->detachFromCurrentValue();
1103
1104 // Effects: Delay
1105 delayFeedback->detachFromCurrentValue();
1106 delayCrossfeed->detachFromCurrentValue();
1107 delayMix->detachFromCurrentValue();
1108
1109 // Effects: Chorus
1110 chorusSpeed->detachFromCurrentValue();
1111 chorusDepth->detachFromCurrentValue();
1112 chorusWidth->detachFromCurrentValue();
1113 chorusMix->detachFromCurrentValue();
1114
1115 // Voices
1116 legato->detachFromCurrentValue();
1117 masterLevel->detachFromCurrentValue();
1118}
1119
1120const char* FourOscPlugin::xmlTypeName = "4osc";
1121
1122AutomatableParameter* FourOscPlugin::addParam (const juce::String& paramID, const juce::String& name, juce::NormalisableRange<float> valueRange, juce::String label)
1123{
1124 auto p = Plugin::addParam (paramID, name, valueRange);
1125
1126 if (label.isNotEmpty())
1127 labels[paramID] = label;
1128
1129 return p;
1130}
1131
1132void FourOscPlugin::setupTextFunctions()
1133{
1134 // Add a default function that does number of decimal places nicely and adds a labels
1135 for (auto p : getAutomatableParameters())
1136 {
1137 juce::String label;
1138 auto itr = labels.find (p->paramID);
1139 if (itr != labels.end())
1140 label = itr->second;
1141
1142 auto basicValueToTextFunction = [label] (float value)
1143 {
1144 juce::String text;
1145 float v = std::abs (value);
1146 if (v > 100)
1147 text = juce::String (juce::roundToInt (value));
1148 else if (v > 10)
1149 text = juce::String (value, 1);
1150 else if (v > 1)
1151 text = juce::String (value, 2);
1152 else
1153 text = juce::String (value, 3);
1154
1155 if (label.isNotEmpty())
1156 text += label;
1157
1158 return text;
1159 };
1160
1161 p->valueToStringFunction = basicValueToTextFunction;
1162 }
1163
1164 auto timeValueToTextFunction = [] (float value)
1165 {
1166 if (value < 1.0f)
1167 return juce::String (juce::roundToInt (value * 1000)) + "ms";
1168 return juce::String (value, 2) + "s";
1169 };
1170
1171 auto panValueToTextFunction = [] (float value)
1172 {
1173 if (value < 0.0f)
1174 return juce::String (juce::roundToInt (-value * 100)) + "L";
1175 return juce::String (juce::roundToInt (value * 100)) + "R";
1176 };
1177
1178 auto percentValueToTextFunction = [] (float value)
1179 {
1180 return juce::String (juce::roundToInt (value * 100)) + "%";
1181 };
1182
1183 auto tuneValueToTextFunction = [] (float value)
1184 {
1185 return juce::String (juce::roundToInt (value)) + "st";
1186 };
1187
1188 auto freqValueToTextFunction = [] (float value)
1189 {
1190 float freq = 440.0f * std::pow (2.0f, (value - 69) / 12.0f);
1191 return juce::String (juce::roundToInt (freq)) + "Hz";
1192 };
1193
1194 auto textToFreqValueFunction = [] (juce::String text)
1195 {
1196 float freq = text.getFloatValue();
1197 return 12.0f * std::log2 (freq / 440.0f) + 69.0f;
1198 };
1199
1200 auto textToTimeValueFunction = [] (juce::String text)
1201 {
1202 float time = text.getFloatValue();
1203 return (text.contains ("ms") || time > 10.0f) ? (time / 1000.0f) : time;
1204 };
1205
1206 for (auto p : oscParams)
1207 {
1208 p->pulseWidth->valueToStringFunction = percentValueToTextFunction;
1209 p->tune->valueToStringFunction = tuneValueToTextFunction;
1210 p->detune->valueToStringFunction = percentValueToTextFunction;
1211 p->pan->valueToStringFunction = panValueToTextFunction;
1212 }
1213
1214 for (auto p : lfoParams)
1215 {
1216 p->depth->valueToStringFunction = percentValueToTextFunction;
1217 }
1218
1219 for (auto p : modEnvParams)
1220 {
1221 p->modAttack->valueToStringFunction = timeValueToTextFunction;
1222 p->modAttack->stringToValueFunction = textToTimeValueFunction;
1223 p->modDecay->valueToStringFunction = timeValueToTextFunction;
1224 p->modDecay->stringToValueFunction = textToTimeValueFunction;
1225 p->modRelease->valueToStringFunction = timeValueToTextFunction;
1226 p->modRelease->stringToValueFunction = textToTimeValueFunction;
1227 }
1228
1229 ampAttack->valueToStringFunction = timeValueToTextFunction;
1230 ampAttack->stringToValueFunction = textToTimeValueFunction;
1231 ampDecay->valueToStringFunction = timeValueToTextFunction;
1232 ampDecay->stringToValueFunction = textToTimeValueFunction;
1233 ampRelease->valueToStringFunction = timeValueToTextFunction;
1234 ampRelease->stringToValueFunction = textToTimeValueFunction;
1235
1236 filterAttack->valueToStringFunction = timeValueToTextFunction;
1237 filterAttack->stringToValueFunction = textToTimeValueFunction;
1238 filterDecay->valueToStringFunction = timeValueToTextFunction;
1239 filterDecay->stringToValueFunction = textToTimeValueFunction;
1240 filterRelease->valueToStringFunction = timeValueToTextFunction;
1241 filterRelease->stringToValueFunction = textToTimeValueFunction;
1242 filterFreq->valueToStringFunction = freqValueToTextFunction;
1243 filterFreq->stringToValueFunction = textToFreqValueFunction;
1244 filterAmount->valueToStringFunction = percentValueToTextFunction;
1245
1246 distortion->valueToStringFunction = percentValueToTextFunction;
1247
1248 delayMix->valueToStringFunction = percentValueToTextFunction;
1249
1250 chorusWidth->valueToStringFunction = percentValueToTextFunction;
1251 chorusMix->valueToStringFunction = percentValueToTextFunction;
1252
1253 reverbSize->valueToStringFunction = percentValueToTextFunction;
1254 reverbWidth->valueToStringFunction = percentValueToTextFunction;
1255 reverbDamping->valueToStringFunction = percentValueToTextFunction;
1256 reverbMix->valueToStringFunction = percentValueToTextFunction;
1257}
1258
1259float FourOscPlugin::getLevel (int channel)
1260{
1261 auto& peak = levels[channel];
1262
1263 auto elapsedMilliseconds = std::max (0, int (juce::Time::getApproximateMillisecondCounter() - peak.time) - 50);
1264 float currentLevel = peak.dB - (48.0f * elapsedMilliseconds / 1000.0f);
1265
1266 auto latest = getAndClearAudioLevel (channel);
1267
1268 if (latest.dB > currentLevel)
1269 {
1270 peak = latest;
1271 return juce::jlimit (-100.0f, 0.0f, peak.dB);
1272 }
1273
1274 return juce::jlimit (-100.0f, 0.0f, currentLevel);
1275}
1276
1277void FourOscPlugin::valueTreeChanged()
1278{
1279 Plugin::valueTreeChanged();
1280}
1281
1282void FourOscPlugin::valueTreePropertyChanged (juce::ValueTree& v, const juce::Identifier& i)
1283{
1284 Plugin::valueTreePropertyChanged (v, i);
1285
1286 if (v.hasType (IDs::PLUGIN))
1287 {
1288 if (i == IDs::voiceMode
1289 || i == IDs::voices)
1290 {
1291 juce::ScopedLock sl (voicesLock);
1292 if (voiceModeValue == 2)
1293 {
1294 reduceNumVoices (voicesValue.get());
1295 while (getNumVoices() < voicesValue.get())
1296 addVoice (new FourOscVoice (*this));
1297 }
1298 else
1299 {
1300 while (getNumVoices() < 1)
1301 addVoice (new FourOscVoice (*this));
1302
1303 reduceNumVoices (1);
1304 }
1305 }
1306 else if (i == IDs::mpe)
1307 {
1308 if ((bool) state[IDs::mpe])
1309 {
1310 juce::MPEZoneLayout zones;
1311 zones.setLowerZone (15);
1312 instrument.setZoneLayout (zones);
1313 setPitchbendTrackingMode (juce::MPEInstrument::lastNotePlayedOnChannel);
1314 }
1315 else
1316 {
1317 instrument.enableLegacyMode();
1318 setPitchbendTrackingMode (juce::MPEInstrument::allNotesOnChannel);
1319 }
1320 }
1321 }
1322 else if (v.hasType (IDs::MODMATRIX) || v.hasType (IDs::MODMATRIXITEM))
1323 {
1324 if (! flushingState)
1326 }
1327}
1328
1329void FourOscPlugin::valueTreeChildAdded (juce::ValueTree& v, juce::ValueTree& c)
1330{
1331 Plugin::valueTreeChildAdded (v, c);
1332
1333 if (c.hasType (IDs::MODMATRIX) || c.hasType (IDs::MODMATRIXITEM))
1334 if (! flushingState)
1336}
1337
1338void FourOscPlugin::valueTreeChildRemoved (juce::ValueTree& v, juce::ValueTree& c, int i)
1339{
1340 Plugin::valueTreeChildRemoved (v, c, i);
1341
1342 if (c.hasType (IDs::MODMATRIX) || c.hasType (IDs::MODMATRIXITEM))
1343 if (! flushingState)
1345}
1346
1347void FourOscPlugin::handleController (int, int controllerNumber, int controllerValue)
1348{
1349 controllerValues[controllerNumber] = controllerValue / 127.0f;
1350}
1351
1352void FourOscPlugin::handleAsyncUpdate()
1353{
1354 loadModMatrix();
1355}
1356
1357void FourOscPlugin::loadModMatrix()
1358{
1359 // Disable all modulation
1360 for (auto& itr : modMatrix)
1361 for (int s = lfo1; s < numModSources; s++)
1362 itr.second.depths[s] = -1000.0f;
1363
1364 // Read modulation from state ValueTree
1365 auto mm = state.getChildWithName (IDs::MODMATRIX);
1366 if (! mm.isValid())
1367 return;
1368
1369 for (auto mmi : mm)
1370 {
1371 auto paramId = mmi.getProperty (IDs::modParam).toString();
1372 auto src = idToModulationSource (mmi.getProperty (IDs::modItem).toString());
1373 float depth = (float) mmi.getProperty (IDs::modDepth);
1374
1375 if (src != none)
1376 {
1377 if (auto p = getAutomatableParameterByID (paramId))
1378 {
1379 auto itr = modMatrix.find (p.get());
1380 if (itr != modMatrix.end())
1381 itr->second.depths[src] = depth;
1382 else
1384 }
1385 }
1386 }
1387
1388 // Update cached lookup info
1389 for (auto& itr : modMatrix)
1390 itr.second.updateCachedInfo();
1391}
1392
1393void FourOscPlugin::flushPluginStateToValueTree()
1394{
1395 juce::ScopedValueSetter<bool> svs (flushingState, true);
1396
1397 auto um = getUndoManager();
1398
1399 auto vt = state.getChildWithName (IDs::MODMATRIX);
1400 if (vt.isValid())
1401 state.removeChild (vt, um);
1402
1403 auto mm = juce::ValueTree (IDs::MODMATRIX);
1404
1405 for (const auto& itr : modMatrix)
1406 {
1407 for (int s = lfo1; s < numModSources; s++)
1408 {
1409 if (itr.second.depths[s] >= -1.0f)
1410 {
1411 auto mmi = juce::ValueTree (IDs::MODMATRIXITEM);
1412 mmi.setProperty (IDs::modParam, itr.first->paramID, um);
1413 mmi.setProperty (IDs::modItem, modulationSourceToID ((ModSource)s), um);
1414 mmi.setProperty (IDs::modDepth, itr.second.depths[s], um);
1415
1416 mm.addChild (mmi, -1, um);
1417 }
1418 }
1419 }
1420
1421 state.addChild (mm, -1, um);
1422
1423 Plugin::flushPluginStateToValueTree(); // Add any parameter values that are being modified
1424}
1425
1426void FourOscPlugin::initialise (const PluginInitialisationInfo& info)
1427{
1428 setCurrentPlaybackSampleRate (info.sampleRate);
1429
1430 reverb.setSampleRate (info.sampleRate);
1431 delay->setSampleRate (info.sampleRate);
1432 chorus->setSampleRate (info.sampleRate);
1433
1434 reverb.reset();
1435 delay->reset();
1436 chorus->reset();
1437
1438 for (auto& itr : smoothers)
1439 itr.second.reset (info.sampleRate, 0.01f);
1440}
1441
1445
1446//==============================================================================
1448{
1449 turnOffAllVoices (false);
1450}
1451
1453{
1454 juce::ScopedLock sl (voicesLock);
1455
1456 if (fc.destBuffer != nullptr)
1457 {
1458 SCOPED_REALTIME_CHECK
1459
1460 // find the tempo
1461 currentPos.set (fc.editTime.getStart());
1462 currentTempo = float (currentPos.getTempo());
1463
1464 // Handle all notes off first
1465 if (fc.bufferForMidiMessages != nullptr)
1466 if (fc.bufferForMidiMessages->isAllNotesOff)
1467 turnOffAllVoices (true);
1468
1469 // Chop the buffer in 32 sample blocks so modulation is smooth
1470 int todo = fc.bufferNumSamples;
1471 int pos = fc.bufferStartSample;
1472
1473 while (todo > 0)
1474 {
1475 int thisBlock = std::min (32, todo);
1476
1477 AudioScratchBuffer workBuffer (2, thisBlock);
1478 workBuffer.buffer.clear();
1479
1480 juce::MidiBuffer midi;
1481 if (fc.bufferForMidiMessages != nullptr)
1482 {
1483 for (auto m : *fc.bufferForMidiMessages)
1484 {
1485 int midiPos = int (m.getTimeStamp());
1486 if (midiPos >= pos && midiPos < pos + thisBlock)
1487 midi.addEvent (m, midiPos - pos);
1488 }
1489 }
1490
1491 applyToBuffer (workBuffer.buffer, midi);
1492
1493 if (fc.destBuffer->getNumChannels() == 1)
1494 {
1495 fc.destBuffer->copyFrom (0, pos, workBuffer.buffer, 0, 0, thisBlock);
1496 }
1497 else
1498 {
1499 fc.destBuffer->copyFrom (0, pos, workBuffer.buffer, 0, 0, thisBlock);
1500 fc.destBuffer->copyFrom (1, pos, workBuffer.buffer, 1, 0, thisBlock);
1501 }
1502
1503 levelMeasurer.processBuffer (workBuffer.buffer, 0, thisBlock);
1504
1505 todo -= thisBlock;
1506 pos += thisBlock;
1507 }
1508
1509 for (int ch = 2; ch < fc.destBuffer->getNumChannels(); ch++)
1511 }
1512}
1513
1515{
1516 updateParams (buffer);
1517 renderNextBlock (buffer, midi, 0, buffer.getNumSamples());
1518 applyEffects (buffer);
1519
1520 for (auto& itr : smoothers)
1521 itr.second.process (buffer.getNumSamples());
1522}
1523
1524void FourOscPlugin::applyEffects (juce::AudioBuffer<float>& buffer)
1525{
1526 int numSamples = buffer.getNumSamples();
1527
1528 // Apply Distortion
1529 if (distortionOnValue)
1530 {
1531 float drive = paramValue (distortion);
1532 float clip = 1.0f / (2.0f * drive);
1533 Distortion::distortion (buffer.getWritePointer (0), numSamples, drive, -clip, clip);
1534 Distortion::distortion (buffer.getWritePointer (1), numSamples, drive, -clip, clip);
1535 }
1536
1537 // Apply Chorus
1538 if (chorusOnValue)
1539 chorus->process (buffer, numSamples);
1540
1541 // Apply Delay
1542 if (delayOnValue)
1543 delay->process (buffer, numSamples);
1544
1545 // Apply Reverb
1546 if (reverbOnValue)
1547 reverb.processStereo (buffer.getWritePointer (0), buffer.getWritePointer (1), numSamples);
1548
1549 // Apply master level
1550 buffer.applyGain (juce::Decibels::decibelsToGain (paramValue (masterLevel)));
1551}
1552
1553void FourOscPlugin::updateParams (juce::AudioBuffer<float>& buffer)
1554{
1555 ignoreUnused (buffer);
1556
1557 // Reverb
1558 AudioFadeCurve::CrossfadeLevels wetDry (paramValue (reverbMix));
1559
1561 params.roomSize = paramValue (reverbSize);
1562 params.damping = paramValue (reverbDamping);
1563 params.width = paramValue (reverbWidth);
1564 params.wetLevel = wetDry.gain1;
1565 params.dryLevel = wetDry.gain2;
1566 params.freezeMode = 0;
1567
1568 reverb.setParameters (params);
1569
1570 // Delay
1571 float delayTime = (delayValue.get()) / (currentTempo / 60.0f);
1572 delay->setParams (delayTime,
1573 juce::Decibels::decibelsToGain (paramValue (delayFeedback)),
1574 juce::Decibels::decibelsToGain (paramValue (delayCrossfeed)),
1575 paramValue (delayMix));
1576
1577 // Chorus
1578 chorus->setParams (paramValue (chorusSpeed),
1579 paramValue (chorusDepth),
1580 paramValue (chorusWidth),
1581 paramValue (chorusMix));
1582}
1583
1584//==============================================================================
1585void FourOscPlugin::restorePluginStateFromValueTree (const juce::ValueTree& v)
1586{
1587 copyPropertiesToCachedValues (v, ampAttackValue, ampDecayValue, ampSustainValue, ampReleaseValue, ampVelocityValue, filterAttackValue,
1588 filterDecayValue, filterSustainValue, filterReleaseValue, filterFreqValue, filterResonanceValue,
1589 filterAmountValue, filterKeyValue, filterVelocityValue, distortionValue, reverbSizeValue,
1590 reverbDampingValue, reverbWidthValue, reverbMixValue, delayValue, delayFeedbackValue, delayCrossfeedValue,
1591 delayMixValue, chorusSpeedValue, chorusDepthValue, chorusWidthValue, chorusMixValue, legatoValue,
1592 masterLevelValue, voiceModeValue, voicesValue, filterTypeValue, filterSlopeValue,
1593 ampAnalogValue, distortionOnValue, reverbOnValue, delayOnValue, chorusOnValue);
1594
1595 auto um = getUndoManager();
1596
1597 for (auto p : oscParams)
1598 p->restorePluginStateFromValueTree (v);
1599
1600 for (auto p : lfoParams)
1601 p->restorePluginStateFromValueTree (v);
1602
1603 for (auto p : modEnvParams)
1604 p->restorePluginStateFromValueTree (v);
1605
1606 auto mm = state.getChildWithName (IDs::MODMATRIX);
1607 if (mm.isValid())
1608 state.removeChild (mm, um);
1609
1610 mm = v.getChildWithName (IDs::MODMATRIX);
1611
1612 if (mm.isValid())
1613 state.addChild (mm.createCopy(), -1, um);
1614
1615 valueTreePropertyChanged (state, IDs::voiceMode);
1616
1617 for (auto p : getAutomatableParameters())
1618 p->updateFromAttachedValue();
1619}
1620
1621juce::String FourOscPlugin::modulationSourceToName (ModSource src)
1622{
1623 switch (src)
1624 {
1625 case lfo1: return TRANS("LFO 1");
1626 case lfo2: return TRANS("LFO 2");
1627 case env1: return TRANS("Envelope 1");
1628 case env2: return TRANS("Envelope 2");
1629 case mpePressure: return TRANS("MPE Pressure");
1630 case mpeTimbre: return TRANS("MPE Timbre");
1631 case midiNoteNum: return TRANS("MIDI Note Number");
1632 case midiVelocity: return TRANS("MIDI Velocity");
1633 case none:
1634 case ccBankSelect:
1635 case ccPolyMode:
1636 case numModSources:
1637 default:
1638 {
1639 if (src >= ccBankSelect && src <= ccPolyMode)
1640 {
1641 auto prefix = juce::String ("CC#") + juce::String ((int)(src - ccBankSelect));
1642 auto name = juce::String (juce::MidiMessage::getControllerName (src - ccBankSelect));
1643
1644 if (name.isEmpty())
1645 return prefix;
1646
1647 return prefix + " " + name;
1648 }
1649
1651 return {};
1652 }
1653 }
1654}
1655
1656juce::String FourOscPlugin::modulationSourceToID (FourOscPlugin::ModSource src)
1657{
1658 switch (src)
1659 {
1660 case lfo1: return "lfo1";
1661 case lfo2: return "lfo2";
1662 case env1: return "env1";
1663 case env2: return "env2";
1664 case mpePressure: return "mpePressure";
1665 case mpeTimbre: return "mpeTimbre";
1666 case midiNoteNum: return "midiNote";
1667 case midiVelocity: return "midiVelocity";
1668 case none:
1669 case ccBankSelect:
1670 case ccPolyMode:
1671 case numModSources:
1672 default:
1673 {
1674 if (src >= ccBankSelect && src <= ccPolyMode)
1675 return "cc" + juce::String (int (src - ccBankSelect));
1676
1678 return {};
1679 }
1680 }
1681}
1682
1683FourOscPlugin::ModSource FourOscPlugin::idToModulationSource (juce::String idStr)
1684{
1685 if (idStr == "lfo1") return lfo1;
1686 if (idStr == "lfo2") return lfo2;
1687 if (idStr == "env1") return env1;
1688 if (idStr == "env2") return env2;
1689 if (idStr == "mpePressure") return mpePressure;
1690 if (idStr == "mpeTimbre") return mpeTimbre;
1691 if (idStr == "midiNote") return midiNoteNum;
1692 if (idStr == "midiVelocity") return midiVelocity;
1693
1694 if (idStr.startsWith ("cc"))
1695 return ModSource (ccBankSelect + idStr.getTrailingIntValue());
1696
1697 return none;
1698}
1699
1700juce::Array<float> FourOscPlugin::getLiveModulationPositions (AutomatableParameter::Ptr param)
1701{
1702 juce::Array<float> positions;
1703
1704 // Filter frequency is a special case, not only do we want to show modulation,
1705 // also want to show effect of key tracking and filter envelope
1706 if (param->paramID == "filterFreq" && isModulated (param))
1707 {
1708 for (int i = 0; i < getNumVoices(); i++)
1709 if (auto fov = dynamic_cast<FourOscVoice*> (getVoice (i)))
1710 fov->getLiveFilterFrequency (positions);
1711 }
1712 else if (isModulated (param))
1713 {
1714 for (int i = 0; i < getNumVoices(); i++)
1715 if (auto fov = dynamic_cast<FourOscVoice*> (getVoice (i)))
1716 fov->getLiveModulationPositions (param, positions);
1717 }
1718 return positions;
1719}
1720
1721bool FourOscPlugin::isModulated (AutomatableParameter::Ptr param)
1722{
1723 if (param->paramID == "filterFreq" && (filterKeyValue.get() != 0 || filterAmountValue.get() != 0 ))
1724 return true;
1725
1726 auto itr = modMatrix.find (param.get());
1727 if (itr != modMatrix.end())
1728 {
1729 for (auto d : itr->second.depths)
1730 if (d >= -1.0f)
1731 return true;
1732
1733 return false;
1734 }
1736 return false;
1737}
1738
1739juce::Array<FourOscPlugin::ModSource> FourOscPlugin::getModulationSources (AutomatableParameter::Ptr param)
1740{
1741 auto itr = modMatrix.find (param.get());
1742 if (itr != modMatrix.end())
1743 {
1745 for (int s = lfo1; s < numModSources; s++)
1746 if (itr->second.depths[s] >= -1.0f)
1747 res.add ((ModSource) s);
1748
1749 return res;
1750 }
1752 return {};
1753}
1754
1755float FourOscPlugin::getModulationDepth (FourOscPlugin::ModSource src, AutomatableParameter::Ptr param)
1756{
1757 auto itr = modMatrix.find (param.get());
1758 if (itr != modMatrix.end())
1759 return itr->second.depths[src];
1761 return -1000;
1762}
1763
1764void FourOscPlugin::setModulationDepth (FourOscPlugin::ModSource src, AutomatableParameter::Ptr param, float depth)
1765{
1766 auto itr = modMatrix.find (param.get());
1767 if (itr != modMatrix.end())
1768 {
1769 itr->second.depths[src] = depth;
1770 itr->second.updateCachedInfo();
1771 return;
1772 }
1774}
1775
1776void FourOscPlugin::clearModulation (ModSource src, AutomatableParameter::Ptr param)
1777{
1778 auto itr = modMatrix.find (param.get());
1779 if (itr != modMatrix.end())
1780 {
1781 itr->second.depths[src] = -1000.0f;
1782 itr->second.updateCachedInfo();
1783 return;
1784 }
1786}
1787
1788float FourOscPlugin::paramValue (AutomatableParameter::Ptr param)
1789{
1790 jassert (param != nullptr);
1791 if (param == nullptr)
1792 return 0.0f;
1793
1794 auto smoothItr = smoothers.find (param.get());
1795 if (smoothItr == smoothers.end())
1796 return param->getCurrentValue();
1797
1798 float val = param->getCurrentNormalisedValue();
1799 smoothItr->second.setValue (val);
1800 return param->valueRange.convertFrom0to1 (smoothItr->second.getCurrentValue());
1801}
1802
1803}} // namespace tracktion { inline namespace engine
T ceil(T... args)
float getNextSample() noexcept
void add(const ElementType &newElement)
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
Type * getWritePointer(int channelNumber) noexcept
int getNumChannels() const noexcept
int getNumSamples() const noexcept
void clear() noexcept
void copyFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples) noexcept
void addFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples, Type gainToApplyToSource=Type(1)) noexcept
void applyGain(int channel, int startSample, int numSamples, Type gain) noexcept
void referTo(ValueTree &tree, const Identifier &property, UndoManager *um)
Type get() const noexcept
static Type decibelsToGain(Type decibels, Type minusInfinityDb=Type(defaultMinusInfinitydB))
static IIRCoefficients makeLowPass(double sampleRate, double frequency) noexcept
static IIRCoefficients makeNotchFilter(double sampleRate, double frequency) noexcept
static IIRCoefficients makeBandPass(double sampleRate, double frequency) noexcept
static IIRCoefficients makeHighPass(double sampleRate, double frequency) noexcept
void setCoefficients(const IIRCoefficients &newCoefficients) noexcept
void reset() noexcept
void processSamples(float *samples, int numSamples) noexcept
const String & toString() const noexcept
void clearCurrentNote() noexcept
virtual bool isActive() const
void reduceNumVoices(int newNumVoices)
void setCurrentPlaybackSampleRate(double newRate) override
void addVoice(MPESynthesiserVoice *newVoice)
int getNumVoices() const noexcept
MPESynthesiserVoice * getVoice(int index) const
virtual void turnOffAllVoices(bool allowTailOff)
void setLowerZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2) noexcept
void * getData() noexcept
static const char * getControllerName(int controllerNumber)
void processStereo(float *const left, float *const right, const int numSamples) noexcept
void setParameters(const Parameters &newParams)
void setSampleRate(const double sampleRate)
float getFloatValue() const noexcept
bool startsWith(StringRef text) const noexcept
bool contains(StringRef text) const noexcept
int getTrailingIntValue() const noexcept
bool isNotEmpty() const noexcept
static uint32 getApproximateMillisecondCounter() noexcept
void removeChild(const ValueTree &child, UndoManager *undoManager)
void addChild(const ValueTree &child, int index, UndoManager *undoManager)
const var & getProperty(const Identifier &name) const noexcept
ValueTree getChildWithName(const Identifier &type) const
An audio scratch buffer that has pooled storage.
juce::AudioBuffer< float > & buffer
The buffer to use.
void noteOff()
Starts the release phase of the envelope.
void reset()
Resets the envelope to an idle state.
bool isActive() const
Returns true if the envelope is in its attack, decay, sustain or release stage.
void noteOn()
Starts the attack phase of the envelope.
void applyEnvelopeToBuffer(juce::AudioBuffer< FloatType > &buffer, int startSample, int numSamples)
This method will conveniently apply the next numSamples number of envelope values to an AudioBuffer.
void setParameters(const Parameters &newParameters)
Sets the parameters that will be used by an ADSR object.
void applyToBuffer(const PluginRenderContext &) override
Process the next block of data.
void deinitialise() override
Called after play stops to release resources.
void reset() override
Should reset synth voices, tails, clear delay buffers, etc.
void setParameters(const Parameters &newParameters)
Sets the parameters that will be used by an ADSR object.
float getNextSample()
Returns the next sample value for an ADSR object.
Smooths a value between 0 and 1 at a constant rate.
T data(T... args)
T end(T... args)
T fabs(T... args)
T find(T... args)
T floor(T... args)
T is_pointer_v
#define TRANS(stringLiteral)
#define JUCE_UNDENORMALISE(x)
#define jassert(expression)
#define jassertfalse
typedef int
T log2(T... args)
typedef float
T max(T... args)
memset
T min(T... args)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
void ignoreUnused(Types &&...) noexcept
int roundToInt(const FloatType value) noexcept
constexpr int numElementsInArray(Type(&)[N]) noexcept
Passed into Plugins when they are being initialised, to give them useful contextual information that ...
T pow(T... args)
T resize(T... args)
sinf
void setPitchbendTrackingMode(TrackingMode modeToUse)
void renderNextBlock(AudioBuffer< floatType > &outputAudio, const MidiBuffer &inputMidi, int startSample, int numSamples)
void set(TimePosition)
Sets the Position to a new time.
double getTempo() const
Returns the current tempo of the Position.
Calculates the two gain multipliers to use for mixing between two sources, given a position alpha fro...
The context passed to plugin render methods to provide it with buffers to fill.
int bufferNumSamples
The number of samples to write into the audio buffer.
MidiMessageArray * bufferForMidiMessages
A buffer of MIDI events to process.
juce::AudioBuffer< float > * destBuffer
The target audio buffer which needs to be filled.
int bufferStartSample
The index of the start point in the audio buffer from which data must be written.
TimeRange editTime
The edit time range this context represents.
typedef size_t
time