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.

391 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. 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. class FloatParameter : public AudioProcessorParameter
  108. {
  109. public:
  110. FloatParameter (float defaultParameterValue, const String& paramName)
  111. : defaultValue (defaultParameterValue),
  112. value (defaultParameterValue),
  113. name (paramName)
  114. {
  115. }
  116. float getValue() const override
  117. {
  118. return value;
  119. }
  120. void setValue (float newValue) override
  121. {
  122. value = newValue;
  123. }
  124. float getDefaultValue() const override
  125. {
  126. return defaultValue;
  127. }
  128. String getName (int maximumStringLength) const override
  129. {
  130. return name;
  131. }
  132. String getLabel() const override
  133. {
  134. return String();
  135. }
  136. float getValueForText (const String& text) const override
  137. {
  138. return text.getFloatValue();
  139. }
  140. private:
  141. float defaultValue, value;
  142. String name;
  143. };
  144. const float defaultGain = 1.0f;
  145. const float defaultDelay = 0.5f;
  146. //==============================================================================
  147. JuceDemoPluginAudioProcessor::JuceDemoPluginAudioProcessor()
  148. : delayBuffer (2, 12000)
  149. {
  150. // Set up our parameters. The base class will delete them for us.
  151. addParameter (gain = new FloatParameter (defaultGain, "Gain"));
  152. addParameter (delay = new FloatParameter (defaultDelay, "Delay"));
  153. lastUIWidth = 400;
  154. lastUIHeight = 200;
  155. lastPosInfo.resetToDefault();
  156. delayPosition = 0;
  157. // Initialise the synth...
  158. for (int i = 4; --i >= 0;)
  159. synth.addVoice (new SineWaveVoice()); // These voices will play our custom sine-wave sounds..
  160. synth.addSound (new SineWaveSound());
  161. }
  162. JuceDemoPluginAudioProcessor::~JuceDemoPluginAudioProcessor()
  163. {
  164. }
  165. //==============================================================================
  166. void JuceDemoPluginAudioProcessor::prepareToPlay (double sampleRate, int /*samplesPerBlock*/)
  167. {
  168. // Use this method as the place to do any pre-playback
  169. // initialisation that you need..
  170. synth.setCurrentPlaybackSampleRate (sampleRate);
  171. keyboardState.reset();
  172. delayBuffer.clear();
  173. }
  174. void JuceDemoPluginAudioProcessor::releaseResources()
  175. {
  176. // When playback stops, you can use this as an opportunity to free up any
  177. // spare memory, etc.
  178. keyboardState.reset();
  179. }
  180. void JuceDemoPluginAudioProcessor::reset()
  181. {
  182. // Use this method as the place to clear any delay lines, buffers, etc, as it
  183. // means there's been a break in the audio's continuity.
  184. delayBuffer.clear();
  185. }
  186. void JuceDemoPluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
  187. {
  188. const int numSamples = buffer.getNumSamples();
  189. int channel, dp = 0;
  190. // Go through the incoming data, and apply our gain to it...
  191. for (channel = 0; channel < getNumInputChannels(); ++channel)
  192. buffer.applyGain (channel, 0, buffer.getNumSamples(), gain->getValue());
  193. // Now pass any incoming midi messages to our keyboard state object, and let it
  194. // add messages to the buffer if the user is clicking on the on-screen keys
  195. keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
  196. // and now get the synth to process these midi events and generate its output.
  197. synth.renderNextBlock (buffer, midiMessages, 0, numSamples);
  198. // Apply our delay effect to the new output..
  199. for (channel = 0; channel < getNumInputChannels(); ++channel)
  200. {
  201. float* channelData = buffer.getWritePointer (channel);
  202. float* delayData = delayBuffer.getWritePointer (jmin (channel, delayBuffer.getNumChannels() - 1));
  203. dp = delayPosition;
  204. for (int i = 0; i < numSamples; ++i)
  205. {
  206. const float in = channelData[i];
  207. channelData[i] += delayData[dp];
  208. delayData[dp] = (delayData[dp] + in) * delay->getValue();
  209. if (++dp >= delayBuffer.getNumSamples())
  210. dp = 0;
  211. }
  212. }
  213. delayPosition = dp;
  214. // In case we have more outputs than inputs, we'll clear any output
  215. // channels that didn't contain input data, (because these aren't
  216. // guaranteed to be empty - they may contain garbage).
  217. for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
  218. buffer.clear (i, 0, buffer.getNumSamples());
  219. // ask the host for the current time so we can display it...
  220. AudioPlayHead::CurrentPositionInfo newTime;
  221. if (getPlayHead() != nullptr && getPlayHead()->getCurrentPosition (newTime))
  222. {
  223. // Successfully got the current time from the host..
  224. lastPosInfo = newTime;
  225. }
  226. else
  227. {
  228. // If the host fails to fill-in the current time, we'll just clear it to a default..
  229. lastPosInfo.resetToDefault();
  230. }
  231. }
  232. //==============================================================================
  233. AudioProcessorEditor* JuceDemoPluginAudioProcessor::createEditor()
  234. {
  235. return new JuceDemoPluginAudioProcessorEditor (*this);
  236. }
  237. //==============================================================================
  238. void JuceDemoPluginAudioProcessor::getStateInformation (MemoryBlock& destData)
  239. {
  240. // You should use this method to store your parameters in the memory block.
  241. // Here's an example of how you can use XML to make it easy and more robust:
  242. // Create an outer XML element..
  243. XmlElement xml ("MYPLUGINSETTINGS");
  244. // add some attributes to it..
  245. xml.setAttribute ("uiWidth", lastUIWidth);
  246. xml.setAttribute ("uiHeight", lastUIHeight);
  247. xml.setAttribute ("gain", gain->getValue());
  248. xml.setAttribute ("delay", delay->getValue());
  249. // then use this helper function to stuff it into the binary blob and return it..
  250. copyXmlToBinary (xml, destData);
  251. }
  252. void JuceDemoPluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
  253. {
  254. // You should use this method to restore your parameters from this memory block,
  255. // whose contents will have been created by the getStateInformation() call.
  256. // This getXmlFromBinary() helper function retrieves our XML from the binary blob..
  257. ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
  258. if (xmlState != nullptr)
  259. {
  260. // make sure that it's actually our type of XML object..
  261. if (xmlState->hasTagName ("MYPLUGINSETTINGS"))
  262. {
  263. // ok, now pull out our parameters..
  264. lastUIWidth = xmlState->getIntAttribute ("uiWidth", lastUIWidth);
  265. lastUIHeight = xmlState->getIntAttribute ("uiHeight", lastUIHeight);
  266. gain->setValue (xmlState->getDoubleAttribute ("gain", gain->getValue()));
  267. delay->setValue (xmlState->getDoubleAttribute ("delay", delay->getValue()));
  268. }
  269. }
  270. }
  271. const String JuceDemoPluginAudioProcessor::getInputChannelName (const int channelIndex) const
  272. {
  273. return String (channelIndex + 1);
  274. }
  275. const String JuceDemoPluginAudioProcessor::getOutputChannelName (const int channelIndex) const
  276. {
  277. return String (channelIndex + 1);
  278. }
  279. bool JuceDemoPluginAudioProcessor::isInputChannelStereoPair (int /*index*/) const
  280. {
  281. return true;
  282. }
  283. bool JuceDemoPluginAudioProcessor::isOutputChannelStereoPair (int /*index*/) const
  284. {
  285. return true;
  286. }
  287. bool JuceDemoPluginAudioProcessor::acceptsMidi() const
  288. {
  289. #if JucePlugin_WantsMidiInput
  290. return true;
  291. #else
  292. return false;
  293. #endif
  294. }
  295. bool JuceDemoPluginAudioProcessor::producesMidi() const
  296. {
  297. #if JucePlugin_ProducesMidiOutput
  298. return true;
  299. #else
  300. return false;
  301. #endif
  302. }
  303. bool JuceDemoPluginAudioProcessor::silenceInProducesSilenceOut() const
  304. {
  305. return false;
  306. }
  307. double JuceDemoPluginAudioProcessor::getTailLengthSeconds() const
  308. {
  309. return 0.0;
  310. }
  311. //==============================================================================
  312. // This creates new instances of the plugin..
  313. AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  314. {
  315. return new JuceDemoPluginAudioProcessor();
  316. }