2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../core/juce_StandardHeader.h"
30 #include "juce_Synthesiser.h"
33 //==============================================================================
34 SynthesiserSound::SynthesiserSound()
38 SynthesiserSound::~SynthesiserSound()
42 //==============================================================================
43 SynthesiserVoice::SynthesiserVoice()
44 : currentSampleRate (44100.0),
45 currentlyPlayingNote (-1),
48 sostenutoPedalDown (false)
52 SynthesiserVoice::~SynthesiserVoice()
56 bool SynthesiserVoice::isPlayingChannel (const int midiChannel
) const
58 return currentlyPlayingSound
!= nullptr
59 && currentlyPlayingSound
->appliesToChannel (midiChannel
);
62 void SynthesiserVoice::setCurrentPlaybackSampleRate (const double newRate
)
64 currentSampleRate
= newRate
;
67 void SynthesiserVoice::clearCurrentNote()
69 currentlyPlayingNote
= -1;
70 currentlyPlayingSound
= nullptr;
73 //==============================================================================
74 Synthesiser::Synthesiser()
76 lastNoteOnCounter (0),
77 shouldStealNotes (true)
79 for (int i
= 0; i
< numElementsInArray (lastPitchWheelValues
); ++i
)
80 lastPitchWheelValues
[i
] = 0x2000;
83 Synthesiser::~Synthesiser()
87 //==============================================================================
88 SynthesiserVoice
* Synthesiser::getVoice (const int index
) const
90 const ScopedLock
sl (lock
);
91 return voices
[index
];
94 void Synthesiser::clearVoices()
96 const ScopedLock
sl (lock
);
100 void Synthesiser::addVoice (SynthesiserVoice
* const newVoice
)
102 const ScopedLock
sl (lock
);
103 voices
.add (newVoice
);
106 void Synthesiser::removeVoice (const int index
)
108 const ScopedLock
sl (lock
);
109 voices
.remove (index
);
112 void Synthesiser::clearSounds()
114 const ScopedLock
sl (lock
);
118 void Synthesiser::addSound (const SynthesiserSound::Ptr
& newSound
)
120 const ScopedLock
sl (lock
);
121 sounds
.add (newSound
);
124 void Synthesiser::removeSound (const int index
)
126 const ScopedLock
sl (lock
);
127 sounds
.remove (index
);
130 void Synthesiser::setNoteStealingEnabled (const bool shouldStealNotes_
)
132 shouldStealNotes
= shouldStealNotes_
;
135 //==============================================================================
136 void Synthesiser::setCurrentPlaybackSampleRate (const double newRate
)
138 if (sampleRate
!= newRate
)
140 const ScopedLock
sl (lock
);
142 allNotesOff (0, false);
144 sampleRate
= newRate
;
146 for (int i
= voices
.size(); --i
>= 0;)
147 voices
.getUnchecked (i
)->setCurrentPlaybackSampleRate (newRate
);
151 void Synthesiser::renderNextBlock (AudioSampleBuffer
& outputBuffer
,
152 const MidiBuffer
& midiData
,
156 // must set the sample rate before using this!
157 jassert (sampleRate
!= 0);
159 const ScopedLock
sl (lock
);
161 MidiBuffer::Iterator
midiIterator (midiData
);
162 midiIterator
.setNextSamplePosition (startSample
);
163 MidiMessage
m (0xf4, 0.0);
165 while (numSamples
> 0)
168 const bool useEvent
= midiIterator
.getNextEvent (m
, midiEventPos
)
169 && midiEventPos
< startSample
+ numSamples
;
171 const int numThisTime
= useEvent
? midiEventPos
- startSample
176 for (int i
= voices
.size(); --i
>= 0;)
177 voices
.getUnchecked (i
)->renderNextBlock (outputBuffer
, startSample
, numThisTime
);
183 startSample
+= numThisTime
;
184 numSamples
-= numThisTime
;
188 void Synthesiser::handleMidiEvent (const MidiMessage
& m
)
192 noteOn (m
.getChannel(),
194 m
.getFloatVelocity());
196 else if (m
.isNoteOff())
198 noteOff (m
.getChannel(),
202 else if (m
.isAllNotesOff() || m
.isAllSoundOff())
204 allNotesOff (m
.getChannel(), true);
206 else if (m
.isPitchWheel())
208 const int channel
= m
.getChannel();
209 const int wheelPos
= m
.getPitchWheelValue();
210 lastPitchWheelValues
[channel
- 1] = wheelPos
;
212 handlePitchWheel (channel
, wheelPos
);
214 else if (m
.isController())
216 handleController (m
.getChannel(),
217 m
.getControllerNumber(),
218 m
.getControllerValue());
222 //==============================================================================
223 void Synthesiser::noteOn (const int midiChannel
,
224 const int midiNoteNumber
,
225 const float velocity
)
227 const ScopedLock
sl (lock
);
229 for (int i
= sounds
.size(); --i
>= 0;)
231 SynthesiserSound
* const sound
= sounds
.getUnchecked(i
);
233 if (sound
->appliesToNote (midiNoteNumber
)
234 && sound
->appliesToChannel (midiChannel
))
236 // If hitting a note that's still ringing, stop it first (it could be
237 // still playing because of the sustain or sostenuto pedal).
238 for (int j
= voices
.size(); --j
>= 0;)
240 SynthesiserVoice
* const voice
= voices
.getUnchecked (j
);
242 if (voice
->getCurrentlyPlayingNote() == midiNoteNumber
243 && voice
->isPlayingChannel (midiChannel
))
244 stopVoice (voice
, true);
247 startVoice (findFreeVoice (sound
, shouldStealNotes
),
248 sound
, midiChannel
, midiNoteNumber
, velocity
);
253 void Synthesiser::startVoice (SynthesiserVoice
* const voice
,
254 SynthesiserSound
* const sound
,
255 const int midiChannel
,
256 const int midiNoteNumber
,
257 const float velocity
)
259 if (voice
!= nullptr && sound
!= nullptr)
261 if (voice
->currentlyPlayingSound
!= nullptr)
262 voice
->stopNote (false);
264 voice
->startNote (midiNoteNumber
, velocity
, sound
,
265 lastPitchWheelValues
[midiChannel
- 1]);
267 voice
->currentlyPlayingNote
= midiNoteNumber
;
268 voice
->noteOnTime
= ++lastNoteOnCounter
;
269 voice
->currentlyPlayingSound
= sound
;
270 voice
->keyIsDown
= true;
271 voice
->sostenutoPedalDown
= false;
275 void Synthesiser::stopVoice (SynthesiserVoice
* voice
, const bool allowTailOff
)
277 jassert (voice
!= nullptr);
279 voice
->stopNote (allowTailOff
);
281 // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
282 jassert (allowTailOff
|| (voice
->getCurrentlyPlayingNote() < 0 && voice
->getCurrentlyPlayingSound() == 0));
285 void Synthesiser::noteOff (const int midiChannel
,
286 const int midiNoteNumber
,
287 const bool allowTailOff
)
289 const ScopedLock
sl (lock
);
291 for (int i
= voices
.size(); --i
>= 0;)
293 SynthesiserVoice
* const voice
= voices
.getUnchecked (i
);
295 if (voice
->getCurrentlyPlayingNote() == midiNoteNumber
)
297 SynthesiserSound
* const sound
= voice
->getCurrentlyPlayingSound();
300 && sound
->appliesToNote (midiNoteNumber
)
301 && sound
->appliesToChannel (midiChannel
))
303 voice
->keyIsDown
= false;
305 if (! (sustainPedalsDown
[midiChannel
] || voice
->sostenutoPedalDown
))
306 stopVoice (voice
, allowTailOff
);
312 void Synthesiser::allNotesOff (const int midiChannel
, const bool allowTailOff
)
314 const ScopedLock
sl (lock
);
316 for (int i
= voices
.size(); --i
>= 0;)
318 SynthesiserVoice
* const voice
= voices
.getUnchecked (i
);
320 if (midiChannel
<= 0 || voice
->isPlayingChannel (midiChannel
))
321 voice
->stopNote (allowTailOff
);
324 sustainPedalsDown
.clear();
327 void Synthesiser::handlePitchWheel (const int midiChannel
, const int wheelValue
)
329 const ScopedLock
sl (lock
);
331 for (int i
= voices
.size(); --i
>= 0;)
333 SynthesiserVoice
* const voice
= voices
.getUnchecked (i
);
335 if (midiChannel
<= 0 || voice
->isPlayingChannel (midiChannel
))
336 voice
->pitchWheelMoved (wheelValue
);
340 void Synthesiser::handleController (const int midiChannel
,
341 const int controllerNumber
,
342 const int controllerValue
)
344 switch (controllerNumber
)
346 case 0x40: handleSustainPedal (midiChannel
, controllerValue
>= 64); break;
347 case 0x42: handleSostenutoPedal (midiChannel
, controllerValue
>= 64); break;
348 case 0x43: handleSoftPedal (midiChannel
, controllerValue
>= 64); break;
352 const ScopedLock
sl (lock
);
354 for (int i
= voices
.size(); --i
>= 0;)
356 SynthesiserVoice
* const voice
= voices
.getUnchecked (i
);
358 if (midiChannel
<= 0 || voice
->isPlayingChannel (midiChannel
))
359 voice
->controllerMoved (controllerNumber
, controllerValue
);
363 void Synthesiser::handleSustainPedal (int midiChannel
, bool isDown
)
365 jassert (midiChannel
> 0 && midiChannel
<= 16);
366 const ScopedLock
sl (lock
);
370 sustainPedalsDown
.setBit (midiChannel
);
374 for (int i
= voices
.size(); --i
>= 0;)
376 SynthesiserVoice
* const voice
= voices
.getUnchecked (i
);
378 if (voice
->isPlayingChannel (midiChannel
) && ! voice
->keyIsDown
)
379 stopVoice (voice
, true);
382 sustainPedalsDown
.clearBit (midiChannel
);
386 void Synthesiser::handleSostenutoPedal (int midiChannel
, bool isDown
)
388 jassert (midiChannel
> 0 && midiChannel
<= 16);
389 const ScopedLock
sl (lock
);
391 for (int i
= voices
.size(); --i
>= 0;)
393 SynthesiserVoice
* const voice
= voices
.getUnchecked (i
);
395 if (voice
->isPlayingChannel (midiChannel
))
398 voice
->sostenutoPedalDown
= true;
399 else if (voice
->sostenutoPedalDown
)
400 stopVoice (voice
, true);
405 void Synthesiser::handleSoftPedal (int midiChannel
, bool /*isDown*/)
408 jassert (midiChannel
> 0 && midiChannel
<= 16);
411 //==============================================================================
412 SynthesiserVoice
* Synthesiser::findFreeVoice (SynthesiserSound
* soundToPlay
,
413 const bool stealIfNoneAvailable
) const
415 const ScopedLock
sl (lock
);
417 for (int i
= voices
.size(); --i
>= 0;)
418 if (voices
.getUnchecked (i
)->getCurrentlyPlayingNote() < 0
419 && voices
.getUnchecked (i
)->canPlaySound (soundToPlay
))
420 return voices
.getUnchecked (i
);
422 if (stealIfNoneAvailable
)
424 // currently this just steals the one that's been playing the longest, but could be made a bit smarter..
425 SynthesiserVoice
* oldest
= nullptr;
427 for (int i
= voices
.size(); --i
>= 0;)
429 SynthesiserVoice
* const voice
= voices
.getUnchecked (i
);
431 if (voice
->canPlaySound (soundToPlay
)
432 && (oldest
== nullptr || oldest
->noteOnTime
> voice
->noteOnTime
))
436 jassert (oldest
!= nullptr);