OpenShot Library | libopenshot-audio  0.1.9
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 
23 namespace juce
24 {
25 
26 SynthesiserSound::SynthesiserSound() {}
28 
29 //==============================================================================
32 
33 bool 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 
58 bool 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 //==============================================================================
87 SynthesiserVoice* Synthesiser::getVoice (const int index) const
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 
106 void 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 
124 void Synthesiser::removeSound (const int index)
125 {
126  const ScopedLock sl (lock);
127  sounds.remove (index);
128 }
129 
130 void Synthesiser::setNoteStealingEnabled (const bool shouldSteal)
131 {
132  shouldStealNotes = shouldSteal;
133 }
134 
135 void 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 //==============================================================================
143 void Synthesiser::setCurrentPlaybackSampleRate (const double newRate)
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 
156 template <typename floatType>
157 void 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
217 template void Synthesiser::processNextBlock<float> (AudioBuffer<float>&, const MidiBuffer&, int, int);
218 template void Synthesiser::processNextBlock<double> (AudioBuffer<double>&, const MidiBuffer&, int, int);
219 
220 void Synthesiser::renderNextBlock (AudioBuffer<float>& outputAudio, const MidiBuffer& inputMidi,
221  int startSample, int numSamples)
222 {
223  processNextBlock (outputAudio, inputMidi, startSample, numSamples);
224 }
225 
226 void Synthesiser::renderNextBlock (AudioBuffer<double>& outputAudio, const MidiBuffer& inputMidi,
227  int startSample, int numSamples)
228 {
229  processNextBlock (outputAudio, inputMidi, startSample, numSamples);
230 }
231 
232 void Synthesiser::renderVoices (AudioBuffer<float>& buffer, int startSample, int numSamples)
233 {
234  for (auto* voice : voices)
235  voice->renderNextBlock (buffer, startSample, numSamples);
236 }
237 
238 void 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  {
268  handleAftertouch (channel, m.getNoteNumber(), m.getAfterTouchValue());
269  }
270  else if (m.isChannelPressure())
271  {
272  handleChannelPressure (channel, m.getChannelPressureValue());
273  }
274  else if (m.isController())
275  {
276  handleController (channel, m.getControllerNumber(), m.getControllerValue());
277  }
278  else if (m.isProgramChange())
279  {
280  handleProgramChange (channel, m.getProgramChangeNumber());
281  }
282 }
283 
284 //==============================================================================
285 void 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 
331 void 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 
341 void 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 
370 void 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 
381 void 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 
390 void 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 
409 void 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 
419 void 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 
428 void 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 
458 void 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 
475 void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/)
476 {
477  ignoreUnused (midiChannel);
478  jassert (midiChannel > 0 && midiChannel <= 16);
479 }
480 
481 void 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
ElementType * begin() const noexcept
Returns a pointer to the first element in the array.
Definition: juce_Array.h:309
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&#39;s internal storage to hold a minimum number of elements.
Definition: juce_Array.h:1019
void add(const ElementType &newElement)
Appends a new element at the end of the array.
Definition: juce_Array.h:375
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&#39;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.
Definition: juce_Array.h:317
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&#39;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.
Definition: juce_Array.h:59
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 &#39;key-up&#39; 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 &#39;key-down&#39; 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&#39;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.