OpenShot Audio Library | OpenShotAudio 0.3.2
Loading...
Searching...
No Matches
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
23namespace juce
24{
25
27{
28 MPEZoneLayout zoneLayout;
29 zoneLayout.setLowerZone (15);
30 setZoneLayout (zoneLayout);
31}
32
36
40
41//==============================================================================
43{
44 jassert (voice != nullptr);
45
46 voice->currentlyPlayingNote = noteToStart;
47 voice->noteOnTime = lastNoteOnCounter++;
48 voice->noteStarted();
49}
50
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))
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))
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
158
160{
161 const ScopedLock sl (voicesLock);
162
163 for (auto* voice : voices)
164 {
165 if (! voice->isActive())
166 return voice;
167 }
168
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
192
193 for (auto* voice : voices)
194 {
195 jassert (voice->isActive()); // We wouldn't be here otherwise
196
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//==============================================================================
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
278void MPESynthesiser::removeVoice (const int index)
279{
280 const ScopedLock sl (voicesLock);
281 voices.remove (index);
282}
283
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
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//==============================================================================
316void 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
327void 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
ElementType getUnchecked(int index) const
Definition juce_Array.h:252
void ensureStorageAllocated(int minNumElements)
int size() const noexcept
Definition juce_Array.h:215
void remove(int indexToRemove)
Definition juce_Array.h:767
ElementType * begin() noexcept
Definition juce_Array.h:328
ElementType * end() noexcept
Definition juce_Array.h:344
void add(const ElementType &newElement)
Definition juce_Array.h:418
void clear()
Definition juce_Array.h:188
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
virtual void handleMidiEvent(const MidiMessage &)
void setZoneLayout(MPEZoneLayout newLayout)
std::unique_ptr< MPEInstrument > instrument
virtual void setCurrentPlaybackSampleRate(double sampleRate)
double getSampleRate() const noexcept