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.

433 lines
13KB

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