26SynthesiserSound::SynthesiserSound() {}
35 return currentPlayingMidiChannel == midiChannel;
40 currentSampleRate = newRate;
50 currentlyPlayingNote = -1;
51 currentlyPlayingSound =
nullptr;
52 currentPlayingMidiChannel = 0;
60 return noteOnTime < other.noteOnTime;
64 int startSample,
int numSamples)
68 startSample, numSamples);
90 return voices [index];
106 voice = voices.add (newVoice);
111 usableVoicesToStealArray.ensureStorageAllocated (voices.size() + 1);
120 voices.remove (index);
132 return sounds.add (newSound);
138 sounds.remove (index);
143 shouldStealNotes = shouldSteal;
149 minimumSubBlockSize = numSamples;
150 subBlockSubdivisionIsStrict = shouldBeStrict;
160 sampleRate = newRate;
162 for (
auto* voice : voices)
163 voice->setCurrentPlaybackSampleRate (newRate);
167template <
typename floatType>
179 bool firstEvent =
true;
183 for (; numSamples > 0; ++midiIterator)
185 if (midiIterator == midiData.
cend())
187 if (targetChannels > 0)
193 const auto metadata = *midiIterator;
194 const int samplesToNextMidiMessage = metadata.samplePosition - startSample;
196 if (samplesToNextMidiMessage >= numSamples)
198 if (targetChannels > 0)
205 if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize))
213 if (targetChannels > 0)
214 renderVoices (outputAudio, startSample, samplesToNextMidiMessage);
217 startSample += samplesToNextMidiMessage;
218 numSamples -= samplesToNextMidiMessage;
223 [&] (
const MidiMessageMetadata& meta) { handleMidiEvent (meta.getMessage()); });
227template void Synthesiser::processNextBlock<float> (AudioBuffer<float>&,
const MidiBuffer&,
int,
int);
228template void Synthesiser::processNextBlock<double> (AudioBuffer<double>&,
const MidiBuffer&,
int,
int);
231 int startSample,
int numSamples)
233 processNextBlock (outputAudio, inputMidi, startSample, numSamples);
237 int startSample,
int numSamples)
239 processNextBlock (outputAudio, inputMidi, startSample, numSamples);
244 for (
auto* voice : voices)
245 voice->renderNextBlock (buffer, startSample, numSamples);
250 for (
auto* voice : voices)
296 const int midiNoteNumber,
297 const float velocity)
301 for (
auto* sound : sounds)
303 if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel))
307 for (
auto* voice : voices)
308 if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel))
312 sound, midiChannel, midiNoteNumber, velocity);
319 const int midiChannel,
320 const int midiNoteNumber,
321 const float velocity)
323 if (voice !=
nullptr && sound !=
nullptr)
325 if (voice->currentlyPlayingSound !=
nullptr)
328 voice->currentlyPlayingNote = midiNoteNumber;
329 voice->currentPlayingMidiChannel = midiChannel;
330 voice->noteOnTime = ++lastNoteOnCounter;
331 voice->currentlyPlayingSound = sound;
336 voice->
startNote (midiNoteNumber, velocity, sound,
345 voice->
stopNote (velocity, allowTailOff);
352 const int midiNoteNumber,
353 const float velocity,
354 const bool allowTailOff)
358 for (
auto* voice : voices)
360 if (voice->getCurrentlyPlayingNote() == midiNoteNumber
361 && voice->isPlayingChannel (midiChannel))
363 if (
auto sound = voice->getCurrentlyPlayingSound())
365 if (sound->appliesToNote (midiNoteNumber)
366 && sound->appliesToChannel (midiChannel))
368 jassert (! voice->keyIsDown || voice->isSustainPedalDown() == sustainPedalsDown [midiChannel]);
370 voice->setKeyDown (
false);
372 if (! (voice->isSustainPedalDown() || voice->isSostenutoPedalDown()))
373 stopVoice (voice, velocity, allowTailOff);
384 for (
auto* voice : voices)
385 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
386 voice->stopNote (1.0f, allowTailOff);
388 sustainPedalsDown.
clear();
395 for (
auto* voice : voices)
396 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
397 voice->pitchWheelMoved (wheelValue);
401 const int controllerNumber,
402 const int controllerValue)
404 switch (controllerNumber)
408 case 0x43:
handleSoftPedal (midiChannel, controllerValue >= 64);
break;
414 for (
auto* voice : voices)
415 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
416 voice->controllerMoved (controllerNumber, controllerValue);
423 for (
auto* voice : voices)
424 if (voice->getCurrentlyPlayingNote() == midiNoteNumber
425 && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)))
426 voice->aftertouchChanged (aftertouchValue);
433 for (
auto* voice : voices)
434 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
435 voice->channelPressureChanged (channelPressureValue);
440 jassert (midiChannel > 0 && midiChannel <= 16);
445 sustainPedalsDown.
setBit (midiChannel);
447 for (
auto* voice : voices)
448 if (voice->isPlayingChannel (midiChannel) && voice->isKeyDown())
449 voice->setSustainPedalDown (
true);
453 for (
auto* voice : voices)
455 if (voice->isPlayingChannel (midiChannel))
457 voice->setSustainPedalDown (
false);
459 if (! (voice->isKeyDown() || voice->isSostenutoPedalDown()))
464 sustainPedalsDown.
clearBit (midiChannel);
470 jassert (midiChannel > 0 && midiChannel <= 16);
473 for (
auto* voice : voices)
475 if (voice->isPlayingChannel (midiChannel))
478 voice->setSostenutoPedalDown (
true);
479 else if (voice->isSostenutoPedalDown())
487 jassert (midiChannel > 0 && midiChannel <= 16);
491 [[maybe_unused]]
int programNumber)
493 jassert (midiChannel > 0 && midiChannel <= 16);
498 int midiChannel,
int midiNoteNumber,
499 const bool stealIfNoneAvailable)
const
503 for (
auto* voice : voices)
504 if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay))
507 if (stealIfNoneAvailable)
514 int ,
int midiNoteNumber)
const
533 usableVoicesToStealArray.clear();
535 for (
auto* voice : voices)
537 if (voice->canPlaySound (soundToPlay))
539 jassert (voice->isVoiceActive());
541 usableVoicesToStealArray.add (voice);
550 std::sort (usableVoicesToStealArray.begin(), usableVoicesToStealArray.end(), Sorter());
552 if (! voice->isPlayingButReleased())
554 auto note = voice->getCurrentlyPlayingNote();
556 if (low ==
nullptr || note < low->getCurrentlyPlayingNote())
570 for (
auto* voice : usableVoicesToStealArray)
571 if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
575 for (
auto* voice : usableVoicesToStealArray)
580 for (
auto* voice : usableVoicesToStealArray)
581 if (voice != low && voice != top && ! voice->
isKeyDown())
585 for (
auto* voice : usableVoicesToStealArray)
586 if (voice != low && voice != top)
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 *const * getArrayOfWritePointers() noexcept
Returns an array of pointers to the channels in the buffer.
BigInteger & clear() noexcept
Resets the value to 0.
BigInteger & clearBit(int bitNumber) noexcept
Clears a particular bit in the number.
BigInteger & setBit(int bitNumber)
Sets a specified bit to 1.
Automatically locks and unlocks a mutex object.
Holds a sequence of time-stamped midi events.
MidiBufferIterator findNextSamplePosition(int samplePosition) const noexcept
Get an iterator pointing to the first event with a timestamp greater-than or equal-to samplePosition.
MidiBufferIterator cend() const noexcept
Get a read-only iterator pointing one past the end of this buffer.
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.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
constexpr bool exactlyEqual(Type a, Type b)
Equivalent to operator==, but suppresses float-equality warnings.
constexpr int numElementsInArray(Type(&)[N]) noexcept
Handy function for getting the number of elements in a simple const C array.