1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - 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 
MPESynthesiser()26 MPESynthesiser::MPESynthesiser()
27 {
28     MPEZoneLayout zoneLayout;
29     zoneLayout.setLowerZone (15);
30     setZoneLayout (zoneLayout);
31 }
32 
MPESynthesiser(MPEInstrument * mpeInstrument)33 MPESynthesiser::MPESynthesiser (MPEInstrument* mpeInstrument)  : MPESynthesiserBase (mpeInstrument)
34 {
35 }
36 
~MPESynthesiser()37 MPESynthesiser::~MPESynthesiser()
38 {
39 }
40 
41 //==============================================================================
startVoice(MPESynthesiserVoice * voice,MPENote noteToStart)42 void MPESynthesiser::startVoice (MPESynthesiserVoice* voice, MPENote noteToStart)
43 {
44     jassert (voice != nullptr);
45 
46     voice->currentlyPlayingNote = noteToStart;
47     voice->noteOnTime = lastNoteOnCounter++;
48     voice->noteStarted();
49 }
50 
stopVoice(MPESynthesiserVoice * voice,MPENote noteToStop,bool allowTailOff)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 //==============================================================================
noteAdded(MPENote newNote)60 void MPESynthesiser::noteAdded (MPENote newNote)
61 {
62     const ScopedLock sl (voicesLock);
63 
64     if (auto* voice = findFreeVoice (newNote, shouldStealVoices))
65         startVoice (voice, newNote);
66 }
67 
notePressureChanged(MPENote changedNote)68 void MPESynthesiser::notePressureChanged (MPENote changedNote)
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 
notePitchbendChanged(MPENote changedNote)82 void MPESynthesiser::notePitchbendChanged (MPENote changedNote)
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 
noteTimbreChanged(MPENote changedNote)96 void MPESynthesiser::noteTimbreChanged (MPENote changedNote)
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 
noteKeyStateChanged(MPENote changedNote)110 void MPESynthesiser::noteKeyStateChanged (MPENote changedNote)
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 
noteReleased(MPENote finishedNote)124 void MPESynthesiser::noteReleased (MPENote finishedNote)
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 
setCurrentPlaybackSampleRate(const double newRate)137 void MPESynthesiser::setCurrentPlaybackSampleRate (const double newRate)
138 {
139     MPESynthesiserBase::setCurrentPlaybackSampleRate (newRate);
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 
handleMidiEvent(const MidiMessage & m)149 void MPESynthesiser::handleMidiEvent (const MidiMessage& m)
150 {
151     if (m.isController())
152         handleController (m.getChannel(), m.getControllerNumber(), m.getControllerValue());
153     else if (m.isProgramChange())
154         handleProgramChange (m.getChannel(), m.getProgramChangeNumber());
155 
156     MPESynthesiserBase::handleMidiEvent (m);
157 }
158 
findFreeVoice(MPENote noteToFindVoiceFor,bool stealIfNoneAvailable) const159 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 
findVoiceToSteal(MPENote noteToStealVoiceFor) const175 MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceFor) const
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
239              && voice->getCurrentlyPlayingNote().keyState != MPENote::keyDown
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 //==============================================================================
addVoice(MPESynthesiserVoice * const newVoice)259 void MPESynthesiser::addVoice (MPESynthesiserVoice* const newVoice)
260 {
261     const ScopedLock sl (voicesLock);
262     newVoice->setCurrentSampleRate (getSampleRate());
263     voices.add (newVoice);
264 }
265 
clearVoices()266 void MPESynthesiser::clearVoices()
267 {
268     const ScopedLock sl (voicesLock);
269     voices.clear();
270 }
271 
getVoice(const int index) const272 MPESynthesiserVoice* MPESynthesiser::getVoice (const int index) const
273 {
274     const ScopedLock sl (voicesLock);
275     return voices [index];
276 }
277 
removeVoice(const int index)278 void MPESynthesiser::removeVoice (const int index)
279 {
280     const ScopedLock sl (voicesLock);
281     voices.remove (index);
282 }
283 
reduceNumVoices(const int newNumVoices)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 
turnOffAllVoices(bool allowTailOff)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 //==============================================================================
renderNextSubBlock(AudioBuffer<float> & buffer,int startSample,int numSamples)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 
renderNextSubBlock(AudioBuffer<double> & buffer,int startSample,int numSamples)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
339