1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/extras/AudioPluginHost/Source/Plugins/InternalPlugins.cpp

399 lines
15 KiB
C++

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include <JuceHeader.h>
#include "InternalPlugins.h"
#include "PluginGraph.h"
//==============================================================================
class InternalPlugin : public AudioPluginInstance
{
protected:
InternalPlugin (const PluginDescription& descr,
const AudioChannelSet& channelSetToUse = AudioChannelSet::stereo())
: AudioPluginInstance (getBusProperties (descr.numInputChannels == 0, channelSetToUse)),
name (descr.fileOrIdentifier.upToFirstOccurrenceOf (":", false, false)),
state (descr.fileOrIdentifier.fromFirstOccurrenceOf (":", false, false)),
isGenerator (descr.numInputChannels == 0),
hasMidi (descr.isInstrument),
channelSet (channelSetToUse)
{
jassert (channelSetToUse.size() == descr.numOutputChannels);
}
public:
//==============================================================================
const String getName() const override { return name; }
double getTailLengthSeconds() const override { return 0.0; }
bool acceptsMidi() const override { return hasMidi; }
bool producesMidi() const override { return hasMidi; }
AudioProcessorEditor* createEditor() override { return nullptr; }
bool hasEditor() const override { return false; }
int getNumPrograms() override { return 0; }
int getCurrentProgram() override { return 0; }
void setCurrentProgram (int) override {}
const String getProgramName (int) override { return {}; }
void changeProgramName (int, const String&) override {}
void getStateInformation (juce::MemoryBlock&) override {}
void setStateInformation (const void*, int) override {}
//==============================================================================
bool isBusesLayoutSupported (const BusesLayout& layout) const override
{
if (! isGenerator)
if (layout.getMainOutputChannelSet() != channelSet)
return false;
if (layout.getMainInputChannelSet() != channelSet)
return false;
return true;
}
//==============================================================================
void fillInPluginDescription (PluginDescription& description) const override
{
description = getPluginDescription (name + ":" + state,
isGenerator,
hasMidi,
channelSet);
}
static PluginDescription getPluginDescription (const String& identifier,
bool registerAsGenerator,
bool acceptsMidi,
const AudioChannelSet& channelSetToUse
= AudioChannelSet::stereo())
{
PluginDescription descr;
auto pluginName = identifier.upToFirstOccurrenceOf (":", false, false);
auto pluginState = identifier.fromFirstOccurrenceOf (":", false, false);
descr.name = pluginName;
descr.descriptiveName = pluginName;
descr.pluginFormatName = "Internal";
descr.category = (registerAsGenerator ? (acceptsMidi ? "Synth" : "Generator") : "Effect");
descr.manufacturerName = "JUCE";
descr.version = ProjectInfo::versionString;
descr.fileOrIdentifier = pluginName + ":" + pluginState;
descr.uid = pluginName.hashCode();
descr.isInstrument = (acceptsMidi && registerAsGenerator);
descr.numInputChannels = (registerAsGenerator ? 0 : channelSetToUse.size());
descr.numOutputChannels = channelSetToUse.size();
return descr;
}
private:
static BusesProperties getBusProperties (bool registerAsGenerator,
const AudioChannelSet& channelSetToUse)
{
return registerAsGenerator ? BusesProperties().withOutput ("Output", channelSetToUse)
: BusesProperties().withInput ("Input", channelSetToUse)
.withOutput ("Output", channelSetToUse);
}
//==============================================================================
String name, state;
bool isGenerator, hasMidi;
AudioChannelSet channelSet;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InternalPlugin)
};
//==============================================================================
class SineWaveSynth : public InternalPlugin
{
public:
SineWaveSynth (const PluginDescription& descr) : InternalPlugin (descr)
{
const int numVoices = 8;
// Add some voices...
for (int i = numVoices; --i >= 0;)
synth.addVoice (new SineWaveVoice());
// ..and give the synth a sound to play
synth.addSound (new SineWaveSound());
}
static String getIdentifier()
{
return "Sine Wave Synth";
}
static PluginDescription getPluginDescription()
{
return InternalPlugin::getPluginDescription (getIdentifier(), true, true);
}
//==============================================================================
void prepareToPlay (double newSampleRate, int) override
{
synth.setCurrentPlaybackSampleRate (newSampleRate);
}
void releaseResources() override {}
//==============================================================================
void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
{
const int numSamples = buffer.getNumSamples();
buffer.clear();
synth.renderNextBlock (buffer, midiMessages, 0, numSamples);
buffer.applyGain (0.8f);
}
using InternalPlugin::processBlock;
private:
//==============================================================================
struct SineWaveSound : public SynthesiserSound
{
SineWaveSound() = default;
bool appliesToNote (int /*midiNoteNumber*/) override { return true; }
bool appliesToChannel (int /*midiChannel*/) override { return true; }
};
struct SineWaveVoice : public SynthesiserVoice
{
SineWaveVoice() = default;
bool canPlaySound (SynthesiserSound* sound) override
{
return dynamic_cast<SineWaveSound*> (sound) != nullptr;
}
void startNote (int midiNoteNumber, float velocity,
SynthesiserSound* /*sound*/,
int /*currentPitchWheelPosition*/) override
{
currentAngle = 0.0;
level = velocity * 0.15;
tailOff = 0.0;
double cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
double cyclesPerSample = cyclesPerSecond / getSampleRate();
angleDelta = cyclesPerSample * 2.0 * double_Pi;
}
void stopNote (float /*velocity*/, bool allowTailOff) override
{
if (allowTailOff)
{
// start a tail-off by setting this flag. The render callback will pick up on
// this and do a fade out, calling clearCurrentNote() when it's finished.
if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the
// stopNote method could be called more than once.
tailOff = 1.0;
}
else
{
// we're being told to stop playing immediately, so reset everything..
clearCurrentNote();
angleDelta = 0.0;
}
}
void pitchWheelMoved (int /*newValue*/) override
{
// not implemented for the purposes of this demo!
}
void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override
{
// not implemented for the purposes of this demo!
}
void renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override
{
if (angleDelta != 0.0)
{
if (tailOff > 0)
{
while (--numSamples >= 0)
{
const float currentSample = (float) (sin (currentAngle) * level * tailOff);
for (int i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, currentSample);
currentAngle += angleDelta;
++startSample;
tailOff *= 0.99;
if (tailOff <= 0.005)
{
// tells the synth that this voice has stopped
clearCurrentNote();
angleDelta = 0.0;
break;
}
}
}
else
{
while (--numSamples >= 0)
{
const float currentSample = (float) (sin (currentAngle) * level);
for (int i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, currentSample);
currentAngle += angleDelta;
++startSample;
}
}
}
}
using SynthesiserVoice::renderNextBlock;
private:
double currentAngle = 0, angleDelta = 0, level = 0, tailOff = 0;
};
//==============================================================================
Synthesiser synth;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SineWaveSynth)
};
//==============================================================================
class ReverbPlugin : public InternalPlugin
{
public:
ReverbPlugin (const PluginDescription& descr) : InternalPlugin (descr)
{}
static String getIdentifier()
{
return "Reverb";
}
static PluginDescription getPluginDescription()
{
return InternalPlugin::getPluginDescription (getIdentifier(), false, false);
}
void prepareToPlay (double newSampleRate, int) override
{
reverb.setSampleRate (newSampleRate);
}
void reset() override
{
reverb.reset();
}
void releaseResources() override {}
void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override
{
auto numChannels = buffer.getNumChannels();
if (numChannels == 1)
reverb.processMono (buffer.getWritePointer (0), buffer.getNumSamples());
else
reverb.processStereo (buffer.getWritePointer (0),
buffer.getWritePointer (1),
buffer.getNumSamples());
for (int ch = 2; ch < numChannels; ++ch)
buffer.clear (ch, 0, buffer.getNumSamples());
}
using InternalPlugin::processBlock;
private:
Reverb reverb;
};
//==============================================================================
InternalPluginFormat::InternalPluginFormat()
{
{
AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode);
p.fillInPluginDescription (audioOutDesc);
}
{
AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode);
p.fillInPluginDescription (audioInDesc);
}
{
AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode);
p.fillInPluginDescription (midiInDesc);
}
{
AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::midiOutputNode);
p.fillInPluginDescription (midiOutDesc);
}
}
std::unique_ptr<AudioPluginInstance> InternalPluginFormat::createInstance (const String& name)
{
if (name == audioOutDesc.name) return std::make_unique<AudioProcessorGraph::AudioGraphIOProcessor> (AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode);
if (name == audioInDesc.name) return std::make_unique<AudioProcessorGraph::AudioGraphIOProcessor> (AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode);
if (name == midiInDesc.name) return std::make_unique<AudioProcessorGraph::AudioGraphIOProcessor> (AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode);
if (name == midiOutDesc.name) return std::make_unique<AudioProcessorGraph::AudioGraphIOProcessor> (AudioProcessorGraph::AudioGraphIOProcessor::midiOutputNode);
if (name == SineWaveSynth::getIdentifier()) return std::make_unique<SineWaveSynth> (SineWaveSynth::getPluginDescription());
if (name == ReverbPlugin::getIdentifier()) return std::make_unique<ReverbPlugin> (ReverbPlugin::getPluginDescription());
return {};
}
void InternalPluginFormat::createPluginInstance (const PluginDescription& desc,
double /*initialSampleRate*/, int /*initialBufferSize*/,
PluginCreationCallback callback)
{
if (auto p = createInstance (desc.name))
callback (std::move (p), {});
else
callback (nullptr, NEEDS_TRANS ("Invalid internal plugin name"));
}
bool InternalPluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const
{
return false;
}
void InternalPluginFormat::getAllTypes (Array<PluginDescription>& results)
{
results.add (audioInDesc, audioOutDesc, midiInDesc, midiOutDesc,
SineWaveSynth::getPluginDescription(), ReverbPlugin::getPluginDescription());
}