OpenShot Audio Library | OpenShotAudio  0.3.1
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 
23 namespace juce
24 {
25 
26 SynthesiserSound::SynthesiserSound() {}
28 
29 //==============================================================================
32 
33 bool 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 
58 bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept
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 
83 {
84 }
85 
86 //==============================================================================
87 SynthesiserVoice* Synthesiser::getVoice (const int index) const
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 
106 void 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 
119 {
120  const ScopedLock sl (lock);
121  return sounds.add (newSound);
122 }
123 
124 void Synthesiser::removeSound (const int index)
125 {
126  const ScopedLock sl (lock);
127  sounds.remove (index);
128 }
129 
130 void Synthesiser::setNoteStealingEnabled (const bool shouldSteal)
131 {
132  shouldStealNotes = shouldSteal;
133 }
134 
135 void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict) noexcept
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 //==============================================================================
143 void Synthesiser::setCurrentPlaybackSampleRate (const double newRate)
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 
156 template <typename floatType>
157 void 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 
166  MidiBuffer::Iterator midiIterator (midiData);
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);
191 
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
217 template void Synthesiser::processNextBlock<float> (AudioBuffer<float>&, const MidiBuffer&, int, int);
218 template void Synthesiser::processNextBlock<double> (AudioBuffer<double>&, const MidiBuffer&, int, int);
219 
220 void Synthesiser::renderNextBlock (AudioBuffer<float>& outputAudio, const MidiBuffer& inputMidi,
221  int startSample, int numSamples)
222 {
223  processNextBlock (outputAudio, inputMidi, startSample, numSamples);
224 }
225 
226 void Synthesiser::renderNextBlock (AudioBuffer<double>& outputAudio, const MidiBuffer& inputMidi,
227  int startSample, int numSamples)
228 {
229  processNextBlock (outputAudio, inputMidi, startSample, numSamples);
230 }
231 
232 void Synthesiser::renderVoices (AudioBuffer<float>& buffer, int startSample, int numSamples)
233 {
234  for (auto* voice : voices)
235  voice->renderNextBlock (buffer, startSample, numSamples);
236 }
237 
238 void 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  {
268  handleAftertouch (channel, m.getNoteNumber(), m.getAfterTouchValue());
269  }
270  else if (m.isChannelPressure())
271  {
272  handleChannelPressure (channel, m.getChannelPressureValue());
273  }
274  else if (m.isController())
275  {
276  handleController (channel, m.getControllerNumber(), m.getControllerValue());
277  }
278  else if (m.isProgramChange())
279  {
280  handleProgramChange (channel, m.getProgramChangeNumber());
281  }
282 }
283 
284 //==============================================================================
285 void 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 
331 void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool allowTailOff)
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 
341 void 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()))
363  stopVoice (voice, velocity, allowTailOff);
364  }
365  }
366  }
367  }
368 }
369 
370 void 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 
381 void 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 
390 void 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 
409 void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue)
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 
419 void Synthesiser::handleChannelPressure (int midiChannel, int channelPressureValue)
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 
428 void 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 
458 void 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 
475 void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/)
476 {
477  ignoreUnused (midiChannel);
478  jassert (midiChannel > 0 && midiChannel <= 16);
479 }
480 
481 void Synthesiser::handleProgramChange (int midiChannel, int programNumber)
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 
498  if (stealIfNoneAvailable)
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
519  Array<SynthesiserVoice*> usableVoices;
520  usableVoices.ensureStorageAllocated (voices.size());
521 
522  for (auto* voice : voices)
523  {
524  if (voice->canPlaySound (soundToPlay))
525  {
526  jassert (voice->isVoiceActive()); // We wouldn't be here otherwise
527 
528  usableVoices.add (voice);
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 isAftertouch() const noexcept
virtual SynthesiserVoice * findVoiceToSteal(SynthesiserSound *soundToPlay, int midiChannel, int midiNoteNumber) const
void setNextSamplePosition(int samplePosition) noexcept
bool isPitchWheel() const noexcept
int getAfterTouchValue() const noexcept
ElementType * end() noexcept
Definition: juce_Array.h:344
bool wasStartedBefore(const SynthesiserVoice &other) const noexcept
bool getNextEvent(MidiMessage &result, int &samplePosition) noexcept
bool isAllNotesOff() const noexcept
void ensureStorageAllocated(int minNumElements)
Definition: juce_Array.h:1062
void add(const ElementType &newElement)
Definition: juce_Array.h:418
virtual void handleSostenutoPedal(int midiChannel, bool isDown)
void removeVoice(int index)
virtual void allNotesOff(int midiChannel, bool allowTailOff)
virtual void handleController(int midiChannel, int controllerNumber, int controllerValue)
virtual bool isPlayingChannel(int midiChannel) const
void renderNextBlock(AudioBuffer< float > &outputAudio, const MidiBuffer &inputMidi, int startSample, int numSamples)
Type ** getArrayOfWritePointers() noexcept
void setSostenutoPedalDown(bool isNowDown) noexcept
virtual void handleChannelPressure(int midiChannel, int channelPressureValue)
virtual void noteOff(int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff)
SynthesiserVoice * addVoice(SynthesiserVoice *newVoice)
SynthesiserSound::Ptr getCurrentlyPlayingSound() const noexcept
virtual void renderNextBlock(AudioBuffer< float > &outputBuffer, int startSample, int numSamples)=0
void stopVoice(SynthesiserVoice *, float velocity, bool allowTailOff)
bool isProgramChange() const noexcept
virtual void handleMidiEvent(const MidiMessage &)
virtual void setCurrentPlaybackSampleRate(double sampleRate)
ElementType * begin() noexcept
Definition: juce_Array.h:328
void setSustainPedalDown(bool isNowDown) noexcept
int getControllerNumber() const noexcept
int getChannel() const noexcept
bool isController() const noexcept
int getNumChannels() const noexcept
virtual void renderVoices(AudioBuffer< float > &outputAudio, int startSample, int numSamples)
virtual void setCurrentPlaybackSampleRate(double newRate)
virtual void startNote(int midiNoteNumber, float velocity, SynthesiserSound *sound, int currentPitchWheelPosition)=0
virtual bool isVoiceActive() const
bool isChannelPressure() const noexcept
float getFloatVelocity() const noexcept
int getProgramChangeNumber() const noexcept
int getPitchWheelValue() const noexcept
virtual void stopNote(float velocity, bool allowTailOff)=0
bool isKeyDown() const noexcept
int getChannelPressureValue() const noexcept
virtual void handleSoftPedal(int midiChannel, bool isDown)
void setMinimumRenderingSubdivisionSize(int numSamples, bool shouldBeStrict=false) noexcept
bool isNoteOff(bool returnTrueForNoteOnVelocity0=true) const noexcept
virtual void channelPressureChanged(int newChannelPressureValue)
SynthesiserSound * addSound(const SynthesiserSound::Ptr &newSound)
bool isPlayingButReleased() const noexcept
int getCurrentlyPlayingNote() const noexcept
bool isNoteOn(bool returnTrueForVelocity0=false) const noexcept
void startVoice(SynthesiserVoice *voice, SynthesiserSound *sound, int midiChannel, int midiNoteNumber, float velocity)
void makeCopyOf(const AudioBuffer< OtherType > &other, bool avoidReallocating=false)
virtual void handleProgramChange(int midiChannel, int programNumber)
virtual SynthesiserVoice * findFreeVoice(SynthesiserSound *soundToPlay, int midiChannel, int midiNoteNumber, bool stealIfNoneAvailable) const
void removeSound(int index)
int getControllerValue() const noexcept
virtual void aftertouchChanged(int newAftertouchValue)
SynthesiserVoice * getVoice(int index) const
virtual void handleSustainPedal(int midiChannel, bool isDown)
virtual void handleAftertouch(int midiChannel, int midiNoteNumber, int aftertouchValue)
int getNoteNumber() const noexcept
void setNoteStealingEnabled(bool shouldStealNotes)
virtual void noteOn(int midiChannel, int midiNoteNumber, float velocity)
virtual void handlePitchWheel(int midiChannel, int wheelValue)
void setKeyDown(bool isNowDown) noexcept
bool isAllSoundOff() const noexcept