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.

406 lines
13KB

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