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.

363 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 (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override
  70. {
  71. processBlock (outputBuffer, startSample, numSamples);
  72. }
  73. void renderNextBlock (AudioBuffer<double>& outputBuffer, int startSample, int numSamples) override
  74. {
  75. processBlock (outputBuffer, startSample, numSamples);
  76. }
  77. private:
  78. template <typename FloatType>
  79. void processBlock (AudioBuffer<FloatType>& outputBuffer, int startSample, int numSamples)
  80. {
  81. if (angleDelta != 0.0)
  82. {
  83. if (tailOff > 0)
  84. {
  85. while (--numSamples >= 0)
  86. {
  87. const FloatType currentSample =
  88. static_cast<FloatType> (std::sin (currentAngle) * level * tailOff);
  89. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  90. outputBuffer.addSample (i, startSample, currentSample);
  91. currentAngle += angleDelta;
  92. ++startSample;
  93. tailOff *= 0.99;
  94. if (tailOff <= 0.005)
  95. {
  96. clearCurrentNote();
  97. angleDelta = 0.0;
  98. break;
  99. }
  100. }
  101. }
  102. else
  103. {
  104. while (--numSamples >= 0)
  105. {
  106. const FloatType currentSample = static_cast<FloatType> (std::sin (currentAngle) * level);
  107. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  108. outputBuffer.addSample (i, startSample, currentSample);
  109. currentAngle += angleDelta;
  110. ++startSample;
  111. }
  112. }
  113. }
  114. }
  115. double currentAngle, angleDelta, level, tailOff;
  116. };
  117. //==============================================================================
  118. JuceDemoPluginAudioProcessor::JuceDemoPluginAudioProcessor()
  119. : lastUIWidth (400),
  120. lastUIHeight (200),
  121. gainParam (nullptr),
  122. delayParam (nullptr),
  123. delayPosition (0)
  124. {
  125. lastPosInfo.resetToDefault();
  126. // This creates our parameters. We'll keep some raw pointers to them in this class,
  127. // so that we can easily access them later, but the base class will take care of
  128. // deleting them for us.
  129. addParameter (gainParam = new AudioParameterFloat ("gain", "Gain", 0.0f, 1.0f, 0.9f));
  130. addParameter (delayParam = new AudioParameterFloat ("delay", "Delay Feedback", 0.0f, 1.0f, 0.5f));
  131. initialiseSynth();
  132. }
  133. JuceDemoPluginAudioProcessor::~JuceDemoPluginAudioProcessor()
  134. {
  135. }
  136. void JuceDemoPluginAudioProcessor::initialiseSynth()
  137. {
  138. const int numVoices = 8;
  139. // Add some voices...
  140. for (int i = numVoices; --i >= 0;)
  141. synth.addVoice (new SineWaveVoice());
  142. // ..and give the synth a sound to play
  143. synth.addSound (new SineWaveSound());
  144. }
  145. //==============================================================================
  146. void JuceDemoPluginAudioProcessor::prepareToPlay (double newSampleRate, int /*samplesPerBlock*/)
  147. {
  148. // Use this method as the place to do any pre-playback
  149. // initialisation that you need..
  150. synth.setCurrentPlaybackSampleRate (newSampleRate);
  151. keyboardState.reset();
  152. if (isUsingDoublePrecision())
  153. {
  154. delayBufferDouble.setSize (2, 12000);
  155. delayBufferFloat.setSize (1, 1);
  156. }
  157. else
  158. {
  159. delayBufferFloat.setSize (2, 12000);
  160. delayBufferDouble.setSize (1, 1);
  161. }
  162. reset();
  163. }
  164. void JuceDemoPluginAudioProcessor::releaseResources()
  165. {
  166. // When playback stops, you can use this as an opportunity to free up any
  167. // spare memory, etc.
  168. keyboardState.reset();
  169. }
  170. void JuceDemoPluginAudioProcessor::reset()
  171. {
  172. // Use this method as the place to clear any delay lines, buffers, etc, as it
  173. // means there's been a break in the audio's continuity.
  174. delayBufferFloat.clear();
  175. delayBufferDouble.clear();
  176. }
  177. template <typename FloatType>
  178. void JuceDemoPluginAudioProcessor::process (AudioBuffer<FloatType>& buffer,
  179. MidiBuffer& midiMessages,
  180. AudioBuffer<FloatType>& delayBuffer)
  181. {
  182. const int numSamples = buffer.getNumSamples();
  183. // apply our gain-change to the incoming data..
  184. applyGain (buffer, delayBuffer);
  185. // Now pass any incoming midi messages to our keyboard state object, and let it
  186. // add messages to the buffer if the user is clicking on the on-screen keys
  187. keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
  188. // and now get our synth to process these midi events and generate its output.
  189. synth.renderNextBlock (buffer, midiMessages, 0, numSamples);
  190. // Apply our delay effect to the new output..
  191. applyDelay (buffer, delayBuffer);
  192. // In case we have more outputs than inputs, we'll clear any output
  193. // channels that didn't contain input data, (because these aren't
  194. // guaranteed to be empty - they may contain garbage).
  195. for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
  196. buffer.clear (i, 0, numSamples);
  197. // Now ask the host for the current time so we can store it to be displayed later...
  198. updateCurrentTimeInfoFromHost();
  199. }
  200. template <typename FloatType>
  201. void JuceDemoPluginAudioProcessor::applyGain (AudioBuffer<FloatType>& buffer, AudioBuffer<FloatType>& delayBuffer)
  202. {
  203. const float gainLevel = *gainParam;
  204. for (int channel = 0; channel < getNumInputChannels(); ++channel)
  205. buffer.applyGain (channel, 0, buffer.getNumSamples(), gainLevel);
  206. }
  207. template <typename FloatType>
  208. void JuceDemoPluginAudioProcessor::applyDelay (AudioBuffer<FloatType>& buffer, AudioBuffer<FloatType>& delayBuffer)
  209. {
  210. const int numSamples = buffer.getNumSamples();
  211. const float delayLevel = *delayParam;
  212. int delayPos = 0;
  213. for (int channel = 0; channel < getNumInputChannels(); ++channel)
  214. {
  215. FloatType* const channelData = buffer.getWritePointer (channel);
  216. FloatType* const delayData = delayBuffer.getWritePointer (jmin (channel, delayBuffer.getNumChannels() - 1));
  217. delayPos = delayPosition;
  218. for (int i = 0; i < numSamples; ++i)
  219. {
  220. const float in = channelData[i];
  221. channelData[i] += delayData[delayPos];
  222. delayData[delayPos] = (delayData[delayPos] + in) * delayLevel;
  223. if (++delayPos >= delayBuffer.getNumSamples())
  224. delayPos = 0;
  225. }
  226. }
  227. delayPosition = delayPos;
  228. }
  229. void JuceDemoPluginAudioProcessor::updateCurrentTimeInfoFromHost()
  230. {
  231. if (AudioPlayHead* ph = getPlayHead())
  232. {
  233. AudioPlayHead::CurrentPositionInfo newTime;
  234. if (ph->getCurrentPosition (newTime))
  235. {
  236. lastPosInfo = newTime; // Successfully got the current time from the host..
  237. return;
  238. }
  239. }
  240. // If the host fails to provide the current time, we'll just reset our copy to a default..
  241. lastPosInfo.resetToDefault();
  242. }
  243. //==============================================================================
  244. AudioProcessorEditor* JuceDemoPluginAudioProcessor::createEditor()
  245. {
  246. return new JuceDemoPluginAudioProcessorEditor (*this);
  247. }
  248. //==============================================================================
  249. void JuceDemoPluginAudioProcessor::getStateInformation (MemoryBlock& destData)
  250. {
  251. // You should use this method to store your parameters in the memory block.
  252. // Here's an example of how you can use XML to make it easy and more robust:
  253. // Create an outer XML element..
  254. XmlElement xml ("MYPLUGINSETTINGS");
  255. // add some attributes to it..
  256. xml.setAttribute ("uiWidth", lastUIWidth);
  257. xml.setAttribute ("uiHeight", lastUIHeight);
  258. // Store the values of all our parameters, using their param ID as the XML attribute
  259. for (int i = 0; i < getNumParameters(); ++i)
  260. if (AudioProcessorParameterWithID* p = dynamic_cast<AudioProcessorParameterWithID*> (getParameters().getUnchecked(i)))
  261. xml.setAttribute (p->paramID, p->getValue());
  262. // then use this helper function to stuff it into the binary blob and return it..
  263. copyXmlToBinary (xml, destData);
  264. }
  265. void JuceDemoPluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
  266. {
  267. // You should use this method to restore your parameters from this memory block,
  268. // whose contents will have been created by the getStateInformation() call.
  269. // This getXmlFromBinary() helper function retrieves our XML from the binary blob..
  270. ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
  271. if (xmlState != nullptr)
  272. {
  273. // make sure that it's actually our type of XML object..
  274. if (xmlState->hasTagName ("MYPLUGINSETTINGS"))
  275. {
  276. // ok, now pull out our last window size..
  277. lastUIWidth = xmlState->getIntAttribute ("uiWidth", lastUIWidth);
  278. lastUIHeight = xmlState->getIntAttribute ("uiHeight", lastUIHeight);
  279. // Now reload our parameters..
  280. for (int i = 0; i < getNumParameters(); ++i)
  281. if (AudioProcessorParameterWithID* p = dynamic_cast<AudioProcessorParameterWithID*> (getParameters().getUnchecked(i)))
  282. p->setValueNotifyingHost ((float) xmlState->getDoubleAttribute (p->paramID, p->getValue()));
  283. }
  284. }
  285. }
  286. //==============================================================================
  287. // This creates new instances of the plugin..
  288. AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  289. {
  290. return new JuceDemoPluginAudioProcessor();
  291. }