Audio plugin host https://kx.studio/carla
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.

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