1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-26 02:14:22 +00:00
JUCE/extras/audio plugins/demo/Source/PluginProcessor.cpp

416 lines
13 KiB
C++

/*
==============================================================================
This file was auto-generated by the Jucer!
It contains the basic startup code for a Juce application.
==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"
//==============================================================================
/** A demo synth sound that's just a basic sine wave.. */
class SineWaveSound : public SynthesiserSound
{
public:
SineWaveSound()
{
}
bool appliesToNote (const int /*midiNoteNumber*/) { return true; }
bool appliesToChannel (const int /*midiChannel*/) { return true; }
};
//==============================================================================
/** A simple demo synth voice that just plays a sine wave.. */
class SineWaveVoice : public SynthesiserVoice
{
public:
SineWaveVoice()
: angleDelta (0.0),
tailOff (0.0)
{
}
bool canPlaySound (SynthesiserSound* sound)
{
return dynamic_cast <SineWaveSound*> (sound) != 0;
}
void startNote (const int midiNoteNumber, const float velocity,
SynthesiserSound* /*sound*/, const int /*currentPitchWheelPosition*/)
{
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 (const bool allowTailOff)
{
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 (const int /*newValue*/)
{
// can't be bothered implementing this for the demo!
}
void controllerMoved (const int /*controllerNumber*/, const int /*newValue*/)
{
// not interested in controllers in this case.
}
void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples)
{
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.getSampleData (i, startSample) += currentSample;
currentAngle += angleDelta;
++startSample;
tailOff *= 0.99;
if (tailOff <= 0.005)
{
clearCurrentNote();
angleDelta = 0.0;
break;
}
}
}
else
{
while (--numSamples >= 0)
{
const float currentSample = (float) (sin (currentAngle) * level);
for (int i = outputBuffer.getNumChannels(); --i >= 0;)
*outputBuffer.getSampleData (i, startSample) += currentSample;
currentAngle += angleDelta;
++startSample;
}
}
}
}
private:
double currentAngle, angleDelta, level, tailOff;
};
//==============================================================================
JuceDemoPluginAudioProcessor::JuceDemoPluginAudioProcessor()
: delayBuffer (2, 12000)
{
// Set up parameters and default values..
AudioProcessorParameter* gainParam = new AudioProcessorParameter ("gain");
gainParam->resetWithDefault (1.0);
parameters.add (gainParam);
AudioProcessorParameter* delayParam = new AudioProcessorParameter ("delay");
delayParam->resetWithDefault (0.5);
parameters.add (delayParam);
AudioProcessorParameter* cutOffParam = new AudioProcessorParameter ("cutoff", AudioProcessorParameter::hertz);
cutOffParam->setRange (Range<double> (20.0, 20000.0));
cutOffParam->resetWithDefault (10000.0);
parameters.add (cutOffParam);
lastUIWidth = 600;
lastUIHeight = 200;
lastPosInfo.resetToDefault();
delayPosition = 0;
// Initialise the synth...
for (int i = 4; --i >= 0;)
synth.addVoice (new SineWaveVoice()); // These voices will play our custom sine-wave sounds..
synth.addSound (new SineWaveSound());
zeromem (lastSample, sizeof (lastSample));
}
JuceDemoPluginAudioProcessor::~JuceDemoPluginAudioProcessor()
{
}
//==============================================================================
int JuceDemoPluginAudioProcessor::getNumParameters()
{
return parameters.size();
}
float JuceDemoPluginAudioProcessor::getParameter (int index)
{
// This method will be called by the host, probably on the audio thread, so
// it's absolutely time-critical. Don't use critical sections or anything
// UI-related, or anything at all that may block in any way!
jassert (parameters [index] != 0);
parameters.getUnchecked (index)->getValue();
}
void JuceDemoPluginAudioProcessor::setParameter (int index, float newValue)
{
// This method will be called by the host, probably on the audio thread, so
// it's absolutely time-critical. Don't use critical sections or anything
// UI-related, or anything at all that may block in any way!
jassert (parameters [index] != 0);
parameters.getUnchecked (index)->setValue (newValue);
}
const String JuceDemoPluginAudioProcessor::getParameterName (int index)
{
jassert (parameters [index] != 0);
return parameters.getUnchecked (index)->getName();
}
const String JuceDemoPluginAudioProcessor::getParameterText (int index)
{
return String (getParameter (index), 2);
}
int JuceDemoPluginAudioProcessor::indexOfParameter (const String& parameterName) const
{
for (int i = parameters.size(); --i >= 0;)
if (parameters.getUnchecked(i)->getName() == name)
return i;
return -1;
}
AudioProcessorParameter* JuceDemoPluginAudioProcessor::getParameterObject (int index) const
{
return parameters [index];
}
AudioProcessorParameter* JuceDemoPluginAudioProcessor::getParameterWithName (const String& parameterName) const
{
return parameters [indexOfParameter (parameterName)];
}
//==============================================================================
void JuceDemoPluginAudioProcessor::prepareToPlay (double sampleRate, int /*samplesPerBlock*/)
{
// Use this method as the place to do any pre-playback
// initialisation that you need..
synth.setCurrentPlaybackSampleRate (sampleRate);
keyboardState.reset();
delayBuffer.clear();
}
void JuceDemoPluginAudioProcessor::releaseResources()
{
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
keyboardState.reset();
}
void JuceDemoPluginAudioProcessor::reset()
{
// Use this method as the place to clear any delay lines, buffers, etc, as it
// means there's been a break in the audio's continuity.
delayBuffer.clear();
}
void JuceDemoPluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
const int numSamples = buffer.getNumSamples();
int channel, dp = 0;
float gain = getParameter (0);
float delay = getParameter (1);
float cutOff = getParameter (2);
// Get one-pole filter coefficient
const float filterCoeff = (float_Pi * cutOff / getSampleRate());
// Go through the incoming data, and apply our gain to it...
for (channel = 0; channel < getNumInputChannels(); ++channel)
buffer.applyGain (channel, 0, buffer.getNumSamples(), gain);
// Now pass any incoming midi messages to our keyboard state object, and let it
// add messages to the buffer if the user is clicking on the on-screen keys
keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
// and now get the synth to process these midi events and generate its output.
synth.renderNextBlock (buffer, midiMessages, 0, numSamples);
// Apply our delay effect to the new output..
for (channel = 0; channel < getNumInputChannels(); ++channel)
{
float* channelData = buffer.getSampleData (channel);
float* delayData = delayBuffer.getSampleData (jmin (channel, delayBuffer.getNumChannels() - 1));
dp = delayPosition;
for (int i = 0; i < numSamples; ++i)
{
const float in = channelData[i];
// filter delay data
lastSample [channel] += filterCoeff * (delayData[dp] - lastSample [channel]);
// add to output buffers
channelData[i] += lastSample [channel];
delayData[dp] = (delayData[dp] + in) * delay;
if (++dp > delayBuffer.getNumSamples())
dp = 0;
}
}
delayPosition = dp;
// In case we have more outputs than inputs, we'll clear any output
// channels that didn't contain input data, (because these aren't
// guaranteed to be empty - they may contain garbage).
for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
buffer.clear (i, 0, buffer.getNumSamples());
// ask the host for the current time so we can display it...
AudioPlayHead::CurrentPositionInfo newTime;
if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (newTime))
{
// Successfully got the current time from the host..
lastPosInfo = newTime;
}
else
{
// If the host fails to fill-in the current time, we'll just clear it to a default..
lastPosInfo.resetToDefault();
}
}
//==============================================================================
AudioProcessorEditor* JuceDemoPluginAudioProcessor::createEditor()
{
return new JuceDemoPluginAudioProcessorEditor (this);
}
//==============================================================================
void JuceDemoPluginAudioProcessor::getStateInformation (MemoryBlock& destData)
{
// You should use this method to store your parameters in the memory block.
// Here's an example of how you can use XML to make it easy and more robust:
// Create an outer XML element..
XmlElement xml ("MYPLUGINSETTINGS");
// add some attributes to it..
xml.setAttribute ("uiWidth", lastUIWidth);
xml.setAttribute ("uiHeight", lastUIHeight);
for (int i = 0; i < parameters.size(); ++i)
{
const AudioProcessorParameter* const p = parameters.getUnchecked(i);
xml.setAttribute (p->getName(), p->getValue());
}
// then use this helper function to stuff it into the binary blob and return it..
copyXmlToBinary (xml, destData);
}
void JuceDemoPluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
// You should use this method to restore your parameters from this memory block,
// whose contents will have been created by the getStateInformation() call.
// This getXmlFromBinary() helper function retrieves our XML from the binary blob..
ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
if (xmlState != 0)
{
// make sure that it's actually our type of XML object..
if (xmlState->hasTagName ("MYPLUGINSETTINGS"))
{
// ok, now pull out our plugin-specific attributes..
lastUIWidth = xmlState->getIntAttribute ("uiWidth", lastUIWidth);
lastUIHeight = xmlState->getIntAttribute ("uiHeight", lastUIHeight);
// and all the parameters.
for (int i = 0; i < parameters.size(); ++i)
{
const AudioProcessorParameter* const p = parameters.getUnchecked(i);
p->setValue (xmlState->getDoubleAttribute (p->getName(), p->getDefault()));
}
}
}
}
const String JuceDemoPluginAudioProcessor::getInputChannelName (const int channelIndex) const
{
return String (channelIndex + 1);
}
const String JuceDemoPluginAudioProcessor::getOutputChannelName (const int channelIndex) const
{
return String (channelIndex + 1);
}
bool JuceDemoPluginAudioProcessor::isInputChannelStereoPair (int /*index*/) const
{
return true;
}
bool JuceDemoPluginAudioProcessor::isOutputChannelStereoPair (int /*index*/) const
{
return true;
}
bool JuceDemoPluginAudioProcessor::acceptsMidi() const
{
#if JucePlugin_WantsMidiInput
return true;
#else
return false;
#endif
}
bool JuceDemoPluginAudioProcessor::producesMidi() const
{
#if JucePlugin_ProducesMidiOutput
return true;
#else
return false;
#endif
}
//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new JuceDemoPluginAudioProcessor();
}