OpenShot Library | libopenshot-audio  0.2.0
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  // first turn off all voices (it's more efficient to do this immediately
303  // rather than to go through the MPEInstrument for this).
304  for (auto* voice : voices)
305  voice->noteStopped (allowTailOff);
306 
307  // finally make sure the MPE Instrument also doesn't have any notes anymore.
308  instrument->releaseAllNotes();
309 }
310 
311 //==============================================================================
312 void MPESynthesiser::renderNextSubBlock (AudioBuffer<float>& buffer, int startSample, int numSamples)
313 {
314  for (auto* voice : voices)
315  {
316  if (voice->isActive())
317  voice->renderNextBlock (buffer, startSample, numSamples);
318  }
319 }
320 
321 void MPESynthesiser::renderNextSubBlock (AudioBuffer<double>& buffer, int startSample, int numSamples)
322 {
323  for (auto* voice : voices)
324  {
325  if (voice->isActive())
326  voice->renderNextBlock (buffer, startSample, numSamples);
327  }
328 }
329 
330 } // namespace juce
juce::MPESynthesiserVoice::noteOnTime
uint32 noteOnTime
This will be set to an incrementing counter value in MPESynthesiser::startVoice() and can be used to ...
Definition: juce_MPESynthesiserVoice.h:166
juce::MPESynthesiser::clearVoices
void clearVoices()
Deletes all voices.
Definition: juce_MPESynthesiser.cpp:266
juce::MPESynthesiser::getVoice
MPESynthesiserVoice * getVoice(int index) const
Returns one of the voices that have been added.
Definition: juce_MPESynthesiser.cpp:272
juce::MidiMessage::getControllerValue
int getControllerValue() const noexcept
Returns the controller value from a controller message.
Definition: juce_MidiMessage.cpp:560
juce::MPESynthesiser::noteAdded
void noteAdded(MPENote newNote) override
Attempts to start playing a new note.
Definition: juce_MPESynthesiser.cpp:60
juce::MPESynthesiser::addVoice
void addVoice(MPESynthesiserVoice *newVoice)
Adds a new voice to the synth.
Definition: juce_MPESynthesiser.cpp:259
juce::MPESynthesiser::~MPESynthesiser
~MPESynthesiser() override
Destructor.
Definition: juce_MPESynthesiser.cpp:37
juce::MPENote::keyDown
@ keyDown
The note key is currently down (pressed).
Definition: juce_MPENote.h:50
juce::AudioBuffer< float >
juce::MidiMessage::isProgramChange
bool isProgramChange() const noexcept
Returns true if the message is a program (patch) change message.
Definition: juce_MidiMessage.cpp:504
juce::MPEInstrument
This class represents an instrument handling MPE.
Definition: juce_MPEInstrument.h:59
juce::MPESynthesiser::noteKeyStateChanged
void noteKeyStateChanged(MPENote changedNote) override
Will find any voice that is currently playing changedNote, update its currently playing note,...
Definition: juce_MPESynthesiser.cpp:110
juce::MPESynthesiser::MPESynthesiser
MPESynthesiser()
Constructor.
Definition: juce_MPESynthesiser.cpp:26
juce::MPESynthesiserBase::instrument
std::unique_ptr< MPEInstrument > instrument
Definition: juce_MPESynthesiserBase.h:202
juce::Array::add
void add(const ElementType &newElement)
Appends a new element at the end of the array.
Definition: juce_Array.h:375
juce::MPENote::isValid
bool isValid() const noexcept
Checks whether the MPE note is valid.
Definition: juce_MPENote.cpp:62
juce::MPESynthesiser::findVoiceToSteal
virtual MPESynthesiserVoice * findVoiceToSteal(MPENote noteToStealVoiceFor=MPENote()) const
Chooses a voice that is most suitable for being re-used to play a new note, or for being deleted by r...
Definition: juce_MPESynthesiser.cpp:175
juce::Array::begin
ElementType * begin() const noexcept
Returns a pointer to the first element in the array.
Definition: juce_Array.h:309
juce::Array
Holds a resizable array of primitive or copy-by-value objects.
Definition: juce_Array.h:59
juce::MPESynthesiser::handleController
virtual void handleController(int, int, int)
Callback for MIDI controller messages.
Definition: juce_MPESynthesiser.h:167
juce::MPESynthesiser::setCurrentPlaybackSampleRate
void setCurrentPlaybackSampleRate(double newRate) override
Tells the synthesiser what the sample rate is for the audio it's being used to render.
Definition: juce_MPESynthesiser.cpp:137
juce::MPESynthesiserBase::handleMidiEvent
virtual void handleMidiEvent(const MidiMessage &)
Handle incoming MIDI events (called from renderNextBlock).
Definition: juce_MPESynthesiserBase.cpp:98
juce::Array::end
ElementType * end() const noexcept
Returns a pointer to the element which follows the last element in the array.
Definition: juce_Array.h:317
juce::MPENote
This struct represents a playing MPE note.
Definition: juce_MPENote.h:43
juce::MPESynthesiserBase::setCurrentPlaybackSampleRate
virtual void setCurrentPlaybackSampleRate(double sampleRate)
Tells the synthesiser what the sample rate is for the audio it's being used to render.
Definition: juce_MPESynthesiserBase.cpp:162
juce::MPESynthesiser::noteReleased
void noteReleased(MPENote finishedNote) override
Stops playing a note.
Definition: juce_MPESynthesiser.cpp:124
juce::MPESynthesiser::handleProgramChange
virtual void handleProgramChange(int, int)
Callback for MIDI program change messages.
Definition: juce_MPESynthesiser.h:178
juce::MPEZoneLayout
This class represents the current MPE zone layout of a device capable of handling MPE.
Definition: juce_MPEZoneLayout.h:48
juce::MPENote::initialNote
uint8 initialNote
The MIDI note number that was sent when the note was triggered.
Definition: juce_MPENote.h:112
juce::MPESynthesiser::noteTimbreChanged
void noteTimbreChanged(MPENote changedNote) override
Will find any voice that is currently playing changedNote, update its currently playing note,...
Definition: juce_MPESynthesiser.cpp:96
juce::MPESynthesiserBase::setZoneLayout
void setZoneLayout(MPEZoneLayout newLayout)
Re-sets the synthesiser's internal MPE zone layout to the one passed in.
Definition: juce_MPESynthesiserBase.cpp:45
juce::MPESynthesiserBase::getSampleRate
double getSampleRate() const noexcept
Returns the current target sample rate at which rendering is being done.
Definition: juce_MPESynthesiserBase.h:88
juce::MPESynthesiserVoice
Represents an MPE voice that an MPESynthesiser can use to play a sound.
Definition: juce_MPESynthesiserVoice.h:41
juce::MPESynthesiser::findFreeVoice
virtual MPESynthesiserVoice * findFreeVoice(MPENote noteToFindVoiceFor, bool stealIfNoneAvailable) const
Searches through the voices to find one that's not currently playing, and which can play the given MP...
Definition: juce_MPESynthesiser.cpp:159
juce::MPESynthesiser::notePressureChanged
void notePressureChanged(MPENote changedNote) override
Will find any voice that is currently playing changedNote, update its currently playing note,...
Definition: juce_MPESynthesiser.cpp:68
juce::MPENote::keyState
KeyState keyState
Current key state.
Definition: juce_MPENote.h:173
juce::MPESynthesiser::renderNextSubBlock
void renderNextSubBlock(AudioBuffer< float > &outputAudio, int startSample, int numSamples) override
This will simply call renderNextBlock for each currently active voice and fill the buffer with the su...
Definition: juce_MPESynthesiser.cpp:312
juce::MPESynthesiser::handleMidiEvent
void handleMidiEvent(const MidiMessage &) override
Handle incoming MIDI events.
Definition: juce_MPESynthesiser.cpp:149
juce::MPENote::keyDownAndSustained
@ keyDownAndSustained
The note key is down and sustained (by a sustain or sostenuto pedal).
Definition: juce_MPENote.h:52
juce::MidiMessage
Encapsulates a MIDI message.
Definition: juce_MidiMessage.h:38
juce::MPESynthesiser::reduceNumVoices
void reduceNumVoices(int newNumVoices)
Reduces the number of voices to newNumVoices.
Definition: juce_MPESynthesiser.cpp:284
juce::GenericScopedLock
Automatically locks and unlocks a mutex object.
Definition: juce_ScopedLock.h:58
juce::Array::ensureStorageAllocated
void ensureStorageAllocated(int minNumElements)
Increases the array's internal storage to hold a minimum number of elements.
Definition: juce_Array.h:1019
juce::MPESynthesiserVoice::getCurrentlyPlayingNote
MPENote getCurrentlyPlayingNote() const noexcept
Returns the MPENote that this voice is currently playing.
Definition: juce_MPESynthesiserVoice.h:55
juce::MPESynthesiser::stopVoice
void stopVoice(MPESynthesiserVoice *voice, MPENote noteToStop, bool allowTailOff)
Stops a given voice and tells it to stop playing a particular MPENote (which should be the same note ...
Definition: juce_MPESynthesiser.cpp:51
juce::MPESynthesiserBase
Derive from this class to create a basic audio generator capable of MPE.
Definition: juce_MPESynthesiserBase.h:50
juce::MPESynthesiserVoice::noteStopped
virtual void noteStopped(bool allowTailOff)=0
Called by the MPESynthesiser to let the voice know that its currently playing note has stopped.
juce::MidiMessage::getProgramChangeNumber
int getProgramChangeNumber() const noexcept
Returns the new program number of a program change message.
Definition: juce_MidiMessage.cpp:509
juce::MPESynthesiserVoice::isPlayingButReleased
bool isPlayingButReleased() const noexcept
Returns true if a voice is sounding in its release phase.
Definition: juce_MPESynthesiserVoice.cpp:40
juce::MidiMessage::isController
bool isController() const noexcept
Returns true if this is a midi controller message.
Definition: juce_MidiMessage.cpp:543
juce::MPEZoneLayout::setLowerZone
void setLowerZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2) noexcept
Sets the lower zone of this layout.
Definition: juce_MPEZoneLayout.cpp:77
juce::MPESynthesiser::startVoice
void startVoice(MPESynthesiserVoice *voice, MPENote noteToStart)
Starts a specified voice and tells it to play a particular MPENote.
Definition: juce_MPESynthesiser.cpp:42
juce::MidiMessage::getChannel
int getChannel() const noexcept
Returns the midi channel associated with the message.
Definition: juce_MidiMessage.cpp:359
juce::MPESynthesiserVoice::noteStarted
virtual void noteStarted()=0
Called by the MPESynthesiser to let the voice know that a new note has started on it.
juce::MPESynthesiser::turnOffAllVoices
virtual void turnOffAllVoices(bool allowTailOff)
Release all MPE notes and turn off all voices.
Definition: juce_MPESynthesiser.cpp:300
juce::MPESynthesiserVoice::setCurrentSampleRate
virtual void setCurrentSampleRate(double newRate)
Changes the voice's reference sample rate.
Definition: juce_MPESynthesiserVoice.h:156
juce::MPESynthesiser::removeVoice
void removeVoice(int index)
Deletes one of the voices.
Definition: juce_MPESynthesiser.cpp:278
juce::MPESynthesiser::notePitchbendChanged
void notePitchbendChanged(MPENote changedNote) override
Will find any voice that is currently playing changedNote, update its currently playing note,...
Definition: juce_MPESynthesiser.cpp:82
juce::MidiMessage::getControllerNumber
int getControllerNumber() const noexcept
Returns the controller number of a controller message.
Definition: juce_MidiMessage.cpp:554