2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
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.
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
20 ==============================================================================
26 //==============================================================================
28 Base class for an MPE-compatible musical device that can play sounds.
30 This class extends MPESynthesiserBase by adding the concept of voices,
31 each of which can play a sound triggered by a MPENote that can be modulated
32 by MPE dimensions like pressure, pitchbend, and timbre, while the note is
35 To create a synthesiser, you'll need to create a subclass of MPESynthesiserVoice
36 which can play back one of these sounds at a time.
38 Then you can use the addVoice() methods to give the synthesiser a set of voices
39 it can use to play notes. If you only give it one voice it will be monophonic -
40 the more voices it has, the more polyphony it'll have available.
42 Then repeatedly call the renderNextBlock() method to produce the audio (inherited
43 from MPESynthesiserBase). The voices will be started, stopped, and modulated
44 automatically, based on the MPE/MIDI messages that the synthesiser receives.
46 Before rendering, be sure to call the setCurrentPlaybackSampleRate() to tell it
47 what the target playback rate is. This value is passed on to the voices so that
48 they can pitch their output correctly.
50 @see MPESynthesiserBase, MPESynthesiserVoice, MPENote, MPEInstrument
54 class JUCE_API MPESynthesiser
: public MPESynthesiserBase
57 //==============================================================================
59 You'll need to add some voices before it'll make any sound.
65 /** Constructor to pass to the synthesiser a custom MPEInstrument object
66 to handle the MPE note state, MIDI channel assignment etc.
67 (in case you need custom logic for this that goes beyond MIDI and MPE).
69 @see MPESynthesiserBase, MPEInstrument
71 MPESynthesiser (MPEInstrument
& instrumentToUse
);
74 ~MPESynthesiser() override
;
76 //==============================================================================
77 /** Deletes all voices. */
80 /** Returns the number of voices that have been added. */
81 int getNumVoices() const noexcept
{ return voices
.size(); }
83 /** Returns one of the voices that have been added. */
84 MPESynthesiserVoice
* getVoice (int index
) const;
86 /** Adds a new voice to the synth.
88 All the voices should be the same class of object and are treated equally.
90 The object passed in will be managed by the synthesiser, which will delete
91 it later on when no longer needed. The caller should not retain a pointer to the
94 void addVoice (MPESynthesiserVoice
* newVoice
);
96 /** Deletes one of the voices. */
97 void removeVoice (int index
);
99 /** Reduces the number of voices to newNumVoices.
101 This will repeatedly call findVoiceToSteal() and remove that voice, until
102 the total number of voices equals newNumVoices. If newNumVoices is greater than
103 or equal to the current number of voices, this method does nothing.
105 void reduceNumVoices (int newNumVoices
);
107 /** Release all MPE notes and turn off all voices.
109 If allowTailOff is true, the voices will be allowed to fade out the notes gracefully
110 (if they can do). If this is false, the notes will all be cut off immediately.
112 This method is meant to be called by the user, for example to implement
113 a MIDI panic button in a synth.
115 virtual void turnOffAllVoices (bool allowTailOff
);
117 //==============================================================================
118 /** If set to true, then the synth will try to take over an existing voice if
119 it runs out and needs to play another note.
121 The value of this boolean is passed into findFreeVoice(), so the result will
122 depend on the implementation of this method.
124 void setVoiceStealingEnabled (bool shouldSteal
) noexcept
{ shouldStealVoices
= shouldSteal
; }
126 /** Returns true if note-stealing is enabled. */
127 bool isVoiceStealingEnabled() const noexcept
{ return shouldStealVoices
; }
129 //==============================================================================
130 /** Tells the synthesiser what the sample rate is for the audio it's being used to render.
132 This overrides the implementation in MPESynthesiserBase, to additionally
133 propagate the new value to the voices so that they can use it to render the correct
136 void setCurrentPlaybackSampleRate (double newRate
) override
;
138 //==============================================================================
139 /** Handle incoming MIDI events.
141 This method will be called automatically according to the MIDI data passed
142 into renderNextBlock(), but you can also call it yourself to manually
145 This implementation forwards program change messages and non-MPE-related
146 controller messages to handleProgramChange and handleController, respectively,
147 and then simply calls through to MPESynthesiserBase::handleMidiEvent to deal
148 with MPE-related MIDI messages used for MPE notes, zones etc.
150 This method can be overridden further if you need to do custom MIDI
151 handling on top of what is provided here.
153 void handleMidiEvent (const MidiMessage
&) override
;
155 /** Callback for MIDI controller messages. The default implementation
156 provided here does nothing; override this method if you need custom
157 MIDI controller handling on top of MPE.
159 This method will be called automatically according to the midi data passed into
162 virtual void handleController (int /*midiChannel*/,
163 int /*controllerNumber*/,
164 int /*controllerValue*/) {}
166 /** Callback for MIDI program change messages. The default implementation
167 provided here does nothing; override this method if you need to handle
170 This method will be called automatically according to the midi data passed into
173 virtual void handleProgramChange (int /*midiChannel*/,
174 int /*programNumber*/) {}
177 //==============================================================================
178 /** Attempts to start playing a new note.
180 The default method here will find a free voice that is appropriate for
181 playing the given MPENote, and use that voice to start playing the sound.
182 If isNoteStealingEnabled returns true (set this by calling setNoteStealingEnabled),
183 the synthesiser will use the voice stealing algorithm to find a free voice for
184 the note (if no voices are free otherwise).
186 This method will be called automatically according to the midi data passed into
187 renderNextBlock(). Do not call it yourself, otherwise the internal MPE note state
188 will become inconsistent.
190 void noteAdded (MPENote newNote
) override
;
192 /** Stops playing a note.
194 This will be called whenever an MPE note is released (either by a note-off message,
195 or by a sustain/sostenuto pedal release for a note that already received a note-off),
196 and should therefore stop playing.
198 This will find any voice that is currently playing finishedNote,
199 turn its currently playing note off, and call its noteStopped callback.
201 This method will be called automatically according to the midi data passed into
202 renderNextBlock(). Do not call it yourself, otherwise the internal MPE note state
203 will become inconsistent.
205 void noteReleased (MPENote finishedNote
) override
;
207 /** Will find any voice that is currently playing changedNote, update its
208 currently playing note, and call its notePressureChanged method.
210 This method will be called automatically according to the midi data passed into
211 renderNextBlock(). Do not call it yourself.
213 void notePressureChanged (MPENote changedNote
) override
;
215 /** Will find any voice that is currently playing changedNote, update its
216 currently playing note, and call its notePitchbendChanged method.
218 This method will be called automatically according to the midi data passed into
219 renderNextBlock(). Do not call it yourself.
221 void notePitchbendChanged (MPENote changedNote
) override
;
223 /** Will find any voice that is currently playing changedNote, update its
224 currently playing note, and call its noteTimbreChanged method.
226 This method will be called automatically according to the midi data passed into
227 renderNextBlock(). Do not call it yourself.
229 void noteTimbreChanged (MPENote changedNote
) override
;
231 /** Will find any voice that is currently playing changedNote, update its
232 currently playing note, and call its noteKeyStateChanged method.
234 This method will be called automatically according to the midi data passed into
235 renderNextBlock(). Do not call it yourself.
237 void noteKeyStateChanged (MPENote changedNote
) override
;
239 //==============================================================================
240 /** This will simply call renderNextBlock for each currently active
241 voice and fill the buffer with the sum.
242 Override this method if you need to do more work to render your audio.
244 void renderNextSubBlock (AudioBuffer
<float>& outputAudio
,
246 int numSamples
) override
;
248 /** This will simply call renderNextBlock for each currently active
249 voice and fill the buffer with the sum. (double-precision version)
250 Override this method if you need to do more work to render your audio.
252 void renderNextSubBlock (AudioBuffer
<double>& outputAudio
,
254 int numSamples
) override
;
256 //==============================================================================
257 /** Searches through the voices to find one that's not currently playing, and
258 which can play the given MPE note.
260 If all voices are active and stealIfNoneAvailable is false, this returns
261 a nullptr. If all voices are active and stealIfNoneAvailable is true,
262 this will call findVoiceToSteal() to find a voice.
264 If you need to find a free voice for something else than playing a note
265 (e.g. for deleting it), you can pass an invalid (default-constructed) MPENote.
267 virtual MPESynthesiserVoice
* findFreeVoice (MPENote noteToFindVoiceFor
,
268 bool stealIfNoneAvailable
) const;
270 /** Chooses a voice that is most suitable for being re-used to play a new
271 note, or for being deleted by reduceNumVoices.
273 The default method will attempt to find the oldest voice that isn't the
274 bottom or top note being played. If that's not suitable for your synth,
275 you can override this method and do something more cunning instead.
277 If you pass a valid MPENote for the optional argument, then the note number
278 of that note will be taken into account for finding the ideal voice to steal.
279 If you pass an invalid (default-constructed) MPENote instead, this part of
280 the algorithm will be ignored.
282 virtual MPESynthesiserVoice
* findVoiceToSteal (MPENote noteToStealVoiceFor
= MPENote()) const;
284 /** Starts a specified voice and tells it to play a particular MPENote.
285 You should never need to call this, it's called internally by
286 MPESynthesiserBase::instrument via the noteStarted callback,
287 but is protected in case it's useful for some custom subclasses.
289 void startVoice (MPESynthesiserVoice
* voice
, MPENote noteToStart
);
291 /** Stops a given voice and tells it to stop playing a particular MPENote
292 (which should be the same note it is actually playing).
293 You should never need to call this, it's called internally by
294 MPESynthesiserBase::instrument via the noteReleased callback,
295 but is protected in case it's useful for some custom subclasses.
297 void stopVoice (MPESynthesiserVoice
* voice
, MPENote noteToStop
, bool allowTailOff
);
299 //==============================================================================
300 OwnedArray
<MPESynthesiserVoice
> voices
;
301 CriticalSection voicesLock
;
304 //==============================================================================
305 std::atomic
<bool> shouldStealVoices
{ false };
306 uint32 lastNoteOnCounter
= 0;
308 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiser
)