OpenShot Audio Library | OpenShotAudio  0.6.0
juce_Synthesiser.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2022 - Raw Material Software Limited
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  SynthesiserVoice* voice;
102 
103  {
104  const ScopedLock sl (lock);
105  newVoice->setCurrentPlaybackSampleRate (sampleRate);
106  voice = voices.add (newVoice);
107  }
108 
109  {
110  const ScopedLock sl (stealLock);
111  usableVoicesToStealArray.ensureStorageAllocated (voices.size() + 1);
112  }
113 
114  return voice;
115 }
116 
117 void Synthesiser::removeVoice (const int index)
118 {
119  const ScopedLock sl (lock);
120  voices.remove (index);
121 }
122 
124 {
125  const ScopedLock sl (lock);
126  sounds.clear();
127 }
128 
130 {
131  const ScopedLock sl (lock);
132  return sounds.add (newSound);
133 }
134 
135 void Synthesiser::removeSound (const int index)
136 {
137  const ScopedLock sl (lock);
138  sounds.remove (index);
139 }
140 
141 void Synthesiser::setNoteStealingEnabled (const bool shouldSteal)
142 {
143  shouldStealNotes = shouldSteal;
144 }
145 
146 void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict) noexcept
147 {
148  jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1
149  minimumSubBlockSize = numSamples;
150  subBlockSubdivisionIsStrict = shouldBeStrict;
151 }
152 
153 //==============================================================================
154 void Synthesiser::setCurrentPlaybackSampleRate (const double newRate)
155 {
156  if (! approximatelyEqual (sampleRate, newRate))
157  {
158  const ScopedLock sl (lock);
159  allNotesOff (0, false);
160  sampleRate = newRate;
161 
162  for (auto* voice : voices)
163  voice->setCurrentPlaybackSampleRate (newRate);
164  }
165 }
166 
167 template <typename floatType>
168 void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio,
169  const MidiBuffer& midiData,
170  int startSample,
171  int numSamples)
172 {
173  // must set the sample rate before using this!
174  jassert (! exactlyEqual (sampleRate, 0.0));
175  const int targetChannels = outputAudio.getNumChannels();
176 
177  auto midiIterator = midiData.findNextSamplePosition (startSample);
178 
179  bool firstEvent = true;
180 
181  const ScopedLock sl (lock);
182 
183  for (; numSamples > 0; ++midiIterator)
184  {
185  if (midiIterator == midiData.cend())
186  {
187  if (targetChannels > 0)
188  renderVoices (outputAudio, startSample, numSamples);
189 
190  return;
191  }
192 
193  const auto metadata = *midiIterator;
194  const int samplesToNextMidiMessage = metadata.samplePosition - startSample;
195 
196  if (samplesToNextMidiMessage >= numSamples)
197  {
198  if (targetChannels > 0)
199  renderVoices (outputAudio, startSample, numSamples);
200 
201  handleMidiEvent (metadata.getMessage());
202  break;
203  }
204 
205  if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize))
206  {
207  handleMidiEvent (metadata.getMessage());
208  continue;
209  }
210 
211  firstEvent = false;
212 
213  if (targetChannels > 0)
214  renderVoices (outputAudio, startSample, samplesToNextMidiMessage);
215 
216  handleMidiEvent (metadata.getMessage());
217  startSample += samplesToNextMidiMessage;
218  numSamples -= samplesToNextMidiMessage;
219  }
220 
221  std::for_each (midiIterator,
222  midiData.cend(),
223  [&] (const MidiMessageMetadata& meta) { handleMidiEvent (meta.getMessage()); });
224 }
225 
226 // explicit template instantiation
227 template void Synthesiser::processNextBlock<float> (AudioBuffer<float>&, const MidiBuffer&, int, int);
228 template void Synthesiser::processNextBlock<double> (AudioBuffer<double>&, const MidiBuffer&, int, int);
229 
230 void Synthesiser::renderNextBlock (AudioBuffer<float>& outputAudio, const MidiBuffer& inputMidi,
231  int startSample, int numSamples)
232 {
233  processNextBlock (outputAudio, inputMidi, startSample, numSamples);
234 }
235 
236 void Synthesiser::renderNextBlock (AudioBuffer<double>& outputAudio, const MidiBuffer& inputMidi,
237  int startSample, int numSamples)
238 {
239  processNextBlock (outputAudio, inputMidi, startSample, numSamples);
240 }
241 
242 void Synthesiser::renderVoices (AudioBuffer<float>& buffer, int startSample, int numSamples)
243 {
244  for (auto* voice : voices)
245  voice->renderNextBlock (buffer, startSample, numSamples);
246 }
247 
248 void Synthesiser::renderVoices (AudioBuffer<double>& buffer, int startSample, int numSamples)
249 {
250  for (auto* voice : voices)
251  voice->renderNextBlock (buffer, startSample, numSamples);
252 }
253 
255 {
256  const int channel = m.getChannel();
257 
258  if (m.isNoteOn())
259  {
260  noteOn (channel, m.getNoteNumber(), m.getFloatVelocity());
261  }
262  else if (m.isNoteOff())
263  {
264  noteOff (channel, m.getNoteNumber(), m.getFloatVelocity(), true);
265  }
266  else if (m.isAllNotesOff() || m.isAllSoundOff())
267  {
268  allNotesOff (channel, true);
269  }
270  else if (m.isPitchWheel())
271  {
272  const int wheelPos = m.getPitchWheelValue();
273  lastPitchWheelValues [channel - 1] = wheelPos;
274  handlePitchWheel (channel, wheelPos);
275  }
276  else if (m.isAftertouch())
277  {
279  }
280  else if (m.isChannelPressure())
281  {
283  }
284  else if (m.isController())
285  {
287  }
288  else if (m.isProgramChange())
289  {
291  }
292 }
293 
294 //==============================================================================
295 void Synthesiser::noteOn (const int midiChannel,
296  const int midiNoteNumber,
297  const float velocity)
298 {
299  const ScopedLock sl (lock);
300 
301  for (auto* sound : sounds)
302  {
303  if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel))
304  {
305  // If hitting a note that's still ringing, stop it first (it could be
306  // still playing because of the sustain or sostenuto pedal).
307  for (auto* voice : voices)
308  if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel))
309  stopVoice (voice, 1.0f, true);
310 
311  startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
312  sound, midiChannel, midiNoteNumber, velocity);
313  }
314  }
315 }
316 
318  SynthesiserSound* const sound,
319  const int midiChannel,
320  const int midiNoteNumber,
321  const float velocity)
322 {
323  if (voice != nullptr && sound != nullptr)
324  {
325  if (voice->currentlyPlayingSound != nullptr)
326  voice->stopNote (0.0f, false);
327 
328  voice->currentlyPlayingNote = midiNoteNumber;
329  voice->currentPlayingMidiChannel = midiChannel;
330  voice->noteOnTime = ++lastNoteOnCounter;
331  voice->currentlyPlayingSound = sound;
332  voice->setKeyDown (true);
333  voice->setSostenutoPedalDown (false);
334  voice->setSustainPedalDown (sustainPedalsDown[midiChannel]);
335 
336  voice->startNote (midiNoteNumber, velocity, sound,
337  lastPitchWheelValues [midiChannel - 1]);
338  }
339 }
340 
341 void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool allowTailOff)
342 {
343  jassert (voice != nullptr);
344 
345  voice->stopNote (velocity, allowTailOff);
346 
347  // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
348  jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == nullptr));
349 }
350 
351 void Synthesiser::noteOff (const int midiChannel,
352  const int midiNoteNumber,
353  const float velocity,
354  const bool allowTailOff)
355 {
356  const ScopedLock sl (lock);
357 
358  for (auto* voice : voices)
359  {
360  if (voice->getCurrentlyPlayingNote() == midiNoteNumber
361  && voice->isPlayingChannel (midiChannel))
362  {
363  if (auto sound = voice->getCurrentlyPlayingSound())
364  {
365  if (sound->appliesToNote (midiNoteNumber)
366  && sound->appliesToChannel (midiChannel))
367  {
368  jassert (! voice->keyIsDown || voice->isSustainPedalDown() == sustainPedalsDown [midiChannel]);
369 
370  voice->setKeyDown (false);
371 
372  if (! (voice->isSustainPedalDown() || voice->isSostenutoPedalDown()))
373  stopVoice (voice, velocity, allowTailOff);
374  }
375  }
376  }
377  }
378 }
379 
380 void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
381 {
382  const ScopedLock sl (lock);
383 
384  for (auto* voice : voices)
385  if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
386  voice->stopNote (1.0f, allowTailOff);
387 
388  sustainPedalsDown.clear();
389 }
390 
391 void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue)
392 {
393  const ScopedLock sl (lock);
394 
395  for (auto* voice : voices)
396  if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
397  voice->pitchWheelMoved (wheelValue);
398 }
399 
400 void Synthesiser::handleController (const int midiChannel,
401  const int controllerNumber,
402  const int controllerValue)
403 {
404  switch (controllerNumber)
405  {
406  case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break;
407  case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break;
408  case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break;
409  default: break;
410  }
411 
412  const ScopedLock sl (lock);
413 
414  for (auto* voice : voices)
415  if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
416  voice->controllerMoved (controllerNumber, controllerValue);
417 }
418 
419 void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue)
420 {
421  const ScopedLock sl (lock);
422 
423  for (auto* voice : voices)
424  if (voice->getCurrentlyPlayingNote() == midiNoteNumber
425  && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)))
426  voice->aftertouchChanged (aftertouchValue);
427 }
428 
429 void Synthesiser::handleChannelPressure (int midiChannel, int channelPressureValue)
430 {
431  const ScopedLock sl (lock);
432 
433  for (auto* voice : voices)
434  if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
435  voice->channelPressureChanged (channelPressureValue);
436 }
437 
438 void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
439 {
440  jassert (midiChannel > 0 && midiChannel <= 16);
441  const ScopedLock sl (lock);
442 
443  if (isDown)
444  {
445  sustainPedalsDown.setBit (midiChannel);
446 
447  for (auto* voice : voices)
448  if (voice->isPlayingChannel (midiChannel) && voice->isKeyDown())
449  voice->setSustainPedalDown (true);
450  }
451  else
452  {
453  for (auto* voice : voices)
454  {
455  if (voice->isPlayingChannel (midiChannel))
456  {
457  voice->setSustainPedalDown (false);
458 
459  if (! (voice->isKeyDown() || voice->isSostenutoPedalDown()))
460  stopVoice (voice, 1.0f, true);
461  }
462  }
463 
464  sustainPedalsDown.clearBit (midiChannel);
465  }
466 }
467 
468 void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
469 {
470  jassert (midiChannel > 0 && midiChannel <= 16);
471  const ScopedLock sl (lock);
472 
473  for (auto* voice : voices)
474  {
475  if (voice->isPlayingChannel (midiChannel))
476  {
477  if (isDown)
478  voice->setSostenutoPedalDown (true);
479  else if (voice->isSostenutoPedalDown())
480  stopVoice (voice, 1.0f, true);
481  }
482  }
483 }
484 
485 void Synthesiser::handleSoftPedal ([[maybe_unused]] int midiChannel, bool /*isDown*/)
486 {
487  jassert (midiChannel > 0 && midiChannel <= 16);
488 }
489 
490 void Synthesiser::handleProgramChange ([[maybe_unused]] int midiChannel,
491  [[maybe_unused]] int programNumber)
492 {
493  jassert (midiChannel > 0 && midiChannel <= 16);
494 }
495 
496 //==============================================================================
498  int midiChannel, int midiNoteNumber,
499  const bool stealIfNoneAvailable) const
500 {
501  const ScopedLock sl (lock);
502 
503  for (auto* voice : voices)
504  if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay))
505  return voice;
506 
507  if (stealIfNoneAvailable)
508  return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber);
509 
510  return nullptr;
511 }
512 
514  int /*midiChannel*/, int midiNoteNumber) const
515 {
516  // This voice-stealing algorithm applies the following heuristics:
517  // - Re-use the oldest notes first
518  // - Protect the lowest & topmost notes, even if sustained, but not if they've been released.
519 
520  // apparently you are trying to render audio without having any voices...
521  jassert (! voices.isEmpty());
522 
523  // These are the voices we want to protect (ie: only steal if unavoidable)
524  SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
525  SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase
526 
527  // All major OSes use double-locking so this will be lock- and wait-free as long as the lock is not
528  // contended. This is always the case if you do not call findVoiceToSteal on multiple threads at
529  // the same time.
530  const ScopedLock sl (stealLock);
531 
532  // this is a list of voices we can steal, sorted by how long they've been running
533  usableVoicesToStealArray.clear();
534 
535  for (auto* voice : voices)
536  {
537  if (voice->canPlaySound (soundToPlay))
538  {
539  jassert (voice->isVoiceActive()); // We wouldn't be here otherwise
540 
541  usableVoicesToStealArray.add (voice);
542 
543  // NB: Using a functor rather than a lambda here due to scare-stories about
544  // compilers generating code containing heap allocations..
545  struct Sorter
546  {
547  bool operator() (const SynthesiserVoice* a, const SynthesiserVoice* b) const noexcept { return a->wasStartedBefore (*b); }
548  };
549 
550  std::sort (usableVoicesToStealArray.begin(), usableVoicesToStealArray.end(), Sorter());
551 
552  if (! voice->isPlayingButReleased()) // Don't protect released notes
553  {
554  auto note = voice->getCurrentlyPlayingNote();
555 
556  if (low == nullptr || note < low->getCurrentlyPlayingNote())
557  low = voice;
558 
559  if (top == nullptr || note > top->getCurrentlyPlayingNote())
560  top = voice;
561  }
562  }
563  }
564 
565  // Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s)
566  if (top == low)
567  top = nullptr;
568 
569  // The oldest note that's playing with the target pitch is ideal..
570  for (auto* voice : usableVoicesToStealArray)
571  if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
572  return voice;
573 
574  // Oldest voice that has been released (no finger on it and not held by sustain pedal)
575  for (auto* voice : usableVoicesToStealArray)
576  if (voice != low && voice != top && voice->isPlayingButReleased())
577  return voice;
578 
579  // Oldest voice that doesn't have a finger on it:
580  for (auto* voice : usableVoicesToStealArray)
581  if (voice != low && voice != top && ! voice->isKeyDown())
582  return voice;
583 
584  // Oldest voice that isn't protected
585  for (auto* voice : usableVoicesToStealArray)
586  if (voice != low && voice != top)
587  return voice;
588 
589  // We've only got "protected" voices now: lowest note takes priority
590  jassert (low != nullptr);
591 
592  // Duophonic synth: give priority to the bass note:
593  if (top != nullptr)
594  return top;
595 
596  return low;
597 }
598 
599 } // namespace juce
void makeCopyOf(const AudioBuffer< OtherType > &other, bool avoidReallocating=false)
int getNumChannels() const noexcept
Type *const * getArrayOfWritePointers() noexcept
BigInteger & clear() noexcept
BigInteger & clearBit(int bitNumber) noexcept
BigInteger & setBit(int bitNumber)
MidiBufferIterator findNextSamplePosition(int samplePosition) const noexcept
MidiBufferIterator cend() const noexcept
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 stopNote(float velocity, bool allowTailOff)=0
void setSustainPedalDown(bool isNowDown) noexcept
virtual void channelPressureChanged(int newChannelPressureValue)
void setSostenutoPedalDown(bool isNowDown) noexcept
virtual void renderNextBlock(AudioBuffer< float > &outputBuffer, int startSample, int numSamples)=0
virtual bool isPlayingChannel(int midiChannel) const
bool isKeyDown() const noexcept
void setKeyDown(bool isNowDown) noexcept
virtual void setCurrentPlaybackSampleRate(double newRate)
virtual void aftertouchChanged(int newAftertouchValue)
int getCurrentlyPlayingNote() const noexcept
virtual void startNote(int midiNoteNumber, float velocity, SynthesiserSound *sound, int currentPitchWheelPosition)=0
bool wasStartedBefore(const SynthesiserVoice &other) const noexcept
bool isPlayingButReleased() const noexcept
SynthesiserSound::Ptr getCurrentlyPlayingSound() 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)