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.

368 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_Synthesiser.h"
  21. //==============================================================================
  22. SynthesiserSound::SynthesiserSound()
  23. {
  24. }
  25. SynthesiserSound::~SynthesiserSound()
  26. {
  27. }
  28. //==============================================================================
  29. SynthesiserVoice::SynthesiserVoice()
  30. : currentSampleRate (44100.0),
  31. currentlyPlayingNote (-1),
  32. noteOnTime (0),
  33. currentlyPlayingSound (0)
  34. {
  35. }
  36. SynthesiserVoice::~SynthesiserVoice()
  37. {
  38. }
  39. bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const
  40. {
  41. return currentlyPlayingSound != 0
  42. && currentlyPlayingSound->appliesToChannel (midiChannel);
  43. }
  44. void SynthesiserVoice::setCurrentPlaybackSampleRate (const double newRate)
  45. {
  46. currentSampleRate = newRate;
  47. }
  48. void SynthesiserVoice::clearCurrentNote()
  49. {
  50. currentlyPlayingNote = -1;
  51. currentlyPlayingSound = 0;
  52. }
  53. //==============================================================================
  54. Synthesiser::Synthesiser()
  55. : sampleRate (0),
  56. lastNoteOnCounter (0),
  57. shouldStealNotes (true)
  58. {
  59. for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i)
  60. lastPitchWheelValues[i] = 0x2000;
  61. }
  62. Synthesiser::~Synthesiser()
  63. {
  64. }
  65. //==============================================================================
  66. SynthesiserVoice* Synthesiser::getVoice (const int index) const
  67. {
  68. const ScopedLock sl (lock);
  69. return voices [index];
  70. }
  71. void Synthesiser::clearVoices()
  72. {
  73. const ScopedLock sl (lock);
  74. voices.clear();
  75. }
  76. void Synthesiser::addVoice (SynthesiserVoice* const newVoice)
  77. {
  78. const ScopedLock sl (lock);
  79. voices.add (newVoice);
  80. }
  81. void Synthesiser::removeVoice (const int index)
  82. {
  83. const ScopedLock sl (lock);
  84. voices.remove (index);
  85. }
  86. void Synthesiser::clearSounds()
  87. {
  88. const ScopedLock sl (lock);
  89. sounds.clear();
  90. }
  91. void Synthesiser::addSound (const SynthesiserSound::Ptr& newSound)
  92. {
  93. const ScopedLock sl (lock);
  94. sounds.add (newSound);
  95. }
  96. void Synthesiser::removeSound (const int index)
  97. {
  98. const ScopedLock sl (lock);
  99. sounds.remove (index);
  100. }
  101. void Synthesiser::setNoteStealingEnabled (const bool shouldStealNotes_)
  102. {
  103. shouldStealNotes = shouldStealNotes_;
  104. }
  105. //==============================================================================
  106. void Synthesiser::setCurrentPlaybackSampleRate (const double newRate)
  107. {
  108. if (sampleRate != newRate)
  109. {
  110. const ScopedLock sl (lock);
  111. allNotesOff (0, false);
  112. sampleRate = newRate;
  113. for (int i = voices.size(); --i >= 0;)
  114. voices.getUnchecked (i)->setCurrentPlaybackSampleRate (newRate);
  115. }
  116. }
  117. void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer,
  118. const MidiBuffer& midiData,
  119. int startSample,
  120. 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. {
  137. for (int i = voices.size(); --i >= 0;)
  138. voices.getUnchecked (i)->renderNextBlock (outputBuffer, startSample, numThisTime);
  139. }
  140. if (useEvent)
  141. {
  142. if (m.isNoteOn())
  143. {
  144. const int channel = m.getChannel();
  145. noteOn (channel,
  146. m.getNoteNumber(),
  147. m.getFloatVelocity());
  148. }
  149. else if (m.isNoteOff())
  150. {
  151. noteOff (m.getChannel(),
  152. m.getNoteNumber(),
  153. true);
  154. }
  155. else if (m.isAllNotesOff() || m.isAllSoundOff())
  156. {
  157. allNotesOff (m.getChannel(), true);
  158. }
  159. else if (m.isPitchWheel())
  160. {
  161. const int channel = m.getChannel();
  162. const int wheelPos = m.getPitchWheelValue();
  163. lastPitchWheelValues [channel - 1] = wheelPos;
  164. handlePitchWheel (channel, wheelPos);
  165. }
  166. else if (m.isController())
  167. {
  168. handleController (m.getChannel(),
  169. m.getControllerNumber(),
  170. m.getControllerValue());
  171. }
  172. }
  173. startSample += numThisTime;
  174. numSamples -= numThisTime;
  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. startVoice (findFreeVoice (sound, shouldStealNotes),
  190. sound, midiChannel, midiNoteNumber, velocity);
  191. }
  192. }
  193. }
  194. void Synthesiser::startVoice (SynthesiserVoice* const voice,
  195. SynthesiserSound* const sound,
  196. const int midiChannel,
  197. const int midiNoteNumber,
  198. const float velocity)
  199. {
  200. if (voice != 0 && sound != 0)
  201. {
  202. if (voice->currentlyPlayingSound != 0)
  203. voice->stopNote (false);
  204. voice->startNote (midiNoteNumber,
  205. velocity,
  206. sound,
  207. lastPitchWheelValues [midiChannel - 1]);
  208. voice->currentlyPlayingNote = midiNoteNumber;
  209. voice->noteOnTime = ++lastNoteOnCounter;
  210. voice->currentlyPlayingSound = sound;
  211. }
  212. }
  213. void Synthesiser::noteOff (const int midiChannel,
  214. const int midiNoteNumber,
  215. const bool allowTailOff)
  216. {
  217. const ScopedLock sl (lock);
  218. for (int i = voices.size(); --i >= 0;)
  219. {
  220. SynthesiserVoice* const voice = voices.getUnchecked (i);
  221. if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
  222. {
  223. SynthesiserSound* const sound = voice->getCurrentlyPlayingSound();
  224. if (sound != 0
  225. && sound->appliesToNote (midiNoteNumber)
  226. && sound->appliesToChannel (midiChannel))
  227. {
  228. voice->stopNote (allowTailOff);
  229. // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
  230. jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0));
  231. }
  232. }
  233. }
  234. }
  235. void Synthesiser::allNotesOff (const int midiChannel,
  236. const bool allowTailOff)
  237. {
  238. const ScopedLock sl (lock);
  239. for (int i = voices.size(); --i >= 0;)
  240. {
  241. SynthesiserVoice* const voice = voices.getUnchecked (i);
  242. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  243. voice->stopNote (allowTailOff);
  244. }
  245. }
  246. void Synthesiser::handlePitchWheel (const int midiChannel,
  247. const int wheelValue)
  248. {
  249. const ScopedLock sl (lock);
  250. for (int i = voices.size(); --i >= 0;)
  251. {
  252. SynthesiserVoice* const voice = voices.getUnchecked (i);
  253. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  254. {
  255. voice->pitchWheelMoved (wheelValue);
  256. }
  257. }
  258. }
  259. void Synthesiser::handleController (const int midiChannel,
  260. const int controllerNumber,
  261. const int controllerValue)
  262. {
  263. const ScopedLock sl (lock);
  264. for (int i = voices.size(); --i >= 0;)
  265. {
  266. SynthesiserVoice* const voice = voices.getUnchecked (i);
  267. if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
  268. voice->controllerMoved (controllerNumber, controllerValue);
  269. }
  270. }
  271. //==============================================================================
  272. SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay,
  273. const bool stealIfNoneAvailable) const
  274. {
  275. const ScopedLock sl (lock);
  276. for (int i = voices.size(); --i >= 0;)
  277. if (voices.getUnchecked (i)->getCurrentlyPlayingNote() < 0
  278. && voices.getUnchecked (i)->canPlaySound (soundToPlay))
  279. return voices.getUnchecked (i);
  280. if (stealIfNoneAvailable)
  281. {
  282. // currently this just steals the one that's been playing the longest, but could be made a bit smarter..
  283. SynthesiserVoice* oldest = 0;
  284. for (int i = voices.size(); --i >= 0;)
  285. {
  286. SynthesiserVoice* const voice = voices.getUnchecked (i);
  287. if (voice->canPlaySound (soundToPlay)
  288. && (oldest == 0 || oldest->noteOnTime > voice->noteOnTime))
  289. oldest = voice;
  290. }
  291. jassert (oldest != 0);
  292. return oldest;
  293. }
  294. return 0;
  295. }
  296. END_JUCE_NAMESPACE