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.

504 lines
15KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. SynthesiserSound::SynthesiserSound() {}
  18. SynthesiserSound::~SynthesiserSound() {}
  19. //==============================================================================
  20. SynthesiserVoice::SynthesiserVoice()
  21. : currentSampleRate (44100.0),
  22. currentlyPlayingNote (-1),
  23. noteOnTime (0),
  24. keyIsDown (false),
  25. sostenutoPedalDown (false)
  26. {
  27. }
  28. SynthesiserVoice::~SynthesiserVoice()
  29. {
  30. }
  31. bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const
  32. {
  33. return currentlyPlayingSound != nullptr
  34. && currentlyPlayingSound->appliesToChannel (midiChannel);
  35. }
  36. void SynthesiserVoice::setCurrentPlaybackSampleRate (const double newRate)
  37. {
  38. currentSampleRate = newRate;
  39. }
  40. bool SynthesiserVoice::isVoiceActive() const
  41. {
  42. return getCurrentlyPlayingNote() >= 0;
  43. }
  44. void SynthesiserVoice::clearCurrentNote()
  45. {
  46. currentlyPlayingNote = -1;
  47. currentlyPlayingSound = nullptr;
  48. }
  49. void SynthesiserVoice::aftertouchChanged (int) {}
  50. bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept
  51. {
  52. return noteOnTime < other.noteOnTime;
  53. }
  54. //==============================================================================
  55. Synthesiser::Synthesiser()
  56. : sampleRate (0),
  57. lastNoteOnCounter (0),
  58. shouldStealNotes (true)
  59. {
  60. for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i)
  61. lastPitchWheelValues[i] = 0x2000;
  62. }
  63. Synthesiser::~Synthesiser()
  64. {
  65. }
  66. //==============================================================================
  67. SynthesiserVoice* Synthesiser::getVoice (const int index) const
  68. {
  69. const ScopedLock sl (lock);
  70. return voices [index];
  71. }
  72. void Synthesiser::clearVoices()
  73. {
  74. const ScopedLock sl (lock);
  75. voices.clear();
  76. }
  77. SynthesiserVoice* Synthesiser::addVoice (SynthesiserVoice* const newVoice)
  78. {
  79. const ScopedLock sl (lock);
  80. return voices.add (newVoice);
  81. }
  82. void Synthesiser::removeVoice (const int index)
  83. {
  84. const ScopedLock sl (lock);
  85. voices.remove (index);
  86. }
  87. void Synthesiser::clearSounds()
  88. {
  89. const ScopedLock sl (lock);
  90. sounds.clear();
  91. }
  92. SynthesiserSound* Synthesiser::addSound (const SynthesiserSound::Ptr& newSound)
  93. {
  94. const ScopedLock sl (lock);
  95. return sounds.add (newSound);
  96. }
  97. void Synthesiser::removeSound (const int index)
  98. {
  99. const ScopedLock sl (lock);
  100. sounds.remove (index);
  101. }
  102. void Synthesiser::setNoteStealingEnabled (const bool shouldSteal)
  103. {
  104. shouldStealNotes = shouldSteal;
  105. }
  106. //==============================================================================
  107. void Synthesiser::setCurrentPlaybackSampleRate (const double newRate)
  108. {
  109. if (sampleRate != newRate)
  110. {
  111. const ScopedLock sl (lock);
  112. allNotesOff (0, false);
  113. sampleRate = newRate;
  114. for (int i = voices.size(); --i >= 0;)
  115. voices.getUnchecked (i)->setCurrentPlaybackSampleRate (newRate);
  116. }
  117. }
  118. void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBuffer& midiData,
  119. int startSample, 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. renderVoices (outputBuffer, startSample, numThisTime);
  136. if (useEvent)
  137. handleMidiEvent (m);
  138. startSample += numThisTime;
  139. numSamples -= numThisTime;
  140. }
  141. }
  142. void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int numSamples)
  143. {
  144. for (int i = voices.size(); --i >= 0;)
  145. voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples);
  146. }
  147. void Synthesiser::handleMidiEvent (const MidiMessage& m)
  148. {
  149. if (m.isNoteOn())
  150. {
  151. noteOn (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity());
  152. }
  153. else if (m.isNoteOff())
  154. {
  155. noteOff (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity(), 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.isAftertouch())
  169. {
  170. handleAftertouch (m.getChannel(), m.getNoteNumber(), m.getAfterTouchValue());
  171. }
  172. else if (m.isController())
  173. {
  174. handleController (m.getChannel(), m.getControllerNumber(), 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, 1.0f, true);
  197. }
  198. startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, 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 (0.0f, 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, float velocity, const bool allowTailOff)
  223. {
  224. jassert (voice != nullptr);
  225. voice->stopNote (velocity, 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 float velocity,
  232. const bool allowTailOff)
  233. {
  234. const ScopedLock sl (lock);
  235. for (int i = voices.size(); --i >= 0;)
  236. {
  237. SynthesiserVoice* const voice = voices.getUnchecked (i);
  238. if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
  239. {
  240. if (SynthesiserSound* const sound = voice->getCurrentlyPlayingSound())
  241. {
  242. if (sound->appliesToNote (midiNoteNumber)
  243. && sound->appliesToChannel (midiChannel))
  244. {
  245. voice->keyIsDown = false;
  246. if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown))
  247. stopVoice (voice, velocity, allowTailOff);
  248. }
  249. }
  250. }
  251. }
  252. }
  253. void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
  254. {
  255. const ScopedLock sl (lock);
  256. for (int i = voices.size(); --i >= 0;)
  257. {
  258. SynthesiserVoice* const voice = voices.getUnchecked (i);
  259. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  260. voice->stopNote (1.0f, allowTailOff);
  261. }
  262. sustainPedalsDown.clear();
  263. }
  264. void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue)
  265. {
  266. const ScopedLock sl (lock);
  267. for (int i = voices.size(); --i >= 0;)
  268. {
  269. SynthesiserVoice* const voice = voices.getUnchecked (i);
  270. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  271. voice->pitchWheelMoved (wheelValue);
  272. }
  273. }
  274. void Synthesiser::handleController (const int midiChannel,
  275. const int controllerNumber,
  276. const int controllerValue)
  277. {
  278. switch (controllerNumber)
  279. {
  280. case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break;
  281. case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break;
  282. case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break;
  283. default: break;
  284. }
  285. const ScopedLock sl (lock);
  286. for (int i = voices.size(); --i >= 0;)
  287. {
  288. SynthesiserVoice* const voice = voices.getUnchecked (i);
  289. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  290. voice->controllerMoved (controllerNumber, controllerValue);
  291. }
  292. }
  293. void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue)
  294. {
  295. const ScopedLock sl (lock);
  296. for (int i = voices.size(); --i >= 0;)
  297. {
  298. SynthesiserVoice* const voice = voices.getUnchecked (i);
  299. if (voice->getCurrentlyPlayingNote() == midiNoteNumber
  300. && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)))
  301. voice->aftertouchChanged (aftertouchValue);
  302. }
  303. }
  304. void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
  305. {
  306. jassert (midiChannel > 0 && midiChannel <= 16);
  307. const ScopedLock sl (lock);
  308. if (isDown)
  309. {
  310. sustainPedalsDown.setBit (midiChannel);
  311. }
  312. else
  313. {
  314. for (int i = voices.size(); --i >= 0;)
  315. {
  316. SynthesiserVoice* const voice = voices.getUnchecked (i);
  317. if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown)
  318. stopVoice (voice, 1.0f, true);
  319. }
  320. sustainPedalsDown.clearBit (midiChannel);
  321. }
  322. }
  323. void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
  324. {
  325. jassert (midiChannel > 0 && midiChannel <= 16);
  326. const ScopedLock sl (lock);
  327. for (int i = voices.size(); --i >= 0;)
  328. {
  329. SynthesiserVoice* const voice = voices.getUnchecked (i);
  330. if (voice->isPlayingChannel (midiChannel))
  331. {
  332. if (isDown)
  333. voice->sostenutoPedalDown = true;
  334. else if (voice->sostenutoPedalDown)
  335. stopVoice (voice, 1.0f, true);
  336. }
  337. }
  338. }
  339. void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/)
  340. {
  341. (void) midiChannel;
  342. jassert (midiChannel > 0 && midiChannel <= 16);
  343. }
  344. //==============================================================================
  345. SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay,
  346. int midiChannel, int midiNoteNumber,
  347. const bool stealIfNoneAvailable) const
  348. {
  349. const ScopedLock sl (lock);
  350. for (int i = 0; i < voices.size(); ++i)
  351. {
  352. SynthesiserVoice* const voice = voices.getUnchecked (i);
  353. if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay))
  354. return voice;
  355. }
  356. if (stealIfNoneAvailable)
  357. return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber);
  358. return nullptr;
  359. }
  360. struct VoiceAgeSorter
  361. {
  362. static int compareElements (SynthesiserVoice* v1, SynthesiserVoice* v2) noexcept
  363. {
  364. return v1->wasStartedBefore (*v2) ? 1 : (v2->wasStartedBefore (*v1) ? -1 : 0);
  365. }
  366. };
  367. SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
  368. int /*midiChannel*/, int midiNoteNumber) const
  369. {
  370. SynthesiserVoice* bottom = nullptr;
  371. SynthesiserVoice* top = nullptr;
  372. // this is a list of voices we can steal, sorted by how long they've been running
  373. Array<SynthesiserVoice*> usableVoices;
  374. usableVoices.ensureStorageAllocated (voices.size());
  375. for (int i = 0; i < voices.size(); ++i)
  376. {
  377. SynthesiserVoice* const voice = voices.getUnchecked (i);
  378. if (voice->canPlaySound (soundToPlay))
  379. {
  380. VoiceAgeSorter sorter;
  381. usableVoices.addSorted (sorter, voice);
  382. const int note = voice->getCurrentlyPlayingNote();
  383. if (bottom == nullptr || note < bottom->getCurrentlyPlayingNote())
  384. bottom = voice;
  385. if (top == nullptr || note > top->getCurrentlyPlayingNote())
  386. top = voice;
  387. }
  388. }
  389. jassert (bottom != nullptr && top != nullptr);
  390. // The oldest note that's playing with the target pitch playing is ideal..
  391. for (int i = 0; i < usableVoices.size(); ++i)
  392. {
  393. SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
  394. if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
  395. return voice;
  396. }
  397. // ..otherwise, look for the oldest note that isn't the top or bottom note..
  398. for (int i = 0; i < usableVoices.size(); ++i)
  399. {
  400. SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
  401. if (voice != bottom && voice != top)
  402. return voice;
  403. }
  404. // ..otherwise, there's only one or two voices to choose from - we'll return the top one..
  405. return top;
  406. }