- clean working copy for leaving SVN

This commit is contained in:
2022-06-28 19:04:46 +02:00
parent 62a2d47cb3
commit 762b8f0e7d
1966 changed files with 699872 additions and 126 deletions
@@ -0,0 +1,177 @@
/*
==============================================================================
This file was auto-generated by the Jucer!
It contains the basic startup code for a Juce application.
==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"
//==============================================================================
JuceDemoPluginAudioProcessorEditor::JuceDemoPluginAudioProcessorEditor (JuceDemoPluginAudioProcessor& owner)
: AudioProcessorEditor (owner),
midiKeyboard (owner.keyboardState, MidiKeyboardComponent::horizontalKeyboard),
infoLabel (String::empty),
gainLabel ("", "Throughput level:"),
delayLabel ("", "Delay:"),
gainSlider ("gain"),
delaySlider ("delay")
{
// add some sliders..
addAndMakeVisible (gainSlider);
gainSlider.setSliderStyle (Slider::Rotary);
gainSlider.addListener (this);
gainSlider.setRange (0.0, 1.0, 0.01);
addAndMakeVisible (delaySlider);
delaySlider.setSliderStyle (Slider::Rotary);
delaySlider.addListener (this);
delaySlider.setRange (0.0, 1.0, 0.01);
// add some labels for the sliders..
gainLabel.attachToComponent (&gainSlider, false);
gainLabel.setFont (Font (11.0f));
delayLabel.attachToComponent (&delaySlider, false);
delayLabel.setFont (Font (11.0f));
// add the midi keyboard component..
addAndMakeVisible (midiKeyboard);
// add a label that will display the current timecode and status..
addAndMakeVisible (infoLabel);
infoLabel.setColour (Label::textColourId, Colours::blue);
// add the triangular resizer component for the bottom-right of the UI
addAndMakeVisible (resizer = new ResizableCornerComponent (this, &resizeLimits));
resizeLimits.setSizeLimits (150, 150, 800, 300);
// set our component's initial size to be the last one that was stored in the filter's settings
setSize (owner.lastUIWidth,
owner.lastUIHeight);
startTimer (50);
}
JuceDemoPluginAudioProcessorEditor::~JuceDemoPluginAudioProcessorEditor()
{
}
//==============================================================================
void JuceDemoPluginAudioProcessorEditor::paint (Graphics& g)
{
g.setGradientFill (ColourGradient (Colours::white, 0, 0,
Colours::grey, 0, (float) getHeight(), false));
g.fillAll();
}
void JuceDemoPluginAudioProcessorEditor::resized()
{
infoLabel.setBounds (10, 4, 400, 25);
gainSlider.setBounds (20, 60, 150, 40);
delaySlider.setBounds (200, 60, 150, 40);
const int keyboardHeight = 70;
midiKeyboard.setBounds (4, getHeight() - keyboardHeight - 4, getWidth() - 8, keyboardHeight);
resizer->setBounds (getWidth() - 16, getHeight() - 16, 16, 16);
getProcessor().lastUIWidth = getWidth();
getProcessor().lastUIHeight = getHeight();
}
//==============================================================================
// This timer periodically checks whether any of the filter's parameters have changed...
void JuceDemoPluginAudioProcessorEditor::timerCallback()
{
JuceDemoPluginAudioProcessor& ourProcessor = getProcessor();
AudioPlayHead::CurrentPositionInfo newPos (ourProcessor.lastPosInfo);
if (lastDisplayedPosition != newPos)
displayPositionInfo (newPos);
gainSlider.setValue (ourProcessor.gain, dontSendNotification);
delaySlider.setValue (ourProcessor.delay, dontSendNotification);
}
// This is our Slider::Listener callback, when the user drags a slider.
void JuceDemoPluginAudioProcessorEditor::sliderValueChanged (Slider* slider)
{
if (slider == &gainSlider)
{
// It's vital to use setParameterNotifyingHost to change any parameters that are automatable
// by the host, rather than just modifying them directly, otherwise the host won't know
// that they've changed.
getProcessor().setParameterNotifyingHost (JuceDemoPluginAudioProcessor::gainParam,
(float) gainSlider.getValue());
}
else if (slider == &delaySlider)
{
getProcessor().setParameterNotifyingHost (JuceDemoPluginAudioProcessor::delayParam,
(float) delaySlider.getValue());
}
}
//==============================================================================
// quick-and-dirty function to format a timecode string
static String timeToTimecodeString (const double seconds)
{
const double absSecs = std::abs (seconds);
const int hours = (int) (absSecs / (60.0 * 60.0));
const int mins = ((int) (absSecs / 60.0)) % 60;
const int secs = ((int) absSecs) % 60;
String s (seconds < 0 ? "-" : "");
s << String (hours).paddedLeft ('0', 2) << ":"
<< String (mins) .paddedLeft ('0', 2) << ":"
<< String (secs) .paddedLeft ('0', 2) << ":"
<< String (roundToInt (absSecs * 1000) % 1000).paddedLeft ('0', 3);
return s;
}
// quick-and-dirty function to format a bars/beats string
static String ppqToBarsBeatsString (double ppq, double /*lastBarPPQ*/, int numerator, int denominator)
{
if (numerator == 0 || denominator == 0)
return "1|1|0";
const int ppqPerBar = (numerator * 4 / denominator);
const double beats = (fmod (ppq, ppqPerBar) / ppqPerBar) * numerator;
const int bar = ((int) ppq) / ppqPerBar + 1;
const int beat = ((int) beats) + 1;
const int ticks = ((int) (fmod (beats, 1.0) * 960.0 + 0.5));
String s;
s << bar << '|' << beat << '|' << ticks;
return s;
}
// Updates the text in our position label.
void JuceDemoPluginAudioProcessorEditor::displayPositionInfo (const AudioPlayHead::CurrentPositionInfo& pos)
{
lastDisplayedPosition = pos;
String displayText;
displayText.preallocateBytes (128);
displayText << String (pos.bpm, 2) << " bpm, "
<< pos.timeSigNumerator << '/' << pos.timeSigDenominator
<< " - " << timeToTimecodeString (pos.timeInSeconds)
<< " - " << ppqToBarsBeatsString (pos.ppqPosition, pos.ppqPositionOfLastBarStart,
pos.timeSigNumerator, pos.timeSigDenominator);
if (pos.isRecording)
displayText << " (recording)";
else if (pos.isPlaying)
displayText << " (playing)";
infoLabel.setText ("[" + SystemStats::getJUCEVersion() + "] " + displayText, dontSendNotification);
}
@@ -0,0 +1,53 @@
/*
==============================================================================
This file was auto-generated by the Jucer!
It contains the basic startup code for a Juce application.
==============================================================================
*/
#ifndef __PLUGINEDITOR_H_4ACCBAA__
#define __PLUGINEDITOR_H_4ACCBAA__
#include "../JuceLibraryCode/JuceHeader.h"
#include "PluginProcessor.h"
//==============================================================================
/** This is the editor component that our filter will display.
*/
class JuceDemoPluginAudioProcessorEditor : public AudioProcessorEditor,
public SliderListener,
public Timer
{
public:
JuceDemoPluginAudioProcessorEditor (JuceDemoPluginAudioProcessor&);
~JuceDemoPluginAudioProcessorEditor();
//==============================================================================
void timerCallback() override;
void paint (Graphics&) override;
void resized() override;
void sliderValueChanged (Slider*) override;
private:
MidiKeyboardComponent midiKeyboard;
Label infoLabel, gainLabel, delayLabel;
Slider gainSlider, delaySlider;
ScopedPointer<ResizableCornerComponent> resizer;
ComponentBoundsConstrainer resizeLimits;
AudioPlayHead::CurrentPositionInfo lastDisplayedPosition;
JuceDemoPluginAudioProcessor& getProcessor() const
{
return static_cast<JuceDemoPluginAudioProcessor&> (processor);
}
void displayPositionInfo (const AudioPlayHead::CurrentPositionInfo& pos);
};
#endif // __PLUGINEDITOR_H_4ACCBAA__
@@ -0,0 +1,405 @@
/*
==============================================================================
This file was auto-generated by the Jucer!
It contains the basic startup code for a Juce application.
==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"
AudioProcessor* JUCE_CALLTYPE createPluginFilter();
//==============================================================================
/** A demo synth sound that's just a basic sine wave.. */
class SineWaveSound : public SynthesiserSound
{
public:
SineWaveSound() {}
bool appliesToNote (int /*midiNoteNumber*/) override { return true; }
bool appliesToChannel (int /*midiChannel*/) override { 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) 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
{
// can't be bothered implementing this for the demo!
}
void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override
{
// not interested in controllers in this case.
}
void renderNextBlock (AudioSampleBuffer& 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)
{
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;
}
}
}
}
private:
double currentAngle, angleDelta, level, tailOff;
};
const float defaultGain = 1.0f;
const float defaultDelay = 0.5f;
//==============================================================================
JuceDemoPluginAudioProcessor::JuceDemoPluginAudioProcessor()
: delayBuffer (2, 12000)
{
// Set up some default values..
gain = defaultGain;
delay = defaultDelay;
lastUIWidth = 400;
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());
}
JuceDemoPluginAudioProcessor::~JuceDemoPluginAudioProcessor()
{
}
//==============================================================================
int JuceDemoPluginAudioProcessor::getNumParameters()
{
return totalNumParams;
}
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!
switch (index)
{
case gainParam: return gain;
case delayParam: return delay;
default: return 0.0f;
}
}
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!
switch (index)
{
case gainParam: gain = newValue; break;
case delayParam: delay = newValue; break;
default: break;
}
}
float JuceDemoPluginAudioProcessor::getParameterDefaultValue (int index)
{
switch (index)
{
case gainParam: return defaultGain;
case delayParam: return defaultDelay;
default: break;
}
return 0.0f;
}
const String JuceDemoPluginAudioProcessor::getParameterName (int index)
{
switch (index)
{
case gainParam: return "gain";
case delayParam: return "delay";
default: break;
}
return String::empty;
}
const String JuceDemoPluginAudioProcessor::getParameterText (int index)
{
return String (getParameter (index), 2);
}
//==============================================================================
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;
// 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.getWritePointer (channel);
float* delayData = delayBuffer.getWritePointer (jmin (channel, delayBuffer.getNumChannels() - 1));
dp = delayPosition;
for (int i = 0; i < numSamples; ++i)
{
const float in = channelData[i];
channelData[i] += delayData[dp];
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() != nullptr && 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);
xml.setAttribute ("gain", gain);
xml.setAttribute ("delay", delay);
// 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 != nullptr)
{
// make sure that it's actually our type of XML object..
if (xmlState->hasTagName ("MYPLUGINSETTINGS"))
{
// ok, now pull out our parameters..
lastUIWidth = xmlState->getIntAttribute ("uiWidth", lastUIWidth);
lastUIHeight = xmlState->getIntAttribute ("uiHeight", lastUIHeight);
gain = (float) xmlState->getDoubleAttribute ("gain", gain);
delay = (float) xmlState->getDoubleAttribute ("delay", delay);
}
}
}
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
}
bool JuceDemoPluginAudioProcessor::silenceInProducesSilenceOut() const
{
return false;
}
double JuceDemoPluginAudioProcessor::getTailLengthSeconds() const
{
return 0.0;
}
//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new JuceDemoPluginAudioProcessor();
}
@@ -0,0 +1,109 @@
/*
==============================================================================
This file was auto-generated by the Jucer!
It contains the basic startup code for a Juce application.
==============================================================================
*/
#ifndef __PLUGINPROCESSOR_H_526ED7A9__
#define __PLUGINPROCESSOR_H_526ED7A9__
#include "../JuceLibraryCode/JuceHeader.h"
//==============================================================================
/**
As the name suggest, this class does the actual audio processing.
*/
class JuceDemoPluginAudioProcessor : public AudioProcessor
{
public:
//==============================================================================
JuceDemoPluginAudioProcessor();
~JuceDemoPluginAudioProcessor();
//==============================================================================
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void releaseResources() override;
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override;
void reset() override;
//==============================================================================
bool hasEditor() const override { return true; }
AudioProcessorEditor* createEditor() override;
//==============================================================================
const String getName() const override { return JucePlugin_Name; }
int getNumParameters() override;
float getParameter (int index) override;
float getParameterDefaultValue (int index) override;
void setParameter (int index, float newValue) override;
const String getParameterName (int index) override;
const String getParameterText (int index) override;
const String getInputChannelName (int channelIndex) const override;
const String getOutputChannelName (int channelIndex) const override;
bool isInputChannelStereoPair (int index) const override;
bool isOutputChannelStereoPair (int index) const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
bool silenceInProducesSilenceOut() const override;
double getTailLengthSeconds() const override;
//==============================================================================
int getNumPrograms() override { return 1; }
int getCurrentProgram() override { return 0; }
void setCurrentProgram (int /*index*/) override {}
const String getProgramName (int /*index*/) override { return "Default"; }
void changeProgramName (int /*index*/, const String& /*newName*/) override {}
//==============================================================================
void getStateInformation (MemoryBlock& destData) override;
void setStateInformation (const void* data, int sizeInBytes) override;
//==============================================================================
// These properties are public so that our editor component can access them
// A bit of a hacky way to do it, but it's only a demo! Obviously in your own
// code you'll do this much more neatly..
// this is kept up to date with the midi messages that arrive, and the UI component
// registers with it so it can represent the incoming messages
MidiKeyboardState keyboardState;
// this keeps a copy of the last set of time info that was acquired during an audio
// callback - the UI component will read this and display it.
AudioPlayHead::CurrentPositionInfo lastPosInfo;
// these are used to persist the UI's size - the values are stored along with the
// filter's other parameters, and the UI component will update them when it gets
// resized.
int lastUIWidth, lastUIHeight;
//==============================================================================
enum Parameters
{
gainParam = 0,
delayParam,
totalNumParams
};
float gain, delay;
private:
//==============================================================================
AudioSampleBuffer delayBuffer;
int delayPosition;
// the synth!
Synthesiser synth;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceDemoPluginAudioProcessor)
};
#endif // __PLUGINPROCESSOR_H_526ED7A9__