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.

423 lines
12KB

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