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.

392 lines
15KB

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