OpenShot Library | libopenshot-audio 0.2.0
juce_Synthesiser.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
26SynthesiserSound::SynthesiserSound() {}
28
29//==============================================================================
32
33bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const
34{
35 return currentPlayingMidiChannel == midiChannel;
36}
37
39{
40 currentSampleRate = newRate;
41}
42
44{
45 return getCurrentlyPlayingNote() >= 0;
46}
47
49{
50 currentlyPlayingNote = -1;
51 currentlyPlayingSound = nullptr;
52 currentPlayingMidiChannel = 0;
53}
54
57
58bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept
59{
60 return noteOnTime < other.noteOnTime;
61}
62
64 int startSample, int numSamples)
65{
66 AudioBuffer<double> subBuffer (outputBuffer.getArrayOfWritePointers(),
67 outputBuffer.getNumChannels(),
68 startSample, numSamples);
69
70 tempBuffer.makeCopyOf (subBuffer, true);
71 renderNextBlock (tempBuffer, 0, numSamples);
72 subBuffer.makeCopyOf (tempBuffer, true);
73}
74
75//==============================================================================
77{
78 for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i)
79 lastPitchWheelValues[i] = 0x2000;
80}
81
83{
84}
85
86//==============================================================================
88{
89 const ScopedLock sl (lock);
90 return voices [index];
91}
92
94{
95 const ScopedLock sl (lock);
96 voices.clear();
97}
98
100{
101 const ScopedLock sl (lock);
102 newVoice->setCurrentPlaybackSampleRate (sampleRate);
103 return voices.add (newVoice);
104}
105
106void Synthesiser::removeVoice (const int index)
107{
108 const ScopedLock sl (lock);
109 voices.remove (index);
110}
111
113{
114 const ScopedLock sl (lock);
115 sounds.clear();
116}
117
119{
120 const ScopedLock sl (lock);
121 return sounds.add (newSound);
122}
123
124void Synthesiser::removeSound (const int index)
125{
126 const ScopedLock sl (lock);
127 sounds.remove (index);
128}
129
130void Synthesiser::setNoteStealingEnabled (const bool shouldSteal)
131{
132 shouldStealNotes = shouldSteal;
133}
134
135void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict) noexcept
136{
137 jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1
138 minimumSubBlockSize = numSamples;
139 subBlockSubdivisionIsStrict = shouldBeStrict;
140}
141
142//==============================================================================
144{
145 if (sampleRate != newRate)
146 {
147 const ScopedLock sl (lock);
148 allNotesOff (0, false);
149 sampleRate = newRate;
150
151 for (auto* voice : voices)
152 voice->setCurrentPlaybackSampleRate (newRate);
153 }
154}
155
156template <typename floatType>
157void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio,
158 const MidiBuffer& midiData,
159 int startSample,
160 int numSamples)
161{
162 // must set the sample rate before using this!
163 jassert (sampleRate != 0);
164 const int targetChannels = outputAudio.getNumChannels();
165
166 MidiBuffer::Iterator midiIterator (midiData);
167 midiIterator.setNextSamplePosition (startSample);
168
169 bool firstEvent = true;
170 int midiEventPos;
171 MidiMessage m;
172
173 const ScopedLock sl (lock);
174
175 while (numSamples > 0)
176 {
177 if (! midiIterator.getNextEvent (m, midiEventPos))
178 {
179 if (targetChannels > 0)
180 renderVoices (outputAudio, startSample, numSamples);
181
182 return;
183 }
184
185 const int samplesToNextMidiMessage = midiEventPos - startSample;
186
187 if (samplesToNextMidiMessage >= numSamples)
188 {
189 if (targetChannels > 0)
190 renderVoices (outputAudio, startSample, numSamples);
191
192 handleMidiEvent (m);
193 break;
194 }
195
196 if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize))
197 {
198 handleMidiEvent (m);
199 continue;
200 }
201
202 firstEvent = false;
203
204 if (targetChannels > 0)
205 renderVoices (outputAudio, startSample, samplesToNextMidiMessage);
206
207 handleMidiEvent (m);
208 startSample += samplesToNextMidiMessage;
209 numSamples -= samplesToNextMidiMessage;
210 }
211
212 while (midiIterator.getNextEvent (m, midiEventPos))
213 handleMidiEvent (m);
214}
215
216// explicit template instantiation
217template void Synthesiser::processNextBlock<float> (AudioBuffer<float>&, const MidiBuffer&, int, int);
218template void Synthesiser::processNextBlock<double> (AudioBuffer<double>&, const MidiBuffer&, int, int);
219
220void Synthesiser::renderNextBlock (AudioBuffer<float>& outputAudio, const MidiBuffer& inputMidi,
221 int startSample, int numSamples)
222{
223 processNextBlock (outputAudio, inputMidi, startSample, numSamples);
224}
225
226void Synthesiser::renderNextBlock (AudioBuffer<double>& outputAudio, const MidiBuffer& inputMidi,
227 int startSample, int numSamples)
228{
229 processNextBlock (outputAudio, inputMidi, startSample, numSamples);
230}
231
232void Synthesiser::renderVoices (AudioBuffer<float>& buffer, int startSample, int numSamples)
233{
234 for (auto* voice : voices)
235 voice->renderNextBlock (buffer, startSample, numSamples);
236}
237
238void Synthesiser::renderVoices (AudioBuffer<double>& buffer, int startSample, int numSamples)
239{
240 for (auto* voice : voices)
241 voice->renderNextBlock (buffer, startSample, numSamples);
242}
243
245{
246 const int channel = m.getChannel();
247
248 if (m.isNoteOn())
249 {
250 noteOn (channel, m.getNoteNumber(), m.getFloatVelocity());
251 }
252 else if (m.isNoteOff())
253 {
254 noteOff (channel, m.getNoteNumber(), m.getFloatVelocity(), true);
255 }
256 else if (m.isAllNotesOff() || m.isAllSoundOff())
257 {
258 allNotesOff (channel, true);
259 }
260 else if (m.isPitchWheel())
261 {
262 const int wheelPos = m.getPitchWheelValue();
263 lastPitchWheelValues [channel - 1] = wheelPos;
264 handlePitchWheel (channel, wheelPos);
265 }
266 else if (m.isAftertouch())
267 {
269 }
270 else if (m.isChannelPressure())
271 {
273 }
274 else if (m.isController())
275 {
277 }
278 else if (m.isProgramChange())
279 {
281 }
282}
283
284//==============================================================================
285void Synthesiser::noteOn (const int midiChannel,
286 const int midiNoteNumber,
287 const float velocity)
288{
289 const ScopedLock sl (lock);
290
291 for (auto* sound : sounds)
292 {
293 if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel))
294 {
295 // If hitting a note that's still ringing, stop it first (it could be
296 // still playing because of the sustain or sostenuto pedal).
297 for (auto* voice : voices)
298 if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel))
299 stopVoice (voice, 1.0f, true);
300
301 startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
302 sound, midiChannel, midiNoteNumber, velocity);
303 }
304 }
305}
306
308 SynthesiserSound* const sound,
309 const int midiChannel,
310 const int midiNoteNumber,
311 const float velocity)
312{
313 if (voice != nullptr && sound != nullptr)
314 {
315 if (voice->currentlyPlayingSound != nullptr)
316 voice->stopNote (0.0f, false);
317
318 voice->currentlyPlayingNote = midiNoteNumber;
319 voice->currentPlayingMidiChannel = midiChannel;
320 voice->noteOnTime = ++lastNoteOnCounter;
321 voice->currentlyPlayingSound = sound;
322 voice->setKeyDown (true);
323 voice->setSostenutoPedalDown (false);
324 voice->setSustainPedalDown (sustainPedalsDown[midiChannel]);
325
326 voice->startNote (midiNoteNumber, velocity, sound,
327 lastPitchWheelValues [midiChannel - 1]);
328 }
329}
330
331void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool allowTailOff)
332{
333 jassert (voice != nullptr);
334
335 voice->stopNote (velocity, allowTailOff);
336
337 // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
338 jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == nullptr));
339}
340
341void Synthesiser::noteOff (const int midiChannel,
342 const int midiNoteNumber,
343 const float velocity,
344 const bool allowTailOff)
345{
346 const ScopedLock sl (lock);
347
348 for (auto* voice : voices)
349 {
350 if (voice->getCurrentlyPlayingNote() == midiNoteNumber
351 && voice->isPlayingChannel (midiChannel))
352 {
353 if (auto sound = voice->getCurrentlyPlayingSound())
354 {
355 if (sound->appliesToNote (midiNoteNumber)
356 && sound->appliesToChannel (midiChannel))
357 {
358 jassert (! voice->keyIsDown || voice->isSustainPedalDown() == sustainPedalsDown [midiChannel]);
359
360 voice->setKeyDown (false);
361
362 if (! (voice->isSustainPedalDown() || voice->isSostenutoPedalDown()))
363 stopVoice (voice, velocity, allowTailOff);
364 }
365 }
366 }
367 }
368}
369
370void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
371{
372 const ScopedLock sl (lock);
373
374 for (auto* voice : voices)
375 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
376 voice->stopNote (1.0f, allowTailOff);
377
378 sustainPedalsDown.clear();
379}
380
381void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue)
382{
383 const ScopedLock sl (lock);
384
385 for (auto* voice : voices)
386 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
387 voice->pitchWheelMoved (wheelValue);
388}
389
390void Synthesiser::handleController (const int midiChannel,
391 const int controllerNumber,
392 const int controllerValue)
393{
394 switch (controllerNumber)
395 {
396 case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break;
397 case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break;
398 case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break;
399 default: break;
400 }
401
402 const ScopedLock sl (lock);
403
404 for (auto* voice : voices)
405 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
406 voice->controllerMoved (controllerNumber, controllerValue);
407}
408
409void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue)
410{
411 const ScopedLock sl (lock);
412
413 for (auto* voice : voices)
414 if (voice->getCurrentlyPlayingNote() == midiNoteNumber
415 && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)))
416 voice->aftertouchChanged (aftertouchValue);
417}
418
419void Synthesiser::handleChannelPressure (int midiChannel, int channelPressureValue)
420{
421 const ScopedLock sl (lock);
422
423 for (auto* voice : voices)
424 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
425 voice->channelPressureChanged (channelPressureValue);
426}
427
428void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
429{
430 jassert (midiChannel > 0 && midiChannel <= 16);
431 const ScopedLock sl (lock);
432
433 if (isDown)
434 {
435 sustainPedalsDown.setBit (midiChannel);
436
437 for (auto* voice : voices)
438 if (voice->isPlayingChannel (midiChannel) && voice->isKeyDown())
439 voice->setSustainPedalDown (true);
440 }
441 else
442 {
443 for (auto* voice : voices)
444 {
445 if (voice->isPlayingChannel (midiChannel))
446 {
447 voice->setSustainPedalDown (false);
448
449 if (! (voice->isKeyDown() || voice->isSostenutoPedalDown()))
450 stopVoice (voice, 1.0f, true);
451 }
452 }
453
454 sustainPedalsDown.clearBit (midiChannel);
455 }
456}
457
458void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
459{
460 jassert (midiChannel > 0 && midiChannel <= 16);
461 const ScopedLock sl (lock);
462
463 for (auto* voice : voices)
464 {
465 if (voice->isPlayingChannel (midiChannel))
466 {
467 if (isDown)
468 voice->setSostenutoPedalDown (true);
469 else if (voice->isSostenutoPedalDown())
470 stopVoice (voice, 1.0f, true);
471 }
472 }
473}
474
475void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/)
476{
477 ignoreUnused (midiChannel);
478 jassert (midiChannel > 0 && midiChannel <= 16);
479}
480
481void Synthesiser::handleProgramChange (int midiChannel, int programNumber)
482{
483 ignoreUnused (midiChannel, programNumber);
484 jassert (midiChannel > 0 && midiChannel <= 16);
485}
486
487//==============================================================================
489 int midiChannel, int midiNoteNumber,
490 const bool stealIfNoneAvailable) const
491{
492 const ScopedLock sl (lock);
493
494 for (auto* voice : voices)
495 if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay))
496 return voice;
497
498 if (stealIfNoneAvailable)
499 return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber);
500
501 return nullptr;
502}
503
505 int /*midiChannel*/, int midiNoteNumber) const
506{
507 // This voice-stealing algorithm applies the following heuristics:
508 // - Re-use the oldest notes first
509 // - Protect the lowest & topmost notes, even if sustained, but not if they've been released.
510
511 // apparently you are trying to render audio without having any voices...
512 jassert (! voices.isEmpty());
513
514 // These are the voices we want to protect (ie: only steal if unavoidable)
515 SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
516 SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase
517
518 // this is a list of voices we can steal, sorted by how long they've been running
519 Array<SynthesiserVoice*> usableVoices;
520 usableVoices.ensureStorageAllocated (voices.size());
521
522 for (auto* voice : voices)
523 {
524 if (voice->canPlaySound (soundToPlay))
525 {
526 jassert (voice->isVoiceActive()); // We wouldn't be here otherwise
527
528 usableVoices.add (voice);
529
530 // NB: Using a functor rather than a lambda here due to scare-stories about
531 // compilers generating code containing heap allocations..
532 struct Sorter
533 {
534 bool operator() (const SynthesiserVoice* a, const SynthesiserVoice* b) const noexcept { return a->wasStartedBefore (*b); }
535 };
536
537 std::sort (usableVoices.begin(), usableVoices.end(), Sorter());
538
539 if (! voice->isPlayingButReleased()) // Don't protect released notes
540 {
541 auto note = voice->getCurrentlyPlayingNote();
542
543 if (low == nullptr || note < low->getCurrentlyPlayingNote())
544 low = voice;
545
546 if (top == nullptr || note > top->getCurrentlyPlayingNote())
547 top = voice;
548 }
549 }
550 }
551
552 // Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s)
553 if (top == low)
554 top = nullptr;
555
556 // The oldest note that's playing with the target pitch is ideal..
557 for (auto* voice : usableVoices)
558 if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
559 return voice;
560
561 // Oldest voice that has been released (no finger on it and not held by sustain pedal)
562 for (auto* voice : usableVoices)
563 if (voice != low && voice != top && voice->isPlayingButReleased())
564 return voice;
565
566 // Oldest voice that doesn't have a finger on it:
567 for (auto* voice : usableVoices)
568 if (voice != low && voice != top && ! voice->isKeyDown())
569 return voice;
570
571 // Oldest voice that isn't protected
572 for (auto* voice : usableVoices)
573 if (voice != low && voice != top)
574 return voice;
575
576 // We've only got "protected" voices now: lowest note takes priority
577 jassert (low != nullptr);
578
579 // Duophonic synth: give priority to the bass note:
580 if (top != nullptr)
581 return top;
582
583 return low;
584}
585
586} // namespace juce
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:60
void ensureStorageAllocated(int minNumElements)
Increases the array's internal storage to hold a minimum number of elements.
ElementType * begin() const noexcept
Returns a pointer to the first element in the array.
Definition juce_Array.h:309
void add(const ElementType &newElement)
Appends a new element at the end of the array.
Definition juce_Array.h:375
ElementType * end() const noexcept
Returns a pointer to the element which follows the last element in the array.
Definition juce_Array.h:317
A multi-channel buffer containing floating point audio samples.
void makeCopyOf(const AudioBuffer< OtherType > &other, bool avoidReallocating=false)
Resizes this buffer to match the given one, and copies all of its content across.
int getNumChannels() const noexcept
Returns the number of channels of audio data that this buffer contains.
Type ** getArrayOfWritePointers() noexcept
Returns an array of pointers to the channels in the buffer.
void clearBit(int bitNumber) noexcept
Clears a particular bit in the number.
void clear() noexcept
Resets the value to 0.
void setBit(int bitNumber)
Sets a specified bit to 1.
Automatically locks and unlocks a mutex object.
Used to iterate through the events in a MidiBuffer.
Holds a sequence of time-stamped midi events.
Encapsulates a MIDI message.
bool isAftertouch() const noexcept
Returns true if the message is an aftertouch event.
bool isNoteOn(bool returnTrueForVelocity0=false) const noexcept
Returns true if this message is a 'key-down' event.
float getFloatVelocity() const noexcept
Returns the velocity of a note-on or note-off message.
int getChannel() const noexcept
Returns the midi channel associated with the message.
bool isProgramChange() const noexcept
Returns true if the message is a program (patch) change message.
bool isController() const noexcept
Returns true if this is a midi controller message.
bool isAllSoundOff() const noexcept
Checks whether this message is an all-sound-off message.
int getControllerNumber() const noexcept
Returns the controller number of a controller message.
int getChannelPressureValue() const noexcept
Returns the pressure from a channel pressure change message.
bool isNoteOff(bool returnTrueForNoteOnVelocity0=true) const noexcept
Returns true if this message is a 'key-up' event.
bool isPitchWheel() const noexcept
Returns true if the message is a pitch-wheel move.
int getNoteNumber() const noexcept
Returns the midi note number for note-on and note-off messages.
int getProgramChangeNumber() const noexcept
Returns the new program number of a program change message.
int getAfterTouchValue() const noexcept
Returns the amount of aftertouch from an aftertouch messages.
int getControllerValue() const noexcept
Returns the controller value from a controller message.
bool isAllNotesOff() const noexcept
Checks whether this message is an all-notes-off message.
bool isChannelPressure() const noexcept
Returns true if the message is a channel-pressure change event.
int getPitchWheelValue() const noexcept
Returns the pitch wheel position from a pitch-wheel move message.
Describes one of the sounds that a Synthesiser can play.
~SynthesiserSound() override
Destructor.
Represents a voice that a Synthesiser can use to play a SynthesiserSound.
virtual void stopNote(float velocity, bool allowTailOff)=0
Called to stop a note.
void setSustainPedalDown(bool isNowDown) noexcept
Modifies the sustain pedal flag.
SynthesiserVoice()
Creates a voice.
virtual void channelPressureChanged(int newChannelPressureValue)
Called to let the voice know that the channel pressure has changed.
void setSostenutoPedalDown(bool isNowDown) noexcept
Modifies the sostenuto pedal flag.
virtual void renderNextBlock(AudioBuffer< float > &outputBuffer, int startSample, int numSamples)=0
Renders the next block of data for this voice.
virtual bool isPlayingChannel(int midiChannel) const
Returns true if the voice is currently playing a sound which is mapped to the given midi channel.
bool isKeyDown() const noexcept
Returns true if the key that triggered this voice is still held down.
void setKeyDown(bool isNowDown) noexcept
Allows you to modify the flag indicating that the key that triggered this voice is still held down.
virtual void setCurrentPlaybackSampleRate(double newRate)
Changes the voice's reference sample rate.
virtual void aftertouchChanged(int newAftertouchValue)
Called to let the voice know that the aftertouch has changed.
int getCurrentlyPlayingNote() const noexcept
Returns the midi note that this voice is currently playing.
virtual void startNote(int midiNoteNumber, float velocity, SynthesiserSound *sound, int currentPitchWheelPosition)=0
Called to start a new note.
void clearCurrentNote()
Resets the state of this voice after a sound has finished playing.
bool wasStartedBefore(const SynthesiserVoice &other) const noexcept
Returns true if this voice started playing its current note before the other voice did.
virtual ~SynthesiserVoice()
Destructor.
bool isPlayingButReleased() const noexcept
Returns true if a voice is sounding in its release phase.
SynthesiserSound::Ptr getCurrentlyPlayingSound() const noexcept
Returns the sound that this voice is currently playing.
virtual bool isVoiceActive() const
Returns true if this voice is currently busy playing a sound.
Synthesiser()
Creates a new synthesiser.
virtual ~Synthesiser()
Destructor.
virtual SynthesiserVoice * findFreeVoice(SynthesiserSound *soundToPlay, int midiChannel, int midiNoteNumber, bool stealIfNoneAvailable) const
Searches through the voices to find one that's not currently playing, and which can play the given so...
virtual void handleProgramChange(int midiChannel, int programNumber)
Can be overridden to handle an incoming program change message.
void removeVoice(int index)
Deletes one of the voices.
void startVoice(SynthesiserVoice *voice, SynthesiserSound *sound, int midiChannel, int midiNoteNumber, float velocity)
Starts a specified voice playing a particular sound.
virtual void handleAftertouch(int midiChannel, int midiNoteNumber, int aftertouchValue)
Sends an aftertouch message.
void renderNextBlock(AudioBuffer< float > &outputAudio, const MidiBuffer &inputMidi, int startSample, int numSamples)
Creates the next block of audio output.
virtual void handleController(int midiChannel, int controllerNumber, int controllerValue)
Sends a midi controller message to any active voices.
void clearSounds()
Deletes all sounds.
virtual SynthesiserVoice * findVoiceToSteal(SynthesiserSound *soundToPlay, int midiChannel, int midiNoteNumber) const
Chooses a voice that is most suitable for being re-used.
virtual void handleSoftPedal(int midiChannel, bool isDown)
Can be overridden to handle soft pedal events.
void removeSound(int index)
Removes and deletes one of the sounds.
virtual void allNotesOff(int midiChannel, bool allowTailOff)
Turns off all notes.
SynthesiserVoice * addVoice(SynthesiserVoice *newVoice)
Adds a new voice to the synth.
void stopVoice(SynthesiserVoice *, float velocity, bool allowTailOff)
Stops a given voice.
SynthesiserSound * addSound(const SynthesiserSound::Ptr &newSound)
Adds a new sound to the synthesiser.
virtual void renderVoices(AudioBuffer< float > &outputAudio, int startSample, int numSamples)
Renders the voices for the given range.
virtual void handleMidiEvent(const MidiMessage &)
Can be overridden to do custom handling of incoming midi events.
void setNoteStealingEnabled(bool shouldStealNotes)
If set to true, then the synth will try to take over an existing voice if it runs out and needs to pl...
virtual void handlePitchWheel(int midiChannel, int wheelValue)
Sends a pitch-wheel message to any active voices.
CriticalSection lock
This is used to control access to the rendering callback and the note trigger methods.
int lastPitchWheelValues[16]
The last pitch-wheel values for each midi channel.
virtual void noteOn(int midiChannel, int midiNoteNumber, float velocity)
Triggers a note-on event.
SynthesiserVoice * getVoice(int index) const
Returns one of the voices that have been added.
virtual void noteOff(int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff)
Triggers a note-off event.
virtual void handleChannelPressure(int midiChannel, int channelPressureValue)
Sends a channel pressure message.
void clearVoices()
Deletes all voices.
virtual void setCurrentPlaybackSampleRate(double sampleRate)
Tells the synthesiser what the sample rate is for the audio it's being used to render.
void setMinimumRenderingSubdivisionSize(int numSamples, bool shouldBeStrict=false) noexcept
Sets a minimum limit on the size to which audio sub-blocks will be divided when rendering.
virtual void handleSustainPedal(int midiChannel, bool isDown)
Handles a sustain pedal event.
virtual void handleSostenutoPedal(int midiChannel, bool isDown)
Handles a sostenuto pedal event.