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.

373 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::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
  185. {
  186. const int numSamples = buffer.getNumSamples();
  187. int channel, dp = 0;
  188. // Go through the incoming data, and apply our gain to it...
  189. for (channel = 0; channel < getNumInputChannels(); ++channel)
  190. buffer.applyGain (channel, 0, buffer.getNumSamples(), gain);
  191. // Now pass any incoming midi messages to our keyboard state object, and let it
  192. // add messages to the buffer if the user is clicking on the on-screen keys
  193. keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
  194. // and now get the synth to process these midi events and generate its output.
  195. synth.renderNextBlock (buffer, midiMessages, 0, numSamples);
  196. // Apply our delay effect to the new output..
  197. for (channel = 0; channel < getNumInputChannels(); ++channel)
  198. {
  199. float* channelData = buffer.getSampleData (channel);
  200. float* delayData = delayBuffer.getSampleData (jmin (channel, delayBuffer.getNumChannels() - 1));
  201. dp = delayPosition;
  202. for (int i = 0; i < numSamples; ++i)
  203. {
  204. const float in = channelData[i];
  205. channelData[i] += delayData[dp];
  206. delayData[dp] = (delayData[dp] + in) * delay;
  207. if (++dp > delayBuffer.getNumSamples())
  208. dp = 0;
  209. }
  210. }
  211. delayPosition = dp;
  212. // In case we have more outputs than inputs, we'll clear any output
  213. // channels that didn't contain input data, (because these aren't
  214. // guaranteed to be empty - they may contain garbage).
  215. for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
  216. buffer.clear (i, 0, buffer.getNumSamples());
  217. // ask the host for the current time so we can display it...
  218. AudioPlayHead::CurrentPositionInfo newTime;
  219. if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (newTime))
  220. {
  221. // Successfully got the current time from the host..
  222. lastPosInfo = newTime;
  223. }
  224. else
  225. {
  226. // If the host fails to fill-in the current time, we'll just clear it to a default..
  227. lastPosInfo.resetToDefault();
  228. }
  229. }
  230. //==============================================================================
  231. AudioProcessorEditor* JuceDemoPluginAudioProcessor::createEditor()
  232. {
  233. return new JuceDemoPluginAudioProcessorEditor (this);
  234. }
  235. //==============================================================================
  236. void JuceDemoPluginAudioProcessor::getStateInformation (MemoryBlock& destData)
  237. {
  238. // You should use this method to store your parameters in the memory block.
  239. // Here's an example of how you can use XML to make it easy and more robust:
  240. // Create an outer XML element..
  241. XmlElement xml ("MYPLUGINSETTINGS");
  242. // add some attributes to it..
  243. xml.setAttribute ("uiWidth", lastUIWidth);
  244. xml.setAttribute ("uiHeight", lastUIHeight);
  245. xml.setAttribute ("gain", gain);
  246. xml.setAttribute ("delay", delay);
  247. // then use this helper function to stuff it into the binary blob and return it..
  248. copyXmlToBinary (xml, destData);
  249. }
  250. void JuceDemoPluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
  251. {
  252. // You should use this method to restore your parameters from this memory block,
  253. // whose contents will have been created by the getStateInformation() call.
  254. // This getXmlFromBinary() helper function retrieves our XML from the binary blob..
  255. ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
  256. if (xmlState != 0)
  257. {
  258. // make sure that it's actually our type of XML object..
  259. if (xmlState->hasTagName ("MYPLUGINSETTINGS"))
  260. {
  261. // ok, now pull out our parameters..
  262. lastUIWidth = xmlState->getIntAttribute ("uiWidth", lastUIWidth);
  263. lastUIHeight = xmlState->getIntAttribute ("uiHeight", lastUIHeight);
  264. gain = (float) xmlState->getDoubleAttribute ("gain", gain);
  265. delay = (float) xmlState->getDoubleAttribute ("delay", delay);
  266. }
  267. }
  268. }
  269. const String JuceDemoPluginAudioProcessor::getInputChannelName (const int channelIndex) const
  270. {
  271. return String (channelIndex + 1);
  272. }
  273. const String JuceDemoPluginAudioProcessor::getOutputChannelName (const int channelIndex) const
  274. {
  275. return String (channelIndex + 1);
  276. }
  277. bool JuceDemoPluginAudioProcessor::isInputChannelStereoPair (int /*index*/) const
  278. {
  279. return true;
  280. }
  281. bool JuceDemoPluginAudioProcessor::isOutputChannelStereoPair (int /*index*/) const
  282. {
  283. return true;
  284. }
  285. bool JuceDemoPluginAudioProcessor::acceptsMidi() const
  286. {
  287. #if JucePlugin_WantsMidiInput
  288. return true;
  289. #else
  290. return false;
  291. #endif
  292. }
  293. bool JuceDemoPluginAudioProcessor::producesMidi() const
  294. {
  295. #if JucePlugin_ProducesMidiOutput
  296. return true;
  297. #else
  298. return false;
  299. #endif
  300. }
  301. //==============================================================================
  302. // This creates new instances of the plugin..
  303. AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  304. {
  305. return new JuceDemoPluginAudioProcessor();
  306. }