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.

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