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