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.

380 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file was auto-generated by the Jucer!
  4. It contains the basic startup code for a Juce application.
  5. ==============================================================================
  6. */
  7. #include "PluginProcessor.h"
  8. #include "PluginEditor.h"
  9. //==============================================================================
  10. /** A demo synth sound that's just a basic sine wave.. */
  11. class SineWaveSound : public SynthesiserSound
  12. {
  13. public:
  14. SineWaveSound()
  15. {
  16. }
  17. bool appliesToNote (const int /*midiNoteNumber*/) { return true; }
  18. bool appliesToChannel (const int /*midiChannel*/) { return true; }
  19. };
  20. //==============================================================================
  21. /** A simple demo synth voice that just plays a sine wave.. */
  22. class SineWaveVoice : public SynthesiserVoice
  23. {
  24. public:
  25. SineWaveVoice()
  26. : angleDelta (0.0),
  27. tailOff (0.0)
  28. {
  29. }
  30. bool canPlaySound (SynthesiserSound* sound)
  31. {
  32. return dynamic_cast <SineWaveSound*> (sound) != 0;
  33. }
  34. void startNote (const int midiNoteNumber, const float velocity,
  35. SynthesiserSound* /*sound*/, const int /*currentPitchWheelPosition*/)
  36. {
  37. currentAngle = 0.0;
  38. level = velocity * 0.15;
  39. tailOff = 0.0;
  40. double cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
  41. double cyclesPerSample = cyclesPerSecond / getSampleRate();
  42. angleDelta = cyclesPerSample * 2.0 * double_Pi;
  43. }
  44. void stopNote (const bool allowTailOff)
  45. {
  46. if (allowTailOff)
  47. {
  48. // start a tail-off by setting this flag. The render callback will pick up on
  49. // this and do a fade out, calling clearCurrentNote() when it's finished.
  50. if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the
  51. // stopNote method could be called more than once.
  52. tailOff = 1.0;
  53. }
  54. else
  55. {
  56. // we're being told to stop playing immediately, so reset everything..
  57. clearCurrentNote();
  58. angleDelta = 0.0;
  59. }
  60. }
  61. void pitchWheelMoved (const int /*newValue*/)
  62. {
  63. // can't be bothered implementing this for the demo!
  64. }
  65. void controllerMoved (const int /*controllerNumber*/, const int /*newValue*/)
  66. {
  67. // not interested in controllers in this case.
  68. }
  69. void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples)
  70. {
  71. if (angleDelta != 0.0)
  72. {
  73. if (tailOff > 0)
  74. {
  75. while (--numSamples >= 0)
  76. {
  77. const float currentSample = (float) (sin (currentAngle) * level * tailOff);
  78. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  79. *outputBuffer.getSampleData (i, startSample) += currentSample;
  80. currentAngle += angleDelta;
  81. ++startSample;
  82. tailOff *= 0.99;
  83. if (tailOff <= 0.005)
  84. {
  85. clearCurrentNote();
  86. angleDelta = 0.0;
  87. break;
  88. }
  89. }
  90. }
  91. else
  92. {
  93. while (--numSamples >= 0)
  94. {
  95. const float currentSample = (float) (sin (currentAngle) * level);
  96. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  97. *outputBuffer.getSampleData (i, startSample) += currentSample;
  98. currentAngle += angleDelta;
  99. ++startSample;
  100. }
  101. }
  102. }
  103. }
  104. private:
  105. double currentAngle, angleDelta, level, tailOff;
  106. };
  107. //==============================================================================
  108. JuceDemoPluginAudioProcessor::JuceDemoPluginAudioProcessor()
  109. : delayBuffer (2, 12000)
  110. {
  111. // Set up some default values..
  112. gain = 1.0f;
  113. delay = 0.5f;
  114. lastUIWidth = 400;
  115. lastUIHeight = 200;
  116. lastPosInfo.resetToDefault();
  117. delayPosition = 0;
  118. // Initialise the synth...
  119. for (int i = 4; --i >= 0;)
  120. synth.addVoice (new SineWaveVoice()); // These voices will play our custom sine-wave sounds..
  121. synth.addSound (new SineWaveSound());
  122. }
  123. JuceDemoPluginAudioProcessor::~JuceDemoPluginAudioProcessor()
  124. {
  125. }
  126. //==============================================================================
  127. int JuceDemoPluginAudioProcessor::getNumParameters()
  128. {
  129. return totalNumParams;
  130. }
  131. float JuceDemoPluginAudioProcessor::getParameter (int index)
  132. {
  133. // This method will be called by the host, probably on the audio thread, so
  134. // it's absolutely time-critical. Don't use critical sections or anything
  135. // UI-related, or anything at all that may block in any way!
  136. switch (index)
  137. {
  138. case gainParam: return gain;
  139. case delayParam: return delay;
  140. default: return 0.0f;
  141. }
  142. }
  143. void JuceDemoPluginAudioProcessor::setParameter (int index, float newValue)
  144. {
  145. // This method will be called by the host, probably on the audio thread, so
  146. // it's absolutely time-critical. Don't use critical sections or anything
  147. // UI-related, or anything at all that may block in any way!
  148. switch (index)
  149. {
  150. case gainParam: gain = newValue; break;
  151. case delayParam: delay = newValue; break;
  152. default: break;
  153. }
  154. }
  155. const String JuceDemoPluginAudioProcessor::getParameterName (int index)
  156. {
  157. switch (index)
  158. {
  159. case gainParam: return "gain";
  160. case delayParam: return "delay";
  161. default: break;
  162. }
  163. return String::empty;
  164. }
  165. const String JuceDemoPluginAudioProcessor::getParameterText (int index)
  166. {
  167. return String (getParameter (index), 2);
  168. }
  169. //==============================================================================
  170. void JuceDemoPluginAudioProcessor::prepareToPlay (double sampleRate, int /*samplesPerBlock*/)
  171. {
  172. // Use this method as the place to do any pre-playback
  173. // initialisation that you need..
  174. synth.setCurrentPlaybackSampleRate (sampleRate);
  175. keyboardState.reset();
  176. delayBuffer.clear();
  177. }
  178. void JuceDemoPluginAudioProcessor::releaseResources()
  179. {
  180. // When playback stops, you can use this as an opportunity to free up any
  181. // spare memory, etc.
  182. keyboardState.reset();
  183. }
  184. void JuceDemoPluginAudioProcessor::reset()
  185. {
  186. // Use this method as the place to clear any delay lines, buffers, etc, as it
  187. // means there's been a break in the audio's continuity.
  188. delayBuffer.clear();
  189. }
  190. void JuceDemoPluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
  191. {
  192. const int numSamples = buffer.getNumSamples();
  193. int channel, dp = 0;
  194. // Go through the incoming data, and apply our gain to it...
  195. for (channel = 0; channel < getNumInputChannels(); ++channel)
  196. buffer.applyGain (channel, 0, buffer.getNumSamples(), gain);
  197. // Now pass any incoming midi messages to our keyboard state object, and let it
  198. // add messages to the buffer if the user is clicking on the on-screen keys
  199. keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
  200. // and now get the synth to process these midi events and generate its output.
  201. synth.renderNextBlock (buffer, midiMessages, 0, numSamples);
  202. // Apply our delay effect to the new output..
  203. for (channel = 0; channel < getNumInputChannels(); ++channel)
  204. {
  205. float* channelData = buffer.getSampleData (channel);
  206. float* delayData = delayBuffer.getSampleData (jmin (channel, delayBuffer.getNumChannels() - 1));
  207. dp = delayPosition;
  208. for (int i = 0; i < numSamples; ++i)
  209. {
  210. const float in = channelData[i];
  211. channelData[i] += delayData[dp];
  212. delayData[dp] = (delayData[dp] + in) * delay;
  213. if (++dp >= delayBuffer.getNumSamples())
  214. dp = 0;
  215. }
  216. }
  217. delayPosition = dp;
  218. // In case we have more outputs than inputs, we'll clear any output
  219. // channels that didn't contain input data, (because these aren't
  220. // guaranteed to be empty - they may contain garbage).
  221. for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
  222. buffer.clear (i, 0, buffer.getNumSamples());
  223. // ask the host for the current time so we can display it...
  224. AudioPlayHead::CurrentPositionInfo newTime;
  225. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (newTime))
  226. {
  227. // Successfully got the current time from the host..
  228. lastPosInfo = newTime;
  229. }
  230. else
  231. {
  232. // If the host fails to fill-in the current time, we'll just clear it to a default..
  233. lastPosInfo.resetToDefault();
  234. }
  235. }
  236. //==============================================================================
  237. AudioProcessorEditor* JuceDemoPluginAudioProcessor::createEditor()
  238. {
  239. return new JuceDemoPluginAudioProcessorEditor (this);
  240. }
  241. //==============================================================================
  242. void JuceDemoPluginAudioProcessor::getStateInformation (MemoryBlock& destData)
  243. {
  244. // You should use this method to store your parameters in the memory block.
  245. // Here's an example of how you can use XML to make it easy and more robust:
  246. // Create an outer XML element..
  247. XmlElement xml ("MYPLUGINSETTINGS");
  248. // add some attributes to it..
  249. xml.setAttribute ("uiWidth", lastUIWidth);
  250. xml.setAttribute ("uiHeight", lastUIHeight);
  251. xml.setAttribute ("gain", gain);
  252. xml.setAttribute ("delay", delay);
  253. // then use this helper function to stuff it into the binary blob and return it..
  254. copyXmlToBinary (xml, destData);
  255. }
  256. void JuceDemoPluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
  257. {
  258. // You should use this method to restore your parameters from this memory block,
  259. // whose contents will have been created by the getStateInformation() call.
  260. // This getXmlFromBinary() helper function retrieves our XML from the binary blob..
  261. ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
  262. if (xmlState != 0)
  263. {
  264. // make sure that it's actually our type of XML object..
  265. if (xmlState->hasTagName ("MYPLUGINSETTINGS"))
  266. {
  267. // ok, now pull out our parameters..
  268. lastUIWidth = xmlState->getIntAttribute ("uiWidth", lastUIWidth);
  269. lastUIHeight = xmlState->getIntAttribute ("uiHeight", lastUIHeight);
  270. gain = (float) xmlState->getDoubleAttribute ("gain", gain);
  271. delay = (float) xmlState->getDoubleAttribute ("delay", delay);
  272. }
  273. }
  274. }
  275. const String JuceDemoPluginAudioProcessor::getInputChannelName (const int channelIndex) const
  276. {
  277. return String (channelIndex + 1);
  278. }
  279. const String JuceDemoPluginAudioProcessor::getOutputChannelName (const int channelIndex) const
  280. {
  281. return String (channelIndex + 1);
  282. }
  283. bool JuceDemoPluginAudioProcessor::isInputChannelStereoPair (int /*index*/) const
  284. {
  285. return true;
  286. }
  287. bool JuceDemoPluginAudioProcessor::isOutputChannelStereoPair (int /*index*/) const
  288. {
  289. return true;
  290. }
  291. bool JuceDemoPluginAudioProcessor::acceptsMidi() const
  292. {
  293. #if JucePlugin_WantsMidiInput
  294. return true;
  295. #else
  296. return false;
  297. #endif
  298. }
  299. bool JuceDemoPluginAudioProcessor::producesMidi() const
  300. {
  301. #if JucePlugin_ProducesMidiOutput
  302. return true;
  303. #else
  304. return false;
  305. #endif
  306. }
  307. //==============================================================================
  308. // This creates new instances of the plugin..
  309. AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  310. {
  311. return new JuceDemoPluginAudioProcessor();
  312. }