26 SynthesiserSound::SynthesiserSound() {}
35 return currentPlayingMidiChannel == midiChannel;
40 currentSampleRate = newRate;
45 return getCurrentlyPlayingNote() >= 0;
50 currentlyPlayingNote = -1;
51 currentlyPlayingSound =
nullptr;
52 currentPlayingMidiChannel = 0;
60 return noteOnTime < other.noteOnTime;
64 int startSample,
int numSamples)
68 startSample, numSamples);
71 renderNextBlock (tempBuffer, 0, numSamples);
72 subBuffer.makeCopyOf (tempBuffer,
true);
78 for (
int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i)
79 lastPitchWheelValues[i] = 0x2000;
90 return voices [index];
103 return voices.add (newVoice);
109 voices.remove (index);
121 return sounds.add (newSound);
127 sounds.remove (index);
132 shouldStealNotes = shouldSteal;
137 jassert (numSamples > 0);
138 minimumSubBlockSize = numSamples;
139 subBlockSubdivisionIsStrict = shouldBeStrict;
145 if (sampleRate != newRate)
148 allNotesOff (0,
false);
149 sampleRate = newRate;
151 for (
auto* voice : voices)
152 voice->setCurrentPlaybackSampleRate (newRate);
156 template <
typename floatType>
163 jassert (sampleRate != 0);
169 bool firstEvent =
true;
175 while (numSamples > 0)
179 if (targetChannels > 0)
180 renderVoices (outputAudio, startSample, numSamples);
185 const int samplesToNextMidiMessage = midiEventPos - startSample;
187 if (samplesToNextMidiMessage >= numSamples)
189 if (targetChannels > 0)
190 renderVoices (outputAudio, startSample, numSamples);
196 if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize))
204 if (targetChannels > 0)
205 renderVoices (outputAudio, startSample, samplesToNextMidiMessage);
208 startSample += samplesToNextMidiMessage;
209 numSamples -= samplesToNextMidiMessage;
218 template void Synthesiser::processNextBlock<double> (
AudioBuffer<double>&,
const MidiBuffer&, int, int);
221 int startSample,
int numSamples)
223 processNextBlock (outputAudio, inputMidi, startSample, numSamples);
227 int startSample,
int numSamples)
229 processNextBlock (outputAudio, inputMidi, startSample, numSamples);
234 for (
auto* voice : voices)
235 voice->renderNextBlock (buffer, startSample, numSamples);
240 for (
auto* voice : voices)
241 voice->renderNextBlock (buffer, startSample, numSamples);
258 allNotesOff (channel,
true);
263 lastPitchWheelValues [channel - 1] = wheelPos;
264 handlePitchWheel (channel, wheelPos);
286 const int midiNoteNumber,
287 const float velocity)
291 for (
auto* sound : sounds)
293 if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel))
297 for (
auto* voice : voices)
298 if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel))
299 stopVoice (voice, 1.0f,
true);
301 startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
302 sound, midiChannel, midiNoteNumber, velocity);
309 const int midiChannel,
310 const int midiNoteNumber,
311 const float velocity)
313 if (voice !=
nullptr && sound !=
nullptr)
315 if (voice->currentlyPlayingSound !=
nullptr)
318 voice->currentlyPlayingNote = midiNoteNumber;
319 voice->currentPlayingMidiChannel = midiChannel;
320 voice->noteOnTime = ++lastNoteOnCounter;
321 voice->currentlyPlayingSound = sound;
326 voice->
startNote (midiNoteNumber, velocity, sound,
327 lastPitchWheelValues [midiChannel - 1]);
333 jassert (voice !=
nullptr);
335 voice->
stopNote (velocity, allowTailOff);
342 const int midiNoteNumber,
343 const float velocity,
344 const bool allowTailOff)
348 for (
auto* voice : voices)
350 if (voice->getCurrentlyPlayingNote() == midiNoteNumber
351 && voice->isPlayingChannel (midiChannel))
353 if (
auto sound = voice->getCurrentlyPlayingSound())
355 if (sound->appliesToNote (midiNoteNumber)
356 && sound->appliesToChannel (midiChannel))
358 jassert (! voice->keyIsDown || voice->isSustainPedalDown() == sustainPedalsDown [midiChannel]);
360 voice->setKeyDown (
false);
362 if (! (voice->isSustainPedalDown() || voice->isSostenutoPedalDown()))
363 stopVoice (voice, velocity, allowTailOff);
374 for (
auto* voice : voices)
375 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
376 voice->stopNote (1.0f, allowTailOff);
378 sustainPedalsDown.clear();
385 for (
auto* voice : voices)
386 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
387 voice->pitchWheelMoved (wheelValue);
391 const int controllerNumber,
392 const int controllerValue)
394 switch (controllerNumber)
396 case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64);
break;
397 case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64);
break;
398 case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64);
break;
404 for (
auto* voice : voices)
405 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
406 voice->controllerMoved (controllerNumber, controllerValue);
413 for (
auto* voice : voices)
414 if (voice->getCurrentlyPlayingNote() == midiNoteNumber
415 && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)))
416 voice->aftertouchChanged (aftertouchValue);
423 for (
auto* voice : voices)
424 if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
425 voice->channelPressureChanged (channelPressureValue);
430 jassert (midiChannel > 0 && midiChannel <= 16);
435 sustainPedalsDown.setBit (midiChannel);
437 for (
auto* voice : voices)
438 if (voice->isPlayingChannel (midiChannel) && voice->isKeyDown())
439 voice->setSustainPedalDown (
true);
443 for (
auto* voice : voices)
445 if (voice->isPlayingChannel (midiChannel))
447 voice->setSustainPedalDown (
false);
449 if (! (voice->isKeyDown() || voice->isSostenutoPedalDown()))
450 stopVoice (voice, 1.0f,
true);
454 sustainPedalsDown.clearBit (midiChannel);
460 jassert (midiChannel > 0 && midiChannel <= 16);
463 for (
auto* voice : voices)
465 if (voice->isPlayingChannel (midiChannel))
468 voice->setSostenutoPedalDown (
true);
469 else if (voice->isSostenutoPedalDown())
470 stopVoice (voice, 1.0f,
true);
477 ignoreUnused (midiChannel);
478 jassert (midiChannel > 0 && midiChannel <= 16);
483 ignoreUnused (midiChannel, programNumber);
484 jassert (midiChannel > 0 && midiChannel <= 16);
489 int midiChannel,
int midiNoteNumber,
490 const bool stealIfNoneAvailable)
const 494 for (
auto* voice : voices)
495 if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay))
498 if (stealIfNoneAvailable)
499 return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber);
505 int ,
int midiNoteNumber)
const 512 jassert (! voices.isEmpty());
522 for (
auto* voice : voices)
524 if (voice->canPlaySound (soundToPlay))
526 jassert (voice->isVoiceActive());
528 usableVoices.
add (voice);
537 std::sort (usableVoices.
begin(), usableVoices.
end(), Sorter());
539 if (! voice->isPlayingButReleased())
541 auto note = voice->getCurrentlyPlayingNote();
543 if (low ==
nullptr || note < low->getCurrentlyPlayingNote())
557 for (
auto* voice : usableVoices)
558 if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
562 for (
auto* voice : usableVoices)
567 for (
auto* voice : usableVoices)
568 if (voice != low && voice != top && ! voice->
isKeyDown())
572 for (
auto* voice : usableVoices)
573 if (voice != low && voice != top)
577 jassert (low !=
nullptr);
ElementType * begin() const noexcept
Returns a pointer to the first element in the array.
bool isAftertouch() const noexcept
Returns true if the message is an aftertouch event.
void clearCurrentNote()
Resets the state of this voice after a sound has finished playing.
virtual SynthesiserVoice * findVoiceToSteal(SynthesiserSound *soundToPlay, int midiChannel, int midiNoteNumber) const
Chooses a voice that is most suitable for being re-used.
void setNextSamplePosition(int samplePosition) noexcept
Repositions the iterator so that the next event retrieved will be the first one whose sample position...
bool isPitchWheel() const noexcept
Returns true if the message is a pitch-wheel move.
int getAfterTouchValue() const noexcept
Returns the amount of aftertouch from an aftertouch messages.
Encapsulates a MIDI message.
bool wasStartedBefore(const SynthesiserVoice &other) const noexcept
Returns true if this voice started playing its current note before the other voice did...
bool getNextEvent(MidiMessage &result, int &samplePosition) noexcept
Retrieves a copy of the next event from the buffer.
bool isAllNotesOff() const noexcept
Checks whether this message is an all-notes-off message.
void ensureStorageAllocated(int minNumElements)
Increases the array's internal storage to hold a minimum number of elements.
void add(const ElementType &newElement)
Appends a new element at the end of the array.
virtual void handleSostenutoPedal(int midiChannel, bool isDown)
Handles a sostenuto pedal event.
void removeVoice(int index)
Deletes one of the voices.
virtual void allNotesOff(int midiChannel, bool allowTailOff)
Turns off all notes.
virtual void handleController(int midiChannel, int controllerNumber, int controllerValue)
Sends a midi controller message to any active voices.
void clearVoices()
Deletes all voices.
virtual bool isPlayingChannel(int midiChannel) const
Returns true if the voice is currently playing a sound which is mapped to the given midi channel...
void renderNextBlock(AudioBuffer< float > &outputAudio, const MidiBuffer &inputMidi, int startSample, int numSamples)
Creates the next block of audio output.
Type ** getArrayOfWritePointers() noexcept
Returns an array of pointers to the channels in the buffer.
void setSostenutoPedalDown(bool isNowDown) noexcept
Modifies the sostenuto pedal flag.
virtual void handleChannelPressure(int midiChannel, int channelPressureValue)
Sends a channel pressure message.
virtual void noteOff(int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff)
Triggers a note-off event.
SynthesiserVoice * addVoice(SynthesiserVoice *newVoice)
Adds a new voice to the synth.
SynthesiserSound::Ptr getCurrentlyPlayingSound() const noexcept
Returns the sound that this voice is currently playing.
virtual void renderNextBlock(AudioBuffer< float > &outputBuffer, int startSample, int numSamples)=0
Renders the next block of data for this voice.
void stopVoice(SynthesiserVoice *, float velocity, bool allowTailOff)
Stops a given voice.
bool isProgramChange() const noexcept
Returns true if the message is a program (patch) change message.
void clearSounds()
Deletes all sounds.
Describes one of the sounds that a Synthesiser can play.
virtual void handleMidiEvent(const MidiMessage &)
Can be overridden to do custom handling of incoming midi events.
virtual void setCurrentPlaybackSampleRate(double sampleRate)
Tells the synthesiser what the sample rate is for the audio it's being used to render.
Synthesiser()
Creates a new synthesiser.
A multi-channel buffer containing floating point audio samples.
void setSustainPedalDown(bool isNowDown) noexcept
Modifies the sustain pedal flag.
int getControllerNumber() const noexcept
Returns the controller number of a controller message.
int getChannel() const noexcept
Returns the midi channel associated with the message.
bool isController() const noexcept
Returns true if this is a midi controller message.
int getNumChannels() const noexcept
Returns the number of channels of audio data that this buffer contains.
ElementType * end() const noexcept
Returns a pointer to the element which follows the last element in the array.
SynthesiserVoice()
Creates a voice.
virtual void renderVoices(AudioBuffer< float > &outputAudio, int startSample, int numSamples)
Renders the voices for the given range.
virtual void setCurrentPlaybackSampleRate(double newRate)
Changes the voice's reference sample rate.
virtual void startNote(int midiNoteNumber, float velocity, SynthesiserSound *sound, int currentPitchWheelPosition)=0
Called to start a new note.
virtual bool isVoiceActive() const
Returns true if this voice is currently busy playing a sound.
virtual ~SynthesiserVoice()
Destructor.
bool isChannelPressure() const noexcept
Returns true if the message is a channel-pressure change event.
float getFloatVelocity() const noexcept
Returns the velocity of a note-on or note-off message.
int getProgramChangeNumber() const noexcept
Returns the new program number of a program change message.
int getPitchWheelValue() const noexcept
Returns the pitch wheel position from a pitch-wheel move message.
Holds a resizable array of primitive or copy-by-value objects.
virtual void stopNote(float velocity, bool allowTailOff)=0
Called to stop a note.
bool isKeyDown() const noexcept
Returns true if the key that triggered this voice is still held down.
int getChannelPressureValue() const noexcept
Returns the pressure from a channel pressure change message.
virtual void handleSoftPedal(int midiChannel, bool isDown)
Can be overridden to handle soft pedal events.
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.
bool isNoteOff(bool returnTrueForNoteOnVelocity0=true) const noexcept
Returns true if this message is a 'key-up' event.
virtual void channelPressureChanged(int newChannelPressureValue)
Called to let the voice know that the channel pressure has changed.
Represents a voice that a Synthesiser can use to play a SynthesiserSound.
Holds a sequence of time-stamped midi events.
SynthesiserSound * addSound(const SynthesiserSound::Ptr &newSound)
Adds a new sound to the synthesiser.
bool isPlayingButReleased() const noexcept
Returns true if a voice is sounding in its release phase.
int getCurrentlyPlayingNote() const noexcept
Returns the midi note that this voice is currently playing.
bool isNoteOn(bool returnTrueForVelocity0=false) const noexcept
Returns true if this message is a 'key-down' event.
~SynthesiserSound() override
Destructor.
void startVoice(SynthesiserVoice *voice, SynthesiserSound *sound, int midiChannel, int midiNoteNumber, float velocity)
Starts a specified voice playing a particular sound.
void makeCopyOf(const AudioBuffer< OtherType > &other, bool avoidReallocating=false)
Resizes this buffer to match the given one, and copies all of its content across. ...
virtual void handleProgramChange(int midiChannel, int programNumber)
Can be overridden to handle an incoming program change message.
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...
void removeSound(int index)
Removes and deletes one of the sounds.
int getControllerValue() const noexcept
Returns the controller value from a controller message.
virtual void aftertouchChanged(int newAftertouchValue)
Called to let the voice know that the aftertouch has changed.
SynthesiserVoice * getVoice(int index) const
Returns one of the voices that have been added.
Used to iterate through the events in a MidiBuffer.
virtual void handleSustainPedal(int midiChannel, bool isDown)
Handles a sustain pedal event.
virtual void handleAftertouch(int midiChannel, int midiNoteNumber, int aftertouchValue)
Sends an aftertouch message.
int getNoteNumber() const noexcept
Returns the midi note number for note-on and note-off messages.
Automatically locks and unlocks a mutex object.
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 noteOn(int midiChannel, int midiNoteNumber, float velocity)
Triggers a note-on event.
virtual ~Synthesiser()
Destructor.
virtual void handlePitchWheel(int midiChannel, int wheelValue)
Sends a pitch-wheel message to any active voices.
void setKeyDown(bool isNowDown) noexcept
Allows you to modify the flag indicating that the key that triggered this voice is still held down...
bool isAllSoundOff() const noexcept
Checks whether this message is an all-sound-off message.