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.

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