The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

440 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. SynthesiserSound::SynthesiserSound()
  21. {
  22. }
  23. SynthesiserSound::~SynthesiserSound()
  24. {
  25. }
  26. //==============================================================================
  27. SynthesiserVoice::SynthesiserVoice()
  28. : currentSampleRate (44100.0),
  29. currentlyPlayingNote (-1),
  30. noteOnTime (0),
  31. keyIsDown (false),
  32. sostenutoPedalDown (false)
  33. {
  34. }
  35. SynthesiserVoice::~SynthesiserVoice()
  36. {
  37. }
  38. bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const
  39. {
  40. return currentlyPlayingSound != nullptr
  41. && currentlyPlayingSound->appliesToChannel (midiChannel);
  42. }
  43. void SynthesiserVoice::setCurrentPlaybackSampleRate (const double newRate)
  44. {
  45. currentSampleRate = newRate;
  46. }
  47. void SynthesiserVoice::clearCurrentNote()
  48. {
  49. currentlyPlayingNote = -1;
  50. currentlyPlayingSound = nullptr;
  51. }
  52. //==============================================================================
  53. Synthesiser::Synthesiser()
  54. : sampleRate (0),
  55. lastNoteOnCounter (0),
  56. shouldStealNotes (true)
  57. {
  58. for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i)
  59. lastPitchWheelValues[i] = 0x2000;
  60. }
  61. Synthesiser::~Synthesiser()
  62. {
  63. }
  64. //==============================================================================
  65. SynthesiserVoice* Synthesiser::getVoice (const int index) const
  66. {
  67. const ScopedLock sl (lock);
  68. return voices [index];
  69. }
  70. void Synthesiser::clearVoices()
  71. {
  72. const ScopedLock sl (lock);
  73. voices.clear();
  74. }
  75. void Synthesiser::addVoice (SynthesiserVoice* const newVoice)
  76. {
  77. const ScopedLock sl (lock);
  78. voices.add (newVoice);
  79. }
  80. void Synthesiser::removeVoice (const int index)
  81. {
  82. const ScopedLock sl (lock);
  83. voices.remove (index);
  84. }
  85. void Synthesiser::clearSounds()
  86. {
  87. const ScopedLock sl (lock);
  88. sounds.clear();
  89. }
  90. void Synthesiser::addSound (const SynthesiserSound::Ptr& newSound)
  91. {
  92. const ScopedLock sl (lock);
  93. sounds.add (newSound);
  94. }
  95. void Synthesiser::removeSound (const int index)
  96. {
  97. const ScopedLock sl (lock);
  98. sounds.remove (index);
  99. }
  100. void Synthesiser::setNoteStealingEnabled (const bool shouldStealNotes_)
  101. {
  102. shouldStealNotes = shouldStealNotes_;
  103. }
  104. //==============================================================================
  105. void Synthesiser::setCurrentPlaybackSampleRate (const double newRate)
  106. {
  107. if (sampleRate != newRate)
  108. {
  109. const ScopedLock sl (lock);
  110. allNotesOff (0, false);
  111. sampleRate = newRate;
  112. for (int i = voices.size(); --i >= 0;)
  113. voices.getUnchecked (i)->setCurrentPlaybackSampleRate (newRate);
  114. }
  115. }
  116. void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer,
  117. const MidiBuffer& midiData,
  118. int startSample,
  119. int numSamples)
  120. {
  121. // must set the sample rate before using this!
  122. jassert (sampleRate != 0);
  123. const ScopedLock sl (lock);
  124. MidiBuffer::Iterator midiIterator (midiData);
  125. midiIterator.setNextSamplePosition (startSample);
  126. MidiMessage m (0xf4, 0.0);
  127. while (numSamples > 0)
  128. {
  129. int midiEventPos;
  130. const bool useEvent = midiIterator.getNextEvent (m, midiEventPos)
  131. && midiEventPos < startSample + numSamples;
  132. const int numThisTime = useEvent ? midiEventPos - startSample
  133. : numSamples;
  134. if (numThisTime > 0)
  135. {
  136. for (int i = voices.size(); --i >= 0;)
  137. voices.getUnchecked (i)->renderNextBlock (outputBuffer, startSample, numThisTime);
  138. }
  139. if (useEvent)
  140. handleMidiEvent (m);
  141. startSample += numThisTime;
  142. numSamples -= numThisTime;
  143. }
  144. }
  145. void Synthesiser::handleMidiEvent (const MidiMessage& m)
  146. {
  147. if (m.isNoteOn())
  148. {
  149. noteOn (m.getChannel(),
  150. m.getNoteNumber(),
  151. m.getFloatVelocity());
  152. }
  153. else if (m.isNoteOff())
  154. {
  155. noteOff (m.getChannel(),
  156. m.getNoteNumber(),
  157. true);
  158. }
  159. else if (m.isAllNotesOff() || m.isAllSoundOff())
  160. {
  161. allNotesOff (m.getChannel(), true);
  162. }
  163. else if (m.isPitchWheel())
  164. {
  165. const int channel = m.getChannel();
  166. const int wheelPos = m.getPitchWheelValue();
  167. lastPitchWheelValues [channel - 1] = wheelPos;
  168. handlePitchWheel (channel, wheelPos);
  169. }
  170. else if (m.isController())
  171. {
  172. handleController (m.getChannel(),
  173. m.getControllerNumber(),
  174. m.getControllerValue());
  175. }
  176. }
  177. //==============================================================================
  178. void Synthesiser::noteOn (const int midiChannel,
  179. const int midiNoteNumber,
  180. const float velocity)
  181. {
  182. const ScopedLock sl (lock);
  183. for (int i = sounds.size(); --i >= 0;)
  184. {
  185. SynthesiserSound* const sound = sounds.getUnchecked(i);
  186. if (sound->appliesToNote (midiNoteNumber)
  187. && sound->appliesToChannel (midiChannel))
  188. {
  189. // If hitting a note that's still ringing, stop it first (it could be
  190. // still playing because of the sustain or sostenuto pedal).
  191. for (int j = voices.size(); --j >= 0;)
  192. {
  193. SynthesiserVoice* const voice = voices.getUnchecked (j);
  194. if (voice->getCurrentlyPlayingNote() == midiNoteNumber
  195. && voice->isPlayingChannel (midiChannel))
  196. stopVoice (voice, true);
  197. }
  198. startVoice (findFreeVoice (sound, shouldStealNotes),
  199. sound, midiChannel, midiNoteNumber, velocity);
  200. }
  201. }
  202. }
  203. void Synthesiser::startVoice (SynthesiserVoice* const voice,
  204. SynthesiserSound* const sound,
  205. const int midiChannel,
  206. const int midiNoteNumber,
  207. const float velocity)
  208. {
  209. if (voice != nullptr && sound != nullptr)
  210. {
  211. if (voice->currentlyPlayingSound != nullptr)
  212. voice->stopNote (false);
  213. voice->startNote (midiNoteNumber, velocity, sound,
  214. lastPitchWheelValues [midiChannel - 1]);
  215. voice->currentlyPlayingNote = midiNoteNumber;
  216. voice->noteOnTime = ++lastNoteOnCounter;
  217. voice->currentlyPlayingSound = sound;
  218. voice->keyIsDown = true;
  219. voice->sostenutoPedalDown = false;
  220. }
  221. }
  222. void Synthesiser::stopVoice (SynthesiserVoice* voice, const bool allowTailOff)
  223. {
  224. jassert (voice != nullptr);
  225. voice->stopNote (allowTailOff);
  226. // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
  227. jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0));
  228. }
  229. void Synthesiser::noteOff (const int midiChannel,
  230. const int midiNoteNumber,
  231. const bool allowTailOff)
  232. {
  233. const ScopedLock sl (lock);
  234. for (int i = voices.size(); --i >= 0;)
  235. {
  236. SynthesiserVoice* const voice = voices.getUnchecked (i);
  237. if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
  238. {
  239. SynthesiserSound* const sound = voice->getCurrentlyPlayingSound();
  240. if (sound != nullptr
  241. && sound->appliesToNote (midiNoteNumber)
  242. && sound->appliesToChannel (midiChannel))
  243. {
  244. voice->keyIsDown = false;
  245. if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown))
  246. stopVoice (voice, allowTailOff);
  247. }
  248. }
  249. }
  250. }
  251. void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
  252. {
  253. const ScopedLock sl (lock);
  254. for (int i = voices.size(); --i >= 0;)
  255. {
  256. SynthesiserVoice* const voice = voices.getUnchecked (i);
  257. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  258. voice->stopNote (allowTailOff);
  259. }
  260. sustainPedalsDown.clear();
  261. }
  262. void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue)
  263. {
  264. const ScopedLock sl (lock);
  265. for (int i = voices.size(); --i >= 0;)
  266. {
  267. SynthesiserVoice* const voice = voices.getUnchecked (i);
  268. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  269. voice->pitchWheelMoved (wheelValue);
  270. }
  271. }
  272. void Synthesiser::handleController (const int midiChannel,
  273. const int controllerNumber,
  274. const int controllerValue)
  275. {
  276. switch (controllerNumber)
  277. {
  278. case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break;
  279. case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break;
  280. case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break;
  281. default: break;
  282. }
  283. const ScopedLock sl (lock);
  284. for (int i = voices.size(); --i >= 0;)
  285. {
  286. SynthesiserVoice* const voice = voices.getUnchecked (i);
  287. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  288. voice->controllerMoved (controllerNumber, controllerValue);
  289. }
  290. }
  291. void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
  292. {
  293. jassert (midiChannel > 0 && midiChannel <= 16);
  294. const ScopedLock sl (lock);
  295. if (isDown)
  296. {
  297. sustainPedalsDown.setBit (midiChannel);
  298. }
  299. else
  300. {
  301. for (int i = voices.size(); --i >= 0;)
  302. {
  303. SynthesiserVoice* const voice = voices.getUnchecked (i);
  304. if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown)
  305. stopVoice (voice, true);
  306. }
  307. sustainPedalsDown.clearBit (midiChannel);
  308. }
  309. }
  310. void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
  311. {
  312. jassert (midiChannel > 0 && midiChannel <= 16);
  313. const ScopedLock sl (lock);
  314. for (int i = voices.size(); --i >= 0;)
  315. {
  316. SynthesiserVoice* const voice = voices.getUnchecked (i);
  317. if (voice->isPlayingChannel (midiChannel))
  318. {
  319. if (isDown)
  320. voice->sostenutoPedalDown = true;
  321. else if (voice->sostenutoPedalDown)
  322. stopVoice (voice, true);
  323. }
  324. }
  325. }
  326. void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/)
  327. {
  328. (void) midiChannel;
  329. jassert (midiChannel > 0 && midiChannel <= 16);
  330. }
  331. //==============================================================================
  332. SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay,
  333. const bool stealIfNoneAvailable) const
  334. {
  335. const ScopedLock sl (lock);
  336. for (int i = voices.size(); --i >= 0;)
  337. if (voices.getUnchecked (i)->getCurrentlyPlayingNote() < 0
  338. && voices.getUnchecked (i)->canPlaySound (soundToPlay))
  339. return voices.getUnchecked (i);
  340. if (stealIfNoneAvailable)
  341. {
  342. // currently this just steals the one that's been playing the longest, but could be made a bit smarter..
  343. SynthesiserVoice* oldest = nullptr;
  344. for (int i = voices.size(); --i >= 0;)
  345. {
  346. SynthesiserVoice* const voice = voices.getUnchecked (i);
  347. if (voice->canPlaySound (soundToPlay)
  348. && (oldest == nullptr || oldest->noteOnTime > voice->noteOnTime))
  349. oldest = voice;
  350. }
  351. jassert (oldest != nullptr);
  352. return oldest;
  353. }
  354. return nullptr;
  355. }
  356. END_JUCE_NAMESPACE