OpenShot Audio Library | OpenShotAudio  0.3.1
juce_MPESynthesiser.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 
27 {
28  MPEZoneLayout zoneLayout;
29  zoneLayout.setLowerZone (15);
30  setZoneLayout (zoneLayout);
31 }
32 
34 {
35 }
36 
38 {
39 }
40 
41 //==============================================================================
43 {
44  jassert (voice != nullptr);
45 
46  voice->currentlyPlayingNote = noteToStart;
47  voice->noteOnTime = lastNoteOnCounter++;
48  voice->noteStarted();
49 }
50 
51 void MPESynthesiser::stopVoice (MPESynthesiserVoice* voice, MPENote noteToStop, bool allowTailOff)
52 {
53  jassert (voice != nullptr);
54 
55  voice->currentlyPlayingNote = noteToStop;
56  voice->noteStopped (allowTailOff);
57 }
58 
59 //==============================================================================
61 {
62  const ScopedLock sl (voicesLock);
63 
64  if (auto* voice = findFreeVoice (newNote, shouldStealVoices))
65  startVoice (voice, newNote);
66 }
67 
69 {
70  const ScopedLock sl (voicesLock);
71 
72  for (auto* voice : voices)
73  {
74  if (voice->isCurrentlyPlayingNote (changedNote))
75  {
76  voice->currentlyPlayingNote = changedNote;
77  voice->notePressureChanged();
78  }
79  }
80 }
81 
83 {
84  const ScopedLock sl (voicesLock);
85 
86  for (auto* voice : voices)
87  {
88  if (voice->isCurrentlyPlayingNote (changedNote))
89  {
90  voice->currentlyPlayingNote = changedNote;
91  voice->notePitchbendChanged();
92  }
93  }
94 }
95 
97 {
98  const ScopedLock sl (voicesLock);
99 
100  for (auto* voice : voices)
101  {
102  if (voice->isCurrentlyPlayingNote (changedNote))
103  {
104  voice->currentlyPlayingNote = changedNote;
105  voice->noteTimbreChanged();
106  }
107  }
108 }
109 
111 {
112  const ScopedLock sl (voicesLock);
113 
114  for (auto* voice : voices)
115  {
116  if (voice->isCurrentlyPlayingNote (changedNote))
117  {
118  voice->currentlyPlayingNote = changedNote;
119  voice->noteKeyStateChanged();
120  }
121  }
122 }
123 
125 {
126  const ScopedLock sl (voicesLock);
127 
128  for (auto i = voices.size(); --i >= 0;)
129  {
130  auto* voice = voices.getUnchecked (i);
131 
132  if (voice->isCurrentlyPlayingNote(finishedNote))
133  stopVoice (voice, finishedNote, true);
134  }
135 }
136 
138 {
140 
141  const ScopedLock sl (voicesLock);
142 
143  turnOffAllVoices (false);
144 
145  for (auto i = voices.size(); --i >= 0;)
146  voices.getUnchecked (i)->setCurrentSampleRate (newRate);
147 }
148 
150 {
151  if (m.isController())
153  else if (m.isProgramChange())
155 
157 }
158 
159 MPESynthesiserVoice* MPESynthesiser::findFreeVoice (MPENote noteToFindVoiceFor, bool stealIfNoneAvailable) const
160 {
161  const ScopedLock sl (voicesLock);
162 
163  for (auto* voice : voices)
164  {
165  if (! voice->isActive())
166  return voice;
167  }
168 
169  if (stealIfNoneAvailable)
170  return findVoiceToSteal (noteToFindVoiceFor);
171 
172  return nullptr;
173 }
174 
176 {
177  // This voice-stealing algorithm applies the following heuristics:
178  // - Re-use the oldest notes first
179  // - Protect the lowest & topmost notes, even if sustained, but not if they've been released.
180 
181 
182  // apparently you are trying to render audio without having any voices...
183  jassert (voices.size() > 0);
184 
185  // These are the voices we want to protect (ie: only steal if unavoidable)
186  MPESynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
187  MPESynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase
188 
189  // this is a list of voices we can steal, sorted by how long they've been running
190  Array<MPESynthesiserVoice*> usableVoices;
191  usableVoices.ensureStorageAllocated (voices.size());
192 
193  for (auto* voice : voices)
194  {
195  jassert (voice->isActive()); // We wouldn't be here otherwise
196 
197  usableVoices.add (voice);
198 
199  // NB: Using a functor rather than a lambda here due to scare-stories about
200  // compilers generating code containing heap allocations..
201  struct Sorter
202  {
203  bool operator() (const MPESynthesiserVoice* a, const MPESynthesiserVoice* b) const noexcept { return a->noteOnTime < b->noteOnTime; }
204  };
205 
206  std::sort (usableVoices.begin(), usableVoices.end(), Sorter());
207 
208  if (! voice->isPlayingButReleased()) // Don't protect released notes
209  {
210  auto noteNumber = voice->getCurrentlyPlayingNote().initialNote;
211 
212  if (low == nullptr || noteNumber < low->getCurrentlyPlayingNote().initialNote)
213  low = voice;
214 
215  if (top == nullptr || noteNumber > top->getCurrentlyPlayingNote().initialNote)
216  top = voice;
217  }
218  }
219 
220  // Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s)
221  if (top == low)
222  top = nullptr;
223 
224  // If we want to re-use the voice to trigger a new note,
225  // then The oldest note that's playing the same note number is ideal.
226  if (noteToStealVoiceFor.isValid())
227  for (auto* voice : usableVoices)
228  if (voice->getCurrentlyPlayingNote().initialNote == noteToStealVoiceFor.initialNote)
229  return voice;
230 
231  // Oldest voice that has been released (no finger on it and not held by sustain pedal)
232  for (auto* voice : usableVoices)
233  if (voice != low && voice != top && voice->isPlayingButReleased())
234  return voice;
235 
236  // Oldest voice that doesn't have a finger on it:
237  for (auto* voice : usableVoices)
238  if (voice != low && voice != top
240  && voice->getCurrentlyPlayingNote().keyState != MPENote::keyDownAndSustained)
241  return voice;
242 
243  // Oldest voice that isn't protected
244  for (auto* voice : usableVoices)
245  if (voice != low && voice != top)
246  return voice;
247 
248  // We've only got "protected" voices now: lowest note takes priority
249  jassert (low != nullptr);
250 
251  // Duophonic synth: give priority to the bass note:
252  if (top != nullptr)
253  return top;
254 
255  return low;
256 }
257 
258 //==============================================================================
260 {
261  const ScopedLock sl (voicesLock);
262  newVoice->setCurrentSampleRate (getSampleRate());
263  voices.add (newVoice);
264 }
265 
267 {
268  const ScopedLock sl (voicesLock);
269  voices.clear();
270 }
271 
273 {
274  const ScopedLock sl (voicesLock);
275  return voices [index];
276 }
277 
278 void MPESynthesiser::removeVoice (const int index)
279 {
280  const ScopedLock sl (voicesLock);
281  voices.remove (index);
282 }
283 
284 void MPESynthesiser::reduceNumVoices (const int newNumVoices)
285 {
286  // we can't possibly get to a negative number of voices...
287  jassert (newNumVoices >= 0);
288 
289  const ScopedLock sl (voicesLock);
290 
291  while (voices.size() > newNumVoices)
292  {
293  if (auto* voice = findFreeVoice ({}, true))
294  voices.removeObject (voice);
295  else
296  voices.remove (0); // if there's no voice to steal, kill the oldest voice
297  }
298 }
299 
300 void MPESynthesiser::turnOffAllVoices (bool allowTailOff)
301 {
302  {
303  const ScopedLock sl (voicesLock);
304 
305  // first turn off all voices (it's more efficient to do this immediately
306  // rather than to go through the MPEInstrument for this).
307  for (auto* voice : voices)
308  voice->noteStopped (allowTailOff);
309  }
310 
311  // finally make sure the MPE Instrument also doesn't have any notes anymore.
312  instrument->releaseAllNotes();
313 }
314 
315 //==============================================================================
316 void MPESynthesiser::renderNextSubBlock (AudioBuffer<float>& buffer, int startSample, int numSamples)
317 {
318  const ScopedLock sl (voicesLock);
319 
320  for (auto* voice : voices)
321  {
322  if (voice->isActive())
323  voice->renderNextBlock (buffer, startSample, numSamples);
324  }
325 }
326 
327 void MPESynthesiser::renderNextSubBlock (AudioBuffer<double>& buffer, int startSample, int numSamples)
328 {
329  const ScopedLock sl (voicesLock);
330 
331  for (auto* voice : voices)
332  {
333  if (voice->isActive())
334  voice->renderNextBlock (buffer, startSample, numSamples);
335  }
336 }
337 
338 } // namespace juce
void ensureStorageAllocated(int minNumElements)
Definition: juce_Array.h:1062
ElementType * end() noexcept
Definition: juce_Array.h:344
ElementType * begin() noexcept
Definition: juce_Array.h:328
void add(const ElementType &newElement)
Definition: juce_Array.h:418
virtual void setCurrentSampleRate(double newRate)
MPENote getCurrentlyPlayingNote() const noexcept
virtual void noteStarted()=0
virtual void noteStopped(bool allowTailOff)=0
bool isPlayingButReleased() const noexcept
void reduceNumVoices(int newNumVoices)
virtual MPESynthesiserVoice * findFreeVoice(MPENote noteToFindVoiceFor, bool stealIfNoneAvailable) const
void stopVoice(MPESynthesiserVoice *voice, MPENote noteToStop, bool allowTailOff)
void setCurrentPlaybackSampleRate(double newRate) override
void startVoice(MPESynthesiserVoice *voice, MPENote noteToStart)
void notePressureChanged(MPENote changedNote) override
void noteReleased(MPENote finishedNote) override
void addVoice(MPESynthesiserVoice *newVoice)
virtual MPESynthesiserVoice * findVoiceToSteal(MPENote noteToStealVoiceFor=MPENote()) const
void noteTimbreChanged(MPENote changedNote) override
void noteAdded(MPENote newNote) override
MPESynthesiserVoice * getVoice(int index) const
void noteKeyStateChanged(MPENote changedNote) override
virtual void turnOffAllVoices(bool allowTailOff)
virtual void handleProgramChange(int, int)
void renderNextSubBlock(AudioBuffer< float > &outputAudio, int startSample, int numSamples) override
void notePitchbendChanged(MPENote changedNote) override
void handleMidiEvent(const MidiMessage &) override
virtual void handleController(int, int, int)
void setLowerZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2) noexcept
int getChannel() const noexcept
bool isProgramChange() const noexcept
bool isController() const noexcept
int getControllerNumber() const noexcept
int getProgramChangeNumber() const noexcept
int getControllerValue() const noexcept
KeyState keyState
Definition: juce_MPENote.h:169
bool isValid() const noexcept
virtual void handleMidiEvent(const MidiMessage &)
void setZoneLayout(MPEZoneLayout newLayout)
std::unique_ptr< MPEInstrument > instrument
virtual void setCurrentPlaybackSampleRate(double sampleRate)
double getSampleRate() const noexcept