OpenShot Audio Library | OpenShotAudio 0.3.2
Loading...
Searching...
No Matches
juce_Synthesiser.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26SynthesiserSound::SynthesiserSound() {}
28
29//==============================================================================
32
33bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const
34{
35 return currentPlayingMidiChannel == midiChannel;
36}
37
39{
40 currentSampleRate = newRate;
41}
42
44{
45 return getCurrentlyPlayingNote() >= 0;
46}
47
49{
50 currentlyPlayingNote = -1;
51 currentlyPlayingSound = nullptr;
52 currentPlayingMidiChannel = 0;
53}
54
57
59{
60 return noteOnTime < other.noteOnTime;
61}
62
64 int startSample, int numSamples)
65{
66 AudioBuffer<double> subBuffer (outputBuffer.getArrayOfWritePointers(),
67 outputBuffer.getNumChannels(),
68 startSample, numSamples);
69
70 tempBuffer.makeCopyOf (subBuffer, true);
71 renderNextBlock (tempBuffer, 0, numSamples);
72 subBuffer.makeCopyOf (tempBuffer, true);
73}
74
75//==============================================================================
77{
78 for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i)
79 lastPitchWheelValues[i] = 0x2000;
80}
81
85
86//==============================================================================
88{
89 const ScopedLock sl (lock);
90 return voices [index];
91}
92
94{
95 const ScopedLock sl (lock);
96 voices.clear();
97}
98
100{
101 const ScopedLock sl (lock);
102 newVoice->setCurrentPlaybackSampleRate (sampleRate);
103 return voices.add (newVoice);
104}
105
106void Synthesiser::removeVoice (const int index)
107{
108 const ScopedLock sl (lock);
109 voices.remove (index);
110}
111
113{
114 const ScopedLock sl (lock);
115 sounds.clear();
116}
117
123
124void Synthesiser::removeSound (const int index)
125{
126 const ScopedLock sl (lock);
127 sounds.remove (index);
128}
129
131{
132 shouldStealNotes = shouldSteal;
133}
134
136{
137 jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1
138 minimumSubBlockSize = numSamples;
139 subBlockSubdivisionIsStrict = shouldBeStrict;
140}
141
142//==============================================================================
144{
145 if (sampleRate != newRate)
146 {
147 const ScopedLock sl (lock);
148 allNotesOff (0, false);
149 sampleRate = newRate;
150
151 for (auto* voice : voices)
152 voice->setCurrentPlaybackSampleRate (newRate);
153 }
154}
155
156template <typename floatType>
157void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio,
158 const MidiBuffer& midiData,
159 int startSample,
160 int numSamples)
161{
162 // must set the sample rate before using this!
163 jassert (sampleRate != 0);
164 const int targetChannels = outputAudio.getNumChannels();
165
167 midiIterator.setNextSamplePosition (startSample);
168
169 bool firstEvent = true;
170 int midiEventPos;
171 MidiMessage m;
172
173 const ScopedLock sl (lock);
174
175 while (numSamples > 0)
176 {
177 if (! midiIterator.getNextEvent (m, midiEventPos))
178 {
179 if (targetChannels > 0)
180 renderVoices (outputAudio, startSample, numSamples);
181
182 return;
183 }
184
185 const int samplesToNextMidiMessage = midiEventPos - startSample;
186
187 if (samplesToNextMidiMessage >= numSamples)
188 {
189 if (targetChannels > 0)
190 renderVoices (outputAudio, startSample, numSamples);
192 handleMidiEvent (m);
193 break;
194 }
195
196 if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize))
197 {
198 handleMidiEvent (m);
199 continue;
200 }
201
202 firstEvent = false;
203
204 if (targetChannels > 0)
205 renderVoices (outputAudio, startSample, samplesToNextMidiMessage);
206
207 handleMidiEvent (m);
208 startSample += samplesToNextMidiMessage;
209 numSamples -= samplesToNextMidiMessage;
210 }
211
212 while (midiIterator.getNextEvent (m, midiEventPos))
213 handleMidiEvent (m);
214}
215
216// explicit template instantiation
217template void Synthesiser::processNextBlock<float> (AudioBuffer<float>&, const MidiBuffer&, int, int);
218template void Synthesiser::processNextBlock<double> (AudioBuffer<double>&, const MidiBuffer&, int, int);
219
221 int startSample, int numSamples)
222{
223 processNextBlock (outputAudio, inputMidi, startSample, numSamples);
224}
225
227 int startSample, int numSamples)
228{
229 processNextBlock (outputAudio, inputMidi, startSample, numSamples);
230}
231
232void Synthesiser::renderVoices (AudioBuffer<float>& buffer, int startSample, int numSamples)
233{
234 for (auto* voice : voices)
235 voice->renderNextBlock (buffer, startSample, numSamples);
236}
237
238void Synthesiser::renderVoices (AudioBuffer<double>& buffer, int startSample, int numSamples)
239{
240 for (auto* voice : voices)
241 voice->renderNextBlock (buffer, startSample, numSamples);
242}
243
245{
246 const int channel = m.getChannel();
247
248 if (m.isNoteOn())
249 {
250 noteOn (channel, m.getNoteNumber(), m.getFloatVelocity());
251 }
252 else if (m.isNoteOff())
253 {
254 noteOff (channel, m.getNoteNumber(), m.getFloatVelocity(), true);
255 }
256 else if (m.isAllNotesOff() || m.isAllSoundOff())
257 {
258 allNotesOff (channel, true);
259 }
260 else if (m.isPitchWheel())
261 {
262 const int wheelPos = m.getPitchWheelValue();
263 lastPitchWheelValues [channel - 1] = wheelPos;
264 handlePitchWheel (channel, wheelPos);
265 }
266 else if (m.isAftertouch())
267 {
269 }
270 else if (m.isChannelPressure())
271 {
273 }
274 else if (m.isController())
275 {
277 }
278 else if (m.isProgramChange())
279 {
281 }
282}
283
284//==============================================================================
285void Synthesiser::noteOn (const int midiChannel,
286 const int midiNoteNumber,
287 const float velocity)
288{
289 const ScopedLock sl (lock);
290
291 for (auto* sound : sounds)
292 {
293 if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel))
294 {
295 // If hitting a note that's still ringing, stop it first (it could be
296 // still playing because of the sustain or sostenuto pedal).
297 for (auto* voice : voices)
298 if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel))
299 stopVoice (voice, 1.0f, true);
300
301 startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
302 sound, midiChannel, midiNoteNumber, velocity);
303 }
304 }
305}
306
308 SynthesiserSound* const sound,
309 const int midiChannel,
310 const int midiNoteNumber,
311 const float velocity)
312{
313 if (voice != nullptr && sound != nullptr)
314 {
315 if (voice->currentlyPlayingSound != nullptr)
316 voice->stopNote (0.0f, false);
317
318 voice->currentlyPlayingNote = midiNoteNumber;
319 voice->currentPlayingMidiChannel = midiChannel;
320 voice->noteOnTime = ++lastNoteOnCounter;
321 voice->currentlyPlayingSound = sound;
322 voice->setKeyDown (true);
323 voice->setSostenutoPedalDown (false);
324 voice->setSustainPedalDown (sustainPedalsDown[midiChannel]);
325
326 voice->startNote (midiNoteNumber, velocity, sound,
327 lastPitchWheelValues [midiChannel - 1]);
328 }
329}
330
332{
333 jassert (voice != nullptr);
334
335 voice->stopNote (velocity, allowTailOff);
336
337 // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
338 jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == nullptr));
339}
340
341void Synthesiser::noteOff (const int midiChannel,
342 const int midiNoteNumber,
343 const float velocity,
344 const bool allowTailOff)
345{
346 const ScopedLock sl (lock);
347
348 for (auto* voice : voices)
349 {
350 if (voice->getCurrentlyPlayingNote() == midiNoteNumber
351 && voice->isPlayingChannel (midiChannel))
352 {
353 if (auto sound = voice->getCurrentlyPlayingSound())
354 {
355 if (sound->appliesToNote (midiNoteNumber)
356 && sound->appliesToChannel (midiChannel))
357 {
358 jassert (! voice->keyIsDown || voice->isSustainPedalDown() == sustainPedalsDown [midiChannel]);
359
360 voice->setKeyDown (false);
361
362 if (! (voice->isSustainPedalDown() || voice->isSostenutoPedalDown()))
364 }
365 }
366 }
367 }
368}
369
370void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
371{
372 const ScopedLock sl (lock);
373
374 for (auto* voice : voices)
375 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
376 voice->stopNote (1.0f, allowTailOff);
377
378 sustainPedalsDown.clear();
379}
380
381void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue)
382{
383 const ScopedLock sl (lock);
384
385 for (auto* voice : voices)
386 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
387 voice->pitchWheelMoved (wheelValue);
388}
389
390void Synthesiser::handleController (const int midiChannel,
391 const int controllerNumber,
392 const int controllerValue)
393{
394 switch (controllerNumber)
395 {
396 case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break;
397 case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break;
398 case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break;
399 default: break;
400 }
401
402 const ScopedLock sl (lock);
403
404 for (auto* voice : voices)
405 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
406 voice->controllerMoved (controllerNumber, controllerValue);
407}
408
410{
411 const ScopedLock sl (lock);
412
413 for (auto* voice : voices)
414 if (voice->getCurrentlyPlayingNote() == midiNoteNumber
415 && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)))
416 voice->aftertouchChanged (aftertouchValue);
417}
418
420{
421 const ScopedLock sl (lock);
422
423 for (auto* voice : voices)
424 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
425 voice->channelPressureChanged (channelPressureValue);
426}
427
428void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
429{
430 jassert (midiChannel > 0 && midiChannel <= 16);
431 const ScopedLock sl (lock);
432
433 if (isDown)
434 {
435 sustainPedalsDown.setBit (midiChannel);
436
437 for (auto* voice : voices)
438 if (voice->isPlayingChannel (midiChannel) && voice->isKeyDown())
439 voice->setSustainPedalDown (true);
440 }
441 else
442 {
443 for (auto* voice : voices)
444 {
445 if (voice->isPlayingChannel (midiChannel))
446 {
447 voice->setSustainPedalDown (false);
448
449 if (! (voice->isKeyDown() || voice->isSostenutoPedalDown()))
450 stopVoice (voice, 1.0f, true);
451 }
452 }
453
454 sustainPedalsDown.clearBit (midiChannel);
455 }
456}
457
458void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
459{
460 jassert (midiChannel > 0 && midiChannel <= 16);
461 const ScopedLock sl (lock);
462
463 for (auto* voice : voices)
464 {
465 if (voice->isPlayingChannel (midiChannel))
466 {
467 if (isDown)
468 voice->setSostenutoPedalDown (true);
469 else if (voice->isSostenutoPedalDown())
470 stopVoice (voice, 1.0f, true);
471 }
472 }
473}
474
475void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/)
476{
477 ignoreUnused (midiChannel);
478 jassert (midiChannel > 0 && midiChannel <= 16);
479}
480
482{
483 ignoreUnused (midiChannel, programNumber);
484 jassert (midiChannel > 0 && midiChannel <= 16);
485}
486
487//==============================================================================
489 int midiChannel, int midiNoteNumber,
490 const bool stealIfNoneAvailable) const
491{
492 const ScopedLock sl (lock);
493
494 for (auto* voice : voices)
495 if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay))
496 return voice;
497
499 return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber);
500
501 return nullptr;
502}
503
505 int /*midiChannel*/, int midiNoteNumber) const
506{
507 // This voice-stealing algorithm applies the following heuristics:
508 // - Re-use the oldest notes first
509 // - Protect the lowest & topmost notes, even if sustained, but not if they've been released.
510
511 // apparently you are trying to render audio without having any voices...
512 jassert (! voices.isEmpty());
513
514 // These are the voices we want to protect (ie: only steal if unavoidable)
515 SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
516 SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase
517
518 // this is a list of voices we can steal, sorted by how long they've been running
521
522 for (auto* voice : voices)
523 {
524 if (voice->canPlaySound (soundToPlay))
525 {
526 jassert (voice->isVoiceActive()); // We wouldn't be here otherwise
527
529
530 // NB: Using a functor rather than a lambda here due to scare-stories about
531 // compilers generating code containing heap allocations..
532 struct Sorter
533 {
534 bool operator() (const SynthesiserVoice* a, const SynthesiserVoice* b) const noexcept { return a->wasStartedBefore (*b); }
535 };
536
537 std::sort (usableVoices.begin(), usableVoices.end(), Sorter());
538
539 if (! voice->isPlayingButReleased()) // Don't protect released notes
540 {
541 auto note = voice->getCurrentlyPlayingNote();
542
543 if (low == nullptr || note < low->getCurrentlyPlayingNote())
544 low = voice;
545
546 if (top == nullptr || note > top->getCurrentlyPlayingNote())
547 top = voice;
548 }
549 }
550 }
551
552 // Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s)
553 if (top == low)
554 top = nullptr;
555
556 // The oldest note that's playing with the target pitch is ideal..
557 for (auto* voice : usableVoices)
558 if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
559 return voice;
560
561 // Oldest voice that has been released (no finger on it and not held by sustain pedal)
562 for (auto* voice : usableVoices)
563 if (voice != low && voice != top && voice->isPlayingButReleased())
564 return voice;
565
566 // Oldest voice that doesn't have a finger on it:
567 for (auto* voice : usableVoices)
568 if (voice != low && voice != top && ! voice->isKeyDown())
569 return voice;
570
571 // Oldest voice that isn't protected
572 for (auto* voice : usableVoices)
573 if (voice != low && voice != top)
574 return voice;
575
576 // We've only got "protected" voices now: lowest note takes priority
577 jassert (low != nullptr);
578
579 // Duophonic synth: give priority to the bass note:
580 if (top != nullptr)
581 return top;
582
583 return low;
584}
585
586} // namespace juce
bool isEmpty() const noexcept
Definition juce_Array.h:222
void ensureStorageAllocated(int minNumElements)
int size() const noexcept
Definition juce_Array.h:215
void remove(int indexToRemove)
Definition juce_Array.h:767
ElementType * begin() noexcept
Definition juce_Array.h:328
ElementType * end() noexcept
Definition juce_Array.h:344
void add(const ElementType &newElement)
Definition juce_Array.h:418
void clear()
Definition juce_Array.h:188
void makeCopyOf(const AudioBuffer< OtherType > &other, bool avoidReallocating=false)
void clearBit(int bitNumber) noexcept
void clear() noexcept
void setBit(int bitNumber)
bool isAftertouch() const noexcept
bool isNoteOn(bool returnTrueForVelocity0=false) const noexcept
float getFloatVelocity() const noexcept
int getChannel() const noexcept
bool isProgramChange() const noexcept
bool isController() const noexcept
bool isAllSoundOff() const noexcept
int getControllerNumber() const noexcept
int getChannelPressureValue() const noexcept
bool isNoteOff(bool returnTrueForNoteOnVelocity0=true) const noexcept
bool isPitchWheel() const noexcept
int getNoteNumber() const noexcept
int getProgramChangeNumber() const noexcept
int getAfterTouchValue() const noexcept
int getControllerValue() const noexcept
bool isAllNotesOff() const noexcept
bool isChannelPressure() const noexcept
int getPitchWheelValue() const noexcept
virtual void channelPressureChanged(int newChannelPressureValue)
virtual void renderNextBlock(AudioBuffer< float > &outputBuffer, int startSample, int numSamples)=0
virtual bool isPlayingChannel(int midiChannel) const
virtual void setCurrentPlaybackSampleRate(double newRate)
virtual void aftertouchChanged(int newAftertouchValue)
int getCurrentlyPlayingNote() const noexcept
bool wasStartedBefore(const SynthesiserVoice &other) const noexcept
virtual bool isVoiceActive() const
virtual SynthesiserVoice * findFreeVoice(SynthesiserSound *soundToPlay, int midiChannel, int midiNoteNumber, bool stealIfNoneAvailable) const
virtual void handleProgramChange(int midiChannel, int programNumber)
void removeVoice(int index)
void startVoice(SynthesiserVoice *voice, SynthesiserSound *sound, int midiChannel, int midiNoteNumber, float velocity)
virtual void handleAftertouch(int midiChannel, int midiNoteNumber, int aftertouchValue)
void renderNextBlock(AudioBuffer< float > &outputAudio, const MidiBuffer &inputMidi, int startSample, int numSamples)
virtual void handleController(int midiChannel, int controllerNumber, int controllerValue)
virtual SynthesiserVoice * findVoiceToSteal(SynthesiserSound *soundToPlay, int midiChannel, int midiNoteNumber) const
virtual void handleSoftPedal(int midiChannel, bool isDown)
void removeSound(int index)
virtual void allNotesOff(int midiChannel, bool allowTailOff)
SynthesiserVoice * addVoice(SynthesiserVoice *newVoice)
void stopVoice(SynthesiserVoice *, float velocity, bool allowTailOff)
SynthesiserSound * addSound(const SynthesiserSound::Ptr &newSound)
virtual void renderVoices(AudioBuffer< float > &outputAudio, int startSample, int numSamples)
virtual void handleMidiEvent(const MidiMessage &)
void setNoteStealingEnabled(bool shouldStealNotes)
virtual void handlePitchWheel(int midiChannel, int wheelValue)
CriticalSection lock
virtual void noteOn(int midiChannel, int midiNoteNumber, float velocity)
SynthesiserVoice * getVoice(int index) const
virtual void noteOff(int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff)
virtual void handleChannelPressure(int midiChannel, int channelPressureValue)
virtual void setCurrentPlaybackSampleRate(double sampleRate)
void setMinimumRenderingSubdivisionSize(int numSamples, bool shouldBeStrict=false) noexcept
virtual void handleSustainPedal(int midiChannel, bool isDown)
virtual void handleSostenutoPedal(int midiChannel, bool isDown)