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.

539 lines
16KB

  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. currentPlayingMidiChannel (0),
  24. noteOnTime (0),
  25. keyIsDown (false),
  26. sostenutoPedalDown (false)
  27. {
  28. }
  29. SynthesiserVoice::~SynthesiserVoice()
  30. {
  31. }
  32. bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const
  33. {
  34. return currentPlayingMidiChannel == 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. currentPlayingMidiChannel = 0;
  49. }
  50. void SynthesiserVoice::aftertouchChanged (int) {}
  51. bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept
  52. {
  53. return noteOnTime < other.noteOnTime;
  54. }
  55. //==============================================================================
  56. Synthesiser::Synthesiser()
  57. : sampleRate (0),
  58. lastNoteOnCounter (0),
  59. minimumSubBlockSize (32),
  60. shouldStealNotes (true)
  61. {
  62. for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i)
  63. lastPitchWheelValues[i] = 0x2000;
  64. }
  65. Synthesiser::~Synthesiser()
  66. {
  67. }
  68. //==============================================================================
  69. SynthesiserVoice* Synthesiser::getVoice (const int index) const
  70. {
  71. const ScopedLock sl (lock);
  72. return voices [index];
  73. }
  74. void Synthesiser::clearVoices()
  75. {
  76. const ScopedLock sl (lock);
  77. voices.clear();
  78. }
  79. SynthesiserVoice* Synthesiser::addVoice (SynthesiserVoice* const newVoice)
  80. {
  81. const ScopedLock sl (lock);
  82. return voices.add (newVoice);
  83. }
  84. void Synthesiser::removeVoice (const int index)
  85. {
  86. const ScopedLock sl (lock);
  87. voices.remove (index);
  88. }
  89. void Synthesiser::clearSounds()
  90. {
  91. const ScopedLock sl (lock);
  92. sounds.clear();
  93. }
  94. SynthesiserSound* Synthesiser::addSound (const SynthesiserSound::Ptr& newSound)
  95. {
  96. const ScopedLock sl (lock);
  97. return sounds.add (newSound);
  98. }
  99. void Synthesiser::removeSound (const int index)
  100. {
  101. const ScopedLock sl (lock);
  102. sounds.remove (index);
  103. }
  104. void Synthesiser::setNoteStealingEnabled (const bool shouldSteal)
  105. {
  106. shouldStealNotes = shouldSteal;
  107. }
  108. void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples) noexcept
  109. {
  110. jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1
  111. minimumSubBlockSize = numSamples;
  112. }
  113. //==============================================================================
  114. void Synthesiser::setCurrentPlaybackSampleRate (const double newRate)
  115. {
  116. if (sampleRate != newRate)
  117. {
  118. const ScopedLock sl (lock);
  119. allNotesOff (0, false);
  120. sampleRate = newRate;
  121. for (int i = voices.size(); --i >= 0;)
  122. voices.getUnchecked (i)->setCurrentPlaybackSampleRate (newRate);
  123. }
  124. }
  125. void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBuffer& midiData,
  126. int startSample, int numSamples)
  127. {
  128. // must set the sample rate before using this!
  129. jassert (sampleRate != 0);
  130. MidiBuffer::Iterator midiIterator (midiData);
  131. midiIterator.setNextSamplePosition (startSample);
  132. int midiEventPos;
  133. MidiMessage m;
  134. const ScopedLock sl (lock);
  135. while (numSamples > 0)
  136. {
  137. if (! midiIterator.getNextEvent (m, midiEventPos))
  138. {
  139. renderVoices (outputBuffer, startSample, numSamples);
  140. return;
  141. }
  142. const int samplesToNextMidiMessage = midiEventPos - startSample;
  143. if (samplesToNextMidiMessage >= numSamples)
  144. {
  145. renderVoices (outputBuffer, startSample, numSamples);
  146. handleMidiEvent (m);
  147. break;
  148. }
  149. if (samplesToNextMidiMessage < minimumSubBlockSize)
  150. {
  151. handleMidiEvent (m);
  152. continue;
  153. }
  154. renderVoices (outputBuffer, startSample, samplesToNextMidiMessage);
  155. handleMidiEvent (m);
  156. startSample += samplesToNextMidiMessage;
  157. numSamples -= samplesToNextMidiMessage;
  158. }
  159. while (midiIterator.getNextEvent (m, midiEventPos))
  160. handleMidiEvent (m);
  161. }
  162. void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int numSamples)
  163. {
  164. for (int i = voices.size(); --i >= 0;)
  165. voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples);
  166. }
  167. void Synthesiser::handleMidiEvent (const MidiMessage& m)
  168. {
  169. if (m.isNoteOn())
  170. {
  171. noteOn (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity());
  172. }
  173. else if (m.isNoteOff())
  174. {
  175. noteOff (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity(), true);
  176. }
  177. else if (m.isAllNotesOff() || m.isAllSoundOff())
  178. {
  179. allNotesOff (m.getChannel(), true);
  180. }
  181. else if (m.isPitchWheel())
  182. {
  183. const int channel = m.getChannel();
  184. const int wheelPos = m.getPitchWheelValue();
  185. lastPitchWheelValues [channel - 1] = wheelPos;
  186. handlePitchWheel (channel, wheelPos);
  187. }
  188. else if (m.isAftertouch())
  189. {
  190. handleAftertouch (m.getChannel(), m.getNoteNumber(), m.getAfterTouchValue());
  191. }
  192. else if (m.isController())
  193. {
  194. handleController (m.getChannel(), m.getControllerNumber(), m.getControllerValue());
  195. }
  196. else if (m.isProgramChange())
  197. {
  198. handleProgramChange (m.getChannel(), m.getProgramChangeNumber());
  199. }
  200. }
  201. //==============================================================================
  202. void Synthesiser::noteOn (const int midiChannel,
  203. const int midiNoteNumber,
  204. const float velocity)
  205. {
  206. const ScopedLock sl (lock);
  207. for (int i = sounds.size(); --i >= 0;)
  208. {
  209. SynthesiserSound* const sound = sounds.getUnchecked(i);
  210. if (sound->appliesToNote (midiNoteNumber)
  211. && sound->appliesToChannel (midiChannel))
  212. {
  213. // If hitting a note that's still ringing, stop it first (it could be
  214. // still playing because of the sustain or sostenuto pedal).
  215. for (int j = voices.size(); --j >= 0;)
  216. {
  217. SynthesiserVoice* const voice = voices.getUnchecked (j);
  218. if (voice->getCurrentlyPlayingNote() == midiNoteNumber
  219. && voice->isPlayingChannel (midiChannel))
  220. stopVoice (voice, 1.0f, true);
  221. }
  222. startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
  223. sound, midiChannel, midiNoteNumber, velocity);
  224. }
  225. }
  226. }
  227. void Synthesiser::startVoice (SynthesiserVoice* const voice,
  228. SynthesiserSound* const sound,
  229. const int midiChannel,
  230. const int midiNoteNumber,
  231. const float velocity)
  232. {
  233. if (voice != nullptr && sound != nullptr)
  234. {
  235. if (voice->currentlyPlayingSound != nullptr)
  236. voice->stopNote (0.0f, false);
  237. voice->currentlyPlayingNote = midiNoteNumber;
  238. voice->currentPlayingMidiChannel = midiChannel;
  239. voice->noteOnTime = ++lastNoteOnCounter;
  240. voice->currentlyPlayingSound = sound;
  241. voice->keyIsDown = true;
  242. voice->sostenutoPedalDown = false;
  243. voice->startNote (midiNoteNumber, velocity, sound,
  244. lastPitchWheelValues [midiChannel - 1]);
  245. }
  246. }
  247. void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool allowTailOff)
  248. {
  249. jassert (voice != nullptr);
  250. voice->stopNote (velocity, allowTailOff);
  251. // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
  252. jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0));
  253. }
  254. void Synthesiser::noteOff (const int midiChannel,
  255. const int midiNoteNumber,
  256. const float velocity,
  257. const bool allowTailOff)
  258. {
  259. const ScopedLock sl (lock);
  260. for (int i = voices.size(); --i >= 0;)
  261. {
  262. SynthesiserVoice* const voice = voices.getUnchecked (i);
  263. if (voice->getCurrentlyPlayingNote() == midiNoteNumber
  264. && voice->isPlayingChannel (midiChannel))
  265. {
  266. if (SynthesiserSound* const sound = voice->getCurrentlyPlayingSound())
  267. {
  268. if (sound->appliesToNote (midiNoteNumber)
  269. && sound->appliesToChannel (midiChannel))
  270. {
  271. voice->keyIsDown = false;
  272. if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown))
  273. stopVoice (voice, velocity, allowTailOff);
  274. }
  275. }
  276. }
  277. }
  278. }
  279. void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
  280. {
  281. const ScopedLock sl (lock);
  282. for (int i = voices.size(); --i >= 0;)
  283. {
  284. SynthesiserVoice* const voice = voices.getUnchecked (i);
  285. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  286. voice->stopNote (1.0f, allowTailOff);
  287. }
  288. sustainPedalsDown.clear();
  289. }
  290. void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue)
  291. {
  292. const ScopedLock sl (lock);
  293. for (int i = voices.size(); --i >= 0;)
  294. {
  295. SynthesiserVoice* const voice = voices.getUnchecked (i);
  296. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  297. voice->pitchWheelMoved (wheelValue);
  298. }
  299. }
  300. void Synthesiser::handleController (const int midiChannel,
  301. const int controllerNumber,
  302. const int controllerValue)
  303. {
  304. switch (controllerNumber)
  305. {
  306. case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break;
  307. case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break;
  308. case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break;
  309. default: break;
  310. }
  311. const ScopedLock sl (lock);
  312. for (int i = voices.size(); --i >= 0;)
  313. {
  314. SynthesiserVoice* const voice = voices.getUnchecked (i);
  315. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  316. voice->controllerMoved (controllerNumber, controllerValue);
  317. }
  318. }
  319. void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue)
  320. {
  321. const ScopedLock sl (lock);
  322. for (int i = voices.size(); --i >= 0;)
  323. {
  324. SynthesiserVoice* const voice = voices.getUnchecked (i);
  325. if (voice->getCurrentlyPlayingNote() == midiNoteNumber
  326. && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)))
  327. voice->aftertouchChanged (aftertouchValue);
  328. }
  329. }
  330. void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
  331. {
  332. jassert (midiChannel > 0 && midiChannel <= 16);
  333. const ScopedLock sl (lock);
  334. if (isDown)
  335. {
  336. sustainPedalsDown.setBit (midiChannel);
  337. }
  338. else
  339. {
  340. for (int i = voices.size(); --i >= 0;)
  341. {
  342. SynthesiserVoice* const voice = voices.getUnchecked (i);
  343. if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown)
  344. stopVoice (voice, 1.0f, true);
  345. }
  346. sustainPedalsDown.clearBit (midiChannel);
  347. }
  348. }
  349. void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
  350. {
  351. jassert (midiChannel > 0 && midiChannel <= 16);
  352. const ScopedLock sl (lock);
  353. for (int i = voices.size(); --i >= 0;)
  354. {
  355. SynthesiserVoice* const voice = voices.getUnchecked (i);
  356. if (voice->isPlayingChannel (midiChannel))
  357. {
  358. if (isDown)
  359. voice->sostenutoPedalDown = true;
  360. else if (voice->sostenutoPedalDown)
  361. stopVoice (voice, 1.0f, true);
  362. }
  363. }
  364. }
  365. void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/)
  366. {
  367. (void) midiChannel;
  368. jassert (midiChannel > 0 && midiChannel <= 16);
  369. }
  370. void Synthesiser::handleProgramChange (int midiChannel, int programNumber)
  371. {
  372. (void) midiChannel; (void) programNumber;
  373. jassert (midiChannel > 0 && midiChannel <= 16);
  374. }
  375. //==============================================================================
  376. SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay,
  377. int midiChannel, int midiNoteNumber,
  378. const bool stealIfNoneAvailable) const
  379. {
  380. const ScopedLock sl (lock);
  381. for (int i = 0; i < voices.size(); ++i)
  382. {
  383. SynthesiserVoice* const voice = voices.getUnchecked (i);
  384. if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay))
  385. return voice;
  386. }
  387. if (stealIfNoneAvailable)
  388. return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber);
  389. return nullptr;
  390. }
  391. struct VoiceAgeSorter
  392. {
  393. static int compareElements (SynthesiserVoice* v1, SynthesiserVoice* v2) noexcept
  394. {
  395. return v1->wasStartedBefore (*v2) ? -1 : (v2->wasStartedBefore (*v1) ? 1 : 0);
  396. }
  397. };
  398. SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
  399. int /*midiChannel*/, int midiNoteNumber) const
  400. {
  401. SynthesiserVoice* bottom = nullptr;
  402. SynthesiserVoice* top = nullptr;
  403. // this is a list of voices we can steal, sorted by how long they've been running
  404. Array<SynthesiserVoice*> usableVoices;
  405. usableVoices.ensureStorageAllocated (voices.size());
  406. for (int i = 0; i < voices.size(); ++i)
  407. {
  408. SynthesiserVoice* const voice = voices.getUnchecked (i);
  409. if (voice->canPlaySound (soundToPlay))
  410. {
  411. VoiceAgeSorter sorter;
  412. usableVoices.addSorted (sorter, voice);
  413. const int note = voice->getCurrentlyPlayingNote();
  414. if (bottom == nullptr || note < bottom->getCurrentlyPlayingNote())
  415. bottom = voice;
  416. if (top == nullptr || note > top->getCurrentlyPlayingNote())
  417. top = voice;
  418. }
  419. }
  420. const int stealableVoiceRange = usableVoices.size() - 6;
  421. // The oldest note that's playing with the target pitch playing is ideal..
  422. for (int i = 0; i < stealableVoiceRange; ++i)
  423. {
  424. SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
  425. if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
  426. return voice;
  427. }
  428. // ..otherwise, look for the oldest note that isn't the top or bottom note..
  429. for (int i = 0; i < stealableVoiceRange; ++i)
  430. {
  431. SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
  432. if (voice != bottom && voice != top)
  433. return voice;
  434. }
  435. // ..otherwise, there's only one or two voices to choose from - we'll return the oldest one..
  436. return usableVoices.getFirst();
  437. }