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.

395 lines
15KB

  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 "../JuceLibraryCode/JuceHeader.h"
  20. #include "InternalPlugins.h"
  21. #include "PluginGraph.h"
  22. //==============================================================================
  23. class InternalPlugin : public AudioPluginInstance
  24. {
  25. protected:
  26. InternalPlugin (const PluginDescription& descr,
  27. const AudioChannelSet& channelSetToUse = AudioChannelSet::stereo())
  28. : AudioPluginInstance (getBusProperties (descr.numInputChannels == 0, channelSetToUse)),
  29. name (descr.fileOrIdentifier.upToFirstOccurrenceOf (":", false, false)),
  30. state (descr.fileOrIdentifier.fromFirstOccurrenceOf (":", false, false)),
  31. isGenerator (descr.numInputChannels == 0),
  32. hasMidi (descr.isInstrument),
  33. channelSet (channelSetToUse)
  34. {
  35. jassert (channelSetToUse.size() == descr.numOutputChannels);
  36. }
  37. public:
  38. //==============================================================================
  39. const String getName() const override { return name; }
  40. double getTailLengthSeconds() const override { return 0.0; }
  41. bool acceptsMidi() const override { return hasMidi; }
  42. bool producesMidi() const override { return hasMidi; }
  43. AudioProcessorEditor* createEditor() override { return nullptr; }
  44. bool hasEditor() const override { return false; }
  45. int getNumPrograms() override { return 0; }
  46. int getCurrentProgram() override { return 0; }
  47. void setCurrentProgram (int) override {}
  48. const String getProgramName (int) override { return {}; }
  49. void changeProgramName (int, const String&) override {}
  50. void getStateInformation (juce::MemoryBlock&) override {}
  51. void setStateInformation (const void*, int) override {}
  52. //==============================================================================
  53. bool isBusesLayoutSupported (const BusesLayout& layout) const override
  54. {
  55. if (! isGenerator)
  56. if (layout.getMainOutputChannelSet() != channelSet)
  57. return false;
  58. if (layout.getMainInputChannelSet() != channelSet)
  59. return false;
  60. return true;
  61. }
  62. //==============================================================================
  63. void fillInPluginDescription (PluginDescription& description) const override
  64. {
  65. description = getPluginDescription (name + ":" + state,
  66. isGenerator,
  67. hasMidi,
  68. channelSet);
  69. }
  70. static PluginDescription getPluginDescription (const String& identifier,
  71. bool registerAsGenerator,
  72. bool acceptsMidi,
  73. const AudioChannelSet& channelSetToUse
  74. = AudioChannelSet::stereo())
  75. {
  76. PluginDescription descr;
  77. auto pluginName = identifier.upToFirstOccurrenceOf (":", false, false);
  78. auto pluginState = identifier.fromFirstOccurrenceOf (":", false, false);
  79. descr.name = pluginName;
  80. descr.descriptiveName = pluginName;
  81. descr.pluginFormatName = "Internal";
  82. descr.category = (registerAsGenerator ? (acceptsMidi ? "Synth" : "Generator") : "Effect");
  83. descr.manufacturerName = "JUCE";
  84. descr.version = ProjectInfo::versionString;
  85. descr.fileOrIdentifier = pluginName + ":" + pluginState;
  86. descr.uid = pluginName.hashCode();
  87. descr.isInstrument = (acceptsMidi && registerAsGenerator);
  88. descr.numInputChannels = (registerAsGenerator ? 0 : channelSetToUse.size());
  89. descr.numOutputChannels = channelSetToUse.size();
  90. return descr;
  91. }
  92. private:
  93. static BusesProperties getBusProperties (bool registerAsGenerator,
  94. const AudioChannelSet& channelSetToUse)
  95. {
  96. return registerAsGenerator ? BusesProperties().withOutput ("Output", channelSetToUse)
  97. : BusesProperties().withInput ("Input", channelSetToUse)
  98. .withOutput ("Output", channelSetToUse);
  99. }
  100. //==============================================================================
  101. String name, state;
  102. bool isGenerator, hasMidi;
  103. AudioChannelSet channelSet;
  104. //==============================================================================
  105. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InternalPlugin)
  106. };
  107. //==============================================================================
  108. class SineWaveSynth : public InternalPlugin
  109. {
  110. public:
  111. SineWaveSynth (const PluginDescription& descr) : InternalPlugin (descr)
  112. {
  113. const int numVoices = 8;
  114. // Add some voices...
  115. for (int i = numVoices; --i >= 0;)
  116. synth.addVoice (new SineWaveVoice());
  117. // ..and give the synth a sound to play
  118. synth.addSound (new SineWaveSound());
  119. }
  120. static String getIdentifier()
  121. {
  122. return "Sine Wave Synth";
  123. }
  124. static PluginDescription getPluginDescription()
  125. {
  126. return InternalPlugin::getPluginDescription (getIdentifier(), true, true);
  127. }
  128. //==============================================================================
  129. void prepareToPlay (double newSampleRate, int) override
  130. {
  131. synth.setCurrentPlaybackSampleRate (newSampleRate);
  132. }
  133. void releaseResources() override {}
  134. //==============================================================================
  135. void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
  136. {
  137. const int numSamples = buffer.getNumSamples();
  138. buffer.clear();
  139. synth.renderNextBlock (buffer, midiMessages, 0, numSamples);
  140. buffer.applyGain (0.8f);
  141. }
  142. using InternalPlugin::processBlock;
  143. private:
  144. //==============================================================================
  145. struct SineWaveSound : public SynthesiserSound
  146. {
  147. SineWaveSound() = default;
  148. bool appliesToNote (int /*midiNoteNumber*/) override { return true; }
  149. bool appliesToChannel (int /*midiChannel*/) override { return true; }
  150. };
  151. struct SineWaveVoice : public SynthesiserVoice
  152. {
  153. SineWaveVoice() = default;
  154. bool canPlaySound (SynthesiserSound* sound) override
  155. {
  156. return dynamic_cast<SineWaveSound*> (sound) != nullptr;
  157. }
  158. void startNote (int midiNoteNumber, float velocity,
  159. SynthesiserSound* /*sound*/,
  160. int /*currentPitchWheelPosition*/) override
  161. {
  162. currentAngle = 0.0;
  163. level = velocity * 0.15;
  164. tailOff = 0.0;
  165. double cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
  166. double cyclesPerSample = cyclesPerSecond / getSampleRate();
  167. angleDelta = cyclesPerSample * 2.0 * double_Pi;
  168. }
  169. void stopNote (float /*velocity*/, bool allowTailOff) override
  170. {
  171. if (allowTailOff)
  172. {
  173. // start a tail-off by setting this flag. The render callback will pick up on
  174. // this and do a fade out, calling clearCurrentNote() when it's finished.
  175. if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the
  176. // stopNote method could be called more than once.
  177. tailOff = 1.0;
  178. }
  179. else
  180. {
  181. // we're being told to stop playing immediately, so reset everything..
  182. clearCurrentNote();
  183. angleDelta = 0.0;
  184. }
  185. }
  186. void pitchWheelMoved (int /*newValue*/) override
  187. {
  188. // not implemented for the purposes of this demo!
  189. }
  190. void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override
  191. {
  192. // not implemented for the purposes of this demo!
  193. }
  194. void renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override
  195. {
  196. if (angleDelta != 0.0)
  197. {
  198. if (tailOff > 0)
  199. {
  200. while (--numSamples >= 0)
  201. {
  202. const float currentSample = (float) (sin (currentAngle) * level * tailOff);
  203. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  204. outputBuffer.addSample (i, startSample, currentSample);
  205. currentAngle += angleDelta;
  206. ++startSample;
  207. tailOff *= 0.99;
  208. if (tailOff <= 0.005)
  209. {
  210. // tells the synth that this voice has stopped
  211. clearCurrentNote();
  212. angleDelta = 0.0;
  213. break;
  214. }
  215. }
  216. }
  217. else
  218. {
  219. while (--numSamples >= 0)
  220. {
  221. const float currentSample = (float) (sin (currentAngle) * level);
  222. for (int i = outputBuffer.getNumChannels(); --i >= 0;)
  223. outputBuffer.addSample (i, startSample, currentSample);
  224. currentAngle += angleDelta;
  225. ++startSample;
  226. }
  227. }
  228. }
  229. }
  230. using SynthesiserVoice::renderNextBlock;
  231. private:
  232. double currentAngle = 0, angleDelta = 0, level = 0, tailOff = 0;
  233. };
  234. //==============================================================================
  235. Synthesiser synth;
  236. //==============================================================================
  237. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SineWaveSynth)
  238. };
  239. //==============================================================================
  240. class ReverbPlugin : public InternalPlugin
  241. {
  242. public:
  243. ReverbPlugin (const PluginDescription& descr) : InternalPlugin (descr)
  244. {}
  245. static String getIdentifier()
  246. {
  247. return "Reverb";
  248. }
  249. static PluginDescription getPluginDescription()
  250. {
  251. return InternalPlugin::getPluginDescription (getIdentifier(), false, false);
  252. }
  253. void prepareToPlay (double newSampleRate, int) override
  254. {
  255. reverb.setSampleRate (newSampleRate);
  256. }
  257. void reset() override
  258. {
  259. reverb.reset();
  260. }
  261. void releaseResources() override {}
  262. void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
  263. {
  264. auto numChannels = buffer.getNumChannels();
  265. if (numChannels == 1)
  266. reverb.processMono (buffer.getWritePointer (0), buffer.getNumSamples());
  267. else
  268. reverb.processStereo (buffer.getWritePointer (0),
  269. buffer.getWritePointer (1),
  270. buffer.getNumSamples());
  271. for (int ch = 2; ch < numChannels; ++ch)
  272. buffer.clear (ch, 0, buffer.getNumSamples());
  273. }
  274. using InternalPlugin::processBlock;
  275. private:
  276. Reverb reverb;
  277. };
  278. //==============================================================================
  279. InternalPluginFormat::InternalPluginFormat()
  280. {
  281. {
  282. AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode);
  283. p.fillInPluginDescription (audioOutDesc);
  284. }
  285. {
  286. AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode);
  287. p.fillInPluginDescription (audioInDesc);
  288. }
  289. {
  290. AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode);
  291. p.fillInPluginDescription (midiInDesc);
  292. }
  293. }
  294. std::unique_ptr<AudioPluginInstance> InternalPluginFormat::createInstance (const String& name)
  295. {
  296. if (name == audioOutDesc.name) return std::make_unique<AudioProcessorGraph::AudioGraphIOProcessor> (AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode);
  297. if (name == audioInDesc.name) return std::make_unique<AudioProcessorGraph::AudioGraphIOProcessor> (AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode);
  298. if (name == midiInDesc.name) return std::make_unique<AudioProcessorGraph::AudioGraphIOProcessor> (AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode);
  299. if (name == SineWaveSynth::getIdentifier()) return std::make_unique<SineWaveSynth> (SineWaveSynth::getPluginDescription());
  300. if (name == ReverbPlugin::getIdentifier()) return std::make_unique<ReverbPlugin> (ReverbPlugin::getPluginDescription());
  301. return {};
  302. }
  303. void InternalPluginFormat::createPluginInstance (const PluginDescription& desc,
  304. double /*initialSampleRate*/, int /*initialBufferSize*/,
  305. PluginCreationCallback callback)
  306. {
  307. if (auto p = createInstance (desc.name))
  308. callback (std::move (p), {});
  309. else
  310. callback (nullptr, NEEDS_TRANS ("Invalid internal plugin name"));
  311. }
  312. bool InternalPluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const
  313. {
  314. return false;
  315. }
  316. void InternalPluginFormat::getAllTypes (Array<PluginDescription>& results)
  317. {
  318. results.add (audioInDesc, audioOutDesc, midiInDesc,
  319. SineWaveSynth::getPluginDescription(),
  320. ReverbPlugin::getPluginDescription());
  321. }