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.

399 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #include "PluginProcessor.h"
  20. #include "PluginEditor.h"
  21. AudioProcessor* JUCE_CALLTYPE createPluginFilter();
  22. //==============================================================================
  23. /** A demo synth sound that's just a basic sine wave.. */
  24. class SineWaveSound : public SynthesiserSound
  25. {
  26. public:
  27. SineWaveSound() {}
  28. bool appliesToNote (int /*midiNoteNumber*/) override { return true; }
  29. bool appliesToChannel (int /*midiChannel*/) override { return true; }
  30. };
  31. //==============================================================================
  32. /** A simple demo synth voice that just plays a sine wave.. */
  33. class SineWaveVoice : public SynthesiserVoice
  34. {
  35. public:
  36. SineWaveVoice()
  37. {
  38. }
  39. bool canPlaySound (SynthesiserSound* sound) override
  40. {
  41. return dynamic_cast<SineWaveSound*> (sound) != nullptr;
  42. }
  43. void startNote (int midiNoteNumber, float velocity,
  44. SynthesiserSound* /*sound*/,
  45. int /*currentPitchWheelPosition*/) override
  46. {
  47. currentAngle = 0.0;
  48. level = velocity * 0.15;
  49. tailOff = 0.0;
  50. double cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
  51. double cyclesPerSample = cyclesPerSecond / getSampleRate();
  52. angleDelta = cyclesPerSample * 2.0 * double_Pi;
  53. }
  54. void stopNote (float /*velocity*/, bool allowTailOff) override
  55. {
  56. if (allowTailOff)
  57. {
  58. // start a tail-off by setting this flag. The render callback will pick up on
  59. // this and do a fade out, calling clearCurrentNote() when it's finished.
  60. if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the
  61. // stopNote method could be called more than once.
  62. tailOff = 1.0;
  63. }
  64. else
  65. {
  66. // we're being told to stop playing immediately, so reset everything..
  67. clearCurrentNote();
  68. angleDelta = 0.0;
  69. }
  70. }
  71. void pitchWheelMoved (int /*newValue*/) override
  72. {
  73. // can't be bothered implementing this for the demo!
  74. }
  75. void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override
  76. {
  77. // not interested in controllers in this case.
  78. }
  79. void renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override
  80. {
  81. processBlock (outputBuffer, startSample, numSamples);
  82. }
  83. void renderNextBlock (AudioBuffer<double>& outputBuffer, int startSample, int numSamples) override
  84. {
  85. processBlock (outputBuffer, startSample, numSamples);
  86. }
  87. private:
  88. template <typename FloatType>
  89. void processBlock (AudioBuffer<FloatType>& outputBuffer, int startSample, int numSamples)
  90. {
  91. if (angleDelta != 0.0)
  92. {
  93. if (tailOff > 0)
  94. {
  95. while (--numSamples >= 0)
  96. {
  97. auto currentSample = static_cast<FloatType> (std::sin (currentAngle) * level * tailOff);
  98. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  99. outputBuffer.addSample (i, startSample, currentSample);
  100. currentAngle += angleDelta;
  101. ++startSample;
  102. tailOff *= 0.99;
  103. if (tailOff <= 0.005)
  104. {
  105. clearCurrentNote();
  106. angleDelta = 0.0;
  107. break;
  108. }
  109. }
  110. }
  111. else
  112. {
  113. while (--numSamples >= 0)
  114. {
  115. auto currentSample = static_cast<FloatType> (std::sin (currentAngle) * level);
  116. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  117. outputBuffer.addSample (i, startSample, currentSample);
  118. currentAngle += angleDelta;
  119. ++startSample;
  120. }
  121. }
  122. }
  123. }
  124. double currentAngle = 0, angleDelta = 0, level = 0, tailOff = 0;
  125. };
  126. //==============================================================================
  127. JuceDemoPluginAudioProcessor::JuceDemoPluginAudioProcessor()
  128. : AudioProcessor (getBusesProperties())
  129. {
  130. lastPosInfo.resetToDefault();
  131. // This creates our parameters. We'll keep some raw pointers to them in this class,
  132. // so that we can easily access them later, but the base class will take care of
  133. // deleting them for us.
  134. addParameter (gainParam = new AudioParameterFloat ("gain", "Gain", 0.0f, 1.0f, 0.9f));
  135. addParameter (delayParam = new AudioParameterFloat ("delay", "Delay Feedback", 0.0f, 1.0f, 0.5f));
  136. initialiseSynth();
  137. }
  138. JuceDemoPluginAudioProcessor::~JuceDemoPluginAudioProcessor()
  139. {
  140. }
  141. void JuceDemoPluginAudioProcessor::initialiseSynth()
  142. {
  143. const int numVoices = 8;
  144. // Add some voices...
  145. for (int i = numVoices; --i >= 0;)
  146. synth.addVoice (new SineWaveVoice());
  147. // ..and give the synth a sound to play
  148. synth.addSound (new SineWaveSound());
  149. }
  150. //==============================================================================
  151. bool JuceDemoPluginAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
  152. {
  153. // Only mono/stereo and input/output must have same layout
  154. const AudioChannelSet& mainOutput = layouts.getMainOutputChannelSet();
  155. const AudioChannelSet& mainInput = layouts.getMainInputChannelSet();
  156. // input and output layout must either be the same or the input must be disabled altogether
  157. if (! mainInput.isDisabled() && mainInput != mainOutput)
  158. return false;
  159. // do not allow disabling the main buses
  160. if (mainOutput.isDisabled())
  161. return false;
  162. // only allow stereo and mono
  163. if (mainOutput.size() > 2)
  164. return false;
  165. return true;
  166. }
  167. AudioProcessor::BusesProperties JuceDemoPluginAudioProcessor::getBusesProperties()
  168. {
  169. return BusesProperties().withInput ("Input", AudioChannelSet::stereo(), true)
  170. .withOutput ("Output", AudioChannelSet::stereo(), true);
  171. }
  172. //==============================================================================
  173. void JuceDemoPluginAudioProcessor::prepareToPlay (double newSampleRate, int /*samplesPerBlock*/)
  174. {
  175. // Use this method as the place to do any pre-playback
  176. // initialisation that you need..
  177. synth.setCurrentPlaybackSampleRate (newSampleRate);
  178. keyboardState.reset();
  179. if (isUsingDoublePrecision())
  180. {
  181. delayBufferDouble.setSize (2, 12000);
  182. delayBufferFloat.setSize (1, 1);
  183. }
  184. else
  185. {
  186. delayBufferFloat.setSize (2, 12000);
  187. delayBufferDouble.setSize (1, 1);
  188. }
  189. reset();
  190. }
  191. void JuceDemoPluginAudioProcessor::releaseResources()
  192. {
  193. // When playback stops, you can use this as an opportunity to free up any
  194. // spare memory, etc.
  195. keyboardState.reset();
  196. }
  197. void JuceDemoPluginAudioProcessor::reset()
  198. {
  199. // Use this method as the place to clear any delay lines, buffers, etc, as it
  200. // means there's been a break in the audio's continuity.
  201. delayBufferFloat.clear();
  202. delayBufferDouble.clear();
  203. }
  204. template <typename FloatType>
  205. void JuceDemoPluginAudioProcessor::process (AudioBuffer<FloatType>& buffer,
  206. MidiBuffer& midiMessages,
  207. AudioBuffer<FloatType>& delayBuffer)
  208. {
  209. const int numSamples = buffer.getNumSamples();
  210. // Now pass any incoming midi messages to our keyboard state object, and let it
  211. // add messages to the buffer if the user is clicking on the on-screen keys
  212. keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
  213. // and now get our synth to process these midi events and generate its output.
  214. synth.renderNextBlock (buffer, midiMessages, 0, numSamples);
  215. // Apply our delay effect to the new output..
  216. applyDelay (buffer, delayBuffer);
  217. // In case we have more outputs than inputs, we'll clear any output
  218. // channels that didn't contain input data, (because these aren't
  219. // guaranteed to be empty - they may contain garbage).
  220. for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i)
  221. buffer.clear (i, 0, numSamples);
  222. applyGain (buffer, delayBuffer); // apply our gain-change to the outgoing data..
  223. // Now ask the host for the current time so we can store it to be displayed later...
  224. updateCurrentTimeInfoFromHost();
  225. }
  226. template <typename FloatType>
  227. void JuceDemoPluginAudioProcessor::applyGain (AudioBuffer<FloatType>& buffer, AudioBuffer<FloatType>& delayBuffer)
  228. {
  229. ignoreUnused (delayBuffer);
  230. const float gainLevel = *gainParam;
  231. for (int channel = 0; channel < getTotalNumOutputChannels(); ++channel)
  232. buffer.applyGain (channel, 0, buffer.getNumSamples(), gainLevel);
  233. }
  234. template <typename FloatType>
  235. void JuceDemoPluginAudioProcessor::applyDelay (AudioBuffer<FloatType>& buffer, AudioBuffer<FloatType>& delayBuffer)
  236. {
  237. const int numSamples = buffer.getNumSamples();
  238. const float delayLevel = *delayParam;
  239. int delayPos = 0;
  240. for (int channel = 0; channel < getTotalNumOutputChannels(); ++channel)
  241. {
  242. auto channelData = buffer.getWritePointer (channel);
  243. auto delayData = delayBuffer.getWritePointer (jmin (channel, delayBuffer.getNumChannels() - 1));
  244. delayPos = delayPosition;
  245. for (int i = 0; i < numSamples; ++i)
  246. {
  247. auto in = channelData[i];
  248. channelData[i] += delayData[delayPos];
  249. delayData[delayPos] = (delayData[delayPos] + in) * delayLevel;
  250. if (++delayPos >= delayBuffer.getNumSamples())
  251. delayPos = 0;
  252. }
  253. }
  254. delayPosition = delayPos;
  255. }
  256. void JuceDemoPluginAudioProcessor::updateCurrentTimeInfoFromHost()
  257. {
  258. if (AudioPlayHead* ph = getPlayHead())
  259. {
  260. AudioPlayHead::CurrentPositionInfo newTime;
  261. if (ph->getCurrentPosition (newTime))
  262. {
  263. lastPosInfo = newTime; // Successfully got the current time from the host..
  264. return;
  265. }
  266. }
  267. // If the host fails to provide the current time, we'll just reset our copy to a default..
  268. lastPosInfo.resetToDefault();
  269. }
  270. //==============================================================================
  271. AudioProcessorEditor* JuceDemoPluginAudioProcessor::createEditor()
  272. {
  273. return new JuceDemoPluginAudioProcessorEditor (*this);
  274. }
  275. //==============================================================================
  276. void JuceDemoPluginAudioProcessor::getStateInformation (MemoryBlock& destData)
  277. {
  278. // You should use this method to store your parameters in the memory block.
  279. // Here's an example of how you can use XML to make it easy and more robust:
  280. // Create an outer XML element..
  281. XmlElement xml ("MYPLUGINSETTINGS");
  282. // add some attributes to it..
  283. xml.setAttribute ("uiWidth", lastUIWidth);
  284. xml.setAttribute ("uiHeight", lastUIHeight);
  285. // Store the values of all our parameters, using their param ID as the XML attribute
  286. for (auto* param : getParameters())
  287. if (auto* p = dynamic_cast<AudioProcessorParameterWithID*> (param))
  288. xml.setAttribute (p->paramID, p->getValue());
  289. // then use this helper function to stuff it into the binary blob and return it..
  290. copyXmlToBinary (xml, destData);
  291. }
  292. void JuceDemoPluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
  293. {
  294. // You should use this method to restore your parameters from this memory block,
  295. // whose contents will have been created by the getStateInformation() call.
  296. // This getXmlFromBinary() helper function retrieves our XML from the binary blob..
  297. ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
  298. if (xmlState != nullptr)
  299. {
  300. // make sure that it's actually our type of XML object..
  301. if (xmlState->hasTagName ("MYPLUGINSETTINGS"))
  302. {
  303. // ok, now pull out our last window size..
  304. lastUIWidth = jmax (xmlState->getIntAttribute ("uiWidth", lastUIWidth), 400);
  305. lastUIHeight = jmax (xmlState->getIntAttribute ("uiHeight", lastUIHeight), 200);
  306. // Now reload our parameters..
  307. for (auto* param : getParameters())
  308. if (auto* p = dynamic_cast<AudioProcessorParameterWithID*> (param))
  309. p->setValue ((float) xmlState->getDoubleAttribute (p->paramID, p->getValue()));
  310. }
  311. }
  312. }
  313. //==============================================================================
  314. // This creates new instances of the plugin..
  315. AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  316. {
  317. return new JuceDemoPluginAudioProcessor();
  318. }