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.

364 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 = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++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. ignoreUnused (delayBuffer);
  204. const float gainLevel = *gainParam;
  205. for (int channel = 0; channel < getTotalNumInputChannels(); ++channel)
  206. buffer.applyGain (channel, 0, buffer.getNumSamples(), gainLevel);
  207. }
  208. template <typename FloatType>
  209. void JuceDemoPluginAudioProcessor::applyDelay (AudioBuffer<FloatType>& buffer, AudioBuffer<FloatType>& delayBuffer)
  210. {
  211. const int numSamples = buffer.getNumSamples();
  212. const float delayLevel = *delayParam;
  213. int delayPos = 0;
  214. for (int channel = 0; channel < getTotalNumInputChannels(); ++channel)
  215. {
  216. FloatType* const channelData = buffer.getWritePointer (channel);
  217. FloatType* const delayData = delayBuffer.getWritePointer (jmin (channel, delayBuffer.getNumChannels() - 1));
  218. delayPos = delayPosition;
  219. for (int i = 0; i < numSamples; ++i)
  220. {
  221. const FloatType in = channelData[i];
  222. channelData[i] += delayData[delayPos];
  223. delayData[delayPos] = (delayData[delayPos] + in) * delayLevel;
  224. if (++delayPos >= delayBuffer.getNumSamples())
  225. delayPos = 0;
  226. }
  227. }
  228. delayPosition = delayPos;
  229. }
  230. void JuceDemoPluginAudioProcessor::updateCurrentTimeInfoFromHost()
  231. {
  232. if (AudioPlayHead* ph = getPlayHead())
  233. {
  234. AudioPlayHead::CurrentPositionInfo newTime;
  235. if (ph->getCurrentPosition (newTime))
  236. {
  237. lastPosInfo = newTime; // Successfully got the current time from the host..
  238. return;
  239. }
  240. }
  241. // If the host fails to provide the current time, we'll just reset our copy to a default..
  242. lastPosInfo.resetToDefault();
  243. }
  244. //==============================================================================
  245. AudioProcessorEditor* JuceDemoPluginAudioProcessor::createEditor()
  246. {
  247. return new JuceDemoPluginAudioProcessorEditor (*this);
  248. }
  249. //==============================================================================
  250. void JuceDemoPluginAudioProcessor::getStateInformation (MemoryBlock& destData)
  251. {
  252. // You should use this method to store your parameters in the memory block.
  253. // Here's an example of how you can use XML to make it easy and more robust:
  254. // Create an outer XML element..
  255. XmlElement xml ("MYPLUGINSETTINGS");
  256. // add some attributes to it..
  257. xml.setAttribute ("uiWidth", lastUIWidth);
  258. xml.setAttribute ("uiHeight", lastUIHeight);
  259. // Store the values of all our parameters, using their param ID as the XML attribute
  260. for (int i = 0; i < getNumParameters(); ++i)
  261. if (AudioProcessorParameterWithID* p = dynamic_cast<AudioProcessorParameterWithID*> (getParameters().getUnchecked(i)))
  262. xml.setAttribute (p->paramID, p->getValue());
  263. // then use this helper function to stuff it into the binary blob and return it..
  264. copyXmlToBinary (xml, destData);
  265. }
  266. void JuceDemoPluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
  267. {
  268. // You should use this method to restore your parameters from this memory block,
  269. // whose contents will have been created by the getStateInformation() call.
  270. // This getXmlFromBinary() helper function retrieves our XML from the binary blob..
  271. ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
  272. if (xmlState != nullptr)
  273. {
  274. // make sure that it's actually our type of XML object..
  275. if (xmlState->hasTagName ("MYPLUGINSETTINGS"))
  276. {
  277. // ok, now pull out our last window size..
  278. lastUIWidth = xmlState->getIntAttribute ("uiWidth", lastUIWidth);
  279. lastUIHeight = xmlState->getIntAttribute ("uiHeight", lastUIHeight);
  280. // Now reload our parameters..
  281. for (int i = 0; i < getNumParameters(); ++i)
  282. if (AudioProcessorParameterWithID* p = dynamic_cast<AudioProcessorParameterWithID*> (getParameters().getUnchecked(i)))
  283. p->setValueNotifyingHost ((float) xmlState->getDoubleAttribute (p->paramID, p->getValue()));
  284. }
  285. }
  286. }
  287. //==============================================================================
  288. // This creates new instances of the plugin..
  289. AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  290. {
  291. return new JuceDemoPluginAudioProcessor();
  292. }