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