2018-02-27 08:17:12 +08:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
This file was auto - generated !
It contains the basic framework code for a JUCE plugin processor .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# include "PluginProcessor.h"
# include "PluginEditor.h"
2019-06-24 01:12:25 +08:00
# include "MidiConstants.h"
2019-07-02 04:15:33 +08:00
# include "Util.h"
2019-07-08 00:35:31 +08:00
# include "GuiConstants.h"
2019-07-02 06:55:14 +08:00
using namespace std ;
2019-07-03 06:27:56 +08:00
using Parameter = AudioProcessorValueTreeState : : Parameter ;
2018-02-27 08:25:20 +08:00
AudioProcessor * JUCE_CALLTYPE createPluginFilter ( ) ;
2018-02-27 08:17:12 +08:00
//==============================================================================
2018-02-27 08:39:50 +08:00
JuicySFAudioProcessor : : JuicySFAudioProcessor ( )
2019-07-03 06:27:56 +08:00
: AudioProcessor { getBusesProperties ( ) }
2019-07-07 07:22:47 +08:00
, valueTreeState {
* this ,
nullptr ,
2019-07-09 06:36:27 +08:00
" MYPLUGINSETTINGS " ,
2019-07-07 07:22:47 +08:00
createParameterLayout ( ) }
2019-07-11 06:13:58 +08:00
, fluidSynthModel { valueTreeState }
2018-02-27 08:17:12 +08:00
{
2019-07-09 06:36:27 +08:00
valueTreeState . state . appendChild ( { " uiState " , {
{ " width " , GuiConstants : : minWidth } ,
{ " height " , GuiConstants : : minHeight }
} , { } } , nullptr ) ;
2019-07-11 06:13:58 +08:00
valueTreeState . state . appendChild ( { " soundFont " , {
{ " path " , " " } ,
} , { } } , nullptr ) ;
2019-07-13 07:16:35 +08:00
// no properties, no subtrees (yet)
valueTreeState . state . appendChild ( { " banks " , { } , { } } , nullptr ) ;
2019-07-09 06:36:27 +08:00
2018-02-27 08:25:20 +08:00
initialiseSynth ( ) ;
2018-02-27 08:17:12 +08:00
}
2019-07-03 06:27:56 +08:00
AudioProcessorValueTreeState : : ParameterLayout JuicySFAudioProcessor : : createParameterLayout ( ) {
2019-07-04 06:38:56 +08:00
// https://stackoverflow.com/a/8469002/5257399
2019-07-05 06:48:20 +08:00
unique_ptr < AudioParameterInt > params [ ] {
2019-07-15 02:31:05 +08:00
// SoundFont 2.4 spec section 7.2: zero through 127, or 128.
make_unique < AudioParameterInt > ( " bank " , " which bank is selected in the soundfont " , MidiConstants : : midiMinValue , 128 , MidiConstants : : midiMinValue , " Bank " ) ,
2019-07-07 07:22:47 +08:00
// note: banks may be sparse, and lack a 0th preset. so defend against this.
2019-07-08 00:35:31 +08:00
make_unique < AudioParameterInt > ( " preset " , " which patch (aka patch, program, instrument) is selected in the soundfont " , MidiConstants : : midiMinValue , MidiConstants : : midiMaxValue , MidiConstants : : midiMinValue , " Preset " ) ,
make_unique < AudioParameterInt > ( " attack " , " volume envelope attack time " , MidiConstants : : midiMinValue , MidiConstants : : midiMaxValue , MidiConstants : : midiMinValue , " A " ) ,
make_unique < AudioParameterInt > ( " decay " , " volume envelope sustain attentuation " , MidiConstants : : midiMinValue , MidiConstants : : midiMaxValue , MidiConstants : : midiMinValue , " D " ) ,
make_unique < AudioParameterInt > ( " sustain " , " volume envelope decay time " , MidiConstants : : midiMinValue , MidiConstants : : midiMaxValue , MidiConstants : : midiMinValue , " S " ) ,
make_unique < AudioParameterInt > ( " release " , " volume envelope release time " , MidiConstants : : midiMinValue , MidiConstants : : midiMaxValue , MidiConstants : : midiMinValue , " R " ) ,
make_unique < AudioParameterInt > ( " filterCutOff " , " low-pass filter cut-off frequency " , MidiConstants : : midiMinValue , MidiConstants : : midiMaxValue , MidiConstants : : midiMinValue , " Cut " ) ,
make_unique < AudioParameterInt > ( " filterResonance " , " low-pass filter resonance attentuation " , MidiConstants : : midiMinValue , MidiConstants : : midiMaxValue , MidiConstants : : midiMinValue , " Res " ) ,
2019-07-03 06:27:56 +08:00
} ;
2019-07-04 06:38:56 +08:00
return {
make_move_iterator ( begin ( params ) ) ,
make_move_iterator ( end ( params ) )
} ;
2019-07-03 06:27:56 +08:00
}
2018-02-27 08:39:50 +08:00
JuicySFAudioProcessor : : ~ JuicySFAudioProcessor ( )
2018-02-27 08:17:12 +08:00
{
2018-02-27 08:25:20 +08:00
}
2018-02-27 08:39:50 +08:00
void JuicySFAudioProcessor : : initialiseSynth ( ) {
2018-04-10 08:20:23 +08:00
fluidSynthModel . initialise ( ) ;
2018-02-27 08:17:12 +08:00
}
//==============================================================================
2018-02-27 08:39:50 +08:00
const String JuicySFAudioProcessor : : getName ( ) const
2018-02-27 08:17:12 +08:00
{
return JucePlugin_Name ;
}
2018-02-27 08:39:50 +08:00
bool JuicySFAudioProcessor : : acceptsMidi ( ) const
2018-02-27 08:17:12 +08:00
{
# if JucePlugin_WantsMidiInput
return true ;
# else
return false ;
# endif
}
2018-02-27 08:39:50 +08:00
bool JuicySFAudioProcessor : : producesMidi ( ) const
2018-02-27 08:17:12 +08:00
{
# if JucePlugin_ProducesMidiOutput
return true ;
# else
return false ;
# endif
}
2018-02-27 08:39:50 +08:00
double JuicySFAudioProcessor : : getTailLengthSeconds ( ) const
2018-02-27 08:17:12 +08:00
{
return 0.0 ;
}
2018-02-27 08:39:50 +08:00
int JuicySFAudioProcessor : : getNumPrograms ( )
2018-02-27 08:17:12 +08:00
{
2019-07-29 05:51:51 +08:00
return fluidSynthModel . getNumPrograms ( ) ; // NB: some hosts don't cope very well if you tell them there are 0 programs,
2018-02-27 08:17:12 +08:00
// so this should be at least 1, even if you're not really implementing programs.
}
2018-02-27 08:39:50 +08:00
int JuicySFAudioProcessor : : getCurrentProgram ( )
2018-02-27 08:17:12 +08:00
{
2019-07-29 05:51:51 +08:00
return fluidSynthModel . getCurrentProgram ( ) ;
2018-02-27 08:17:12 +08:00
}
2019-07-29 05:51:51 +08:00
void JuicySFAudioProcessor : : setCurrentProgram ( int index )
2018-02-27 08:17:12 +08:00
{
2019-07-29 05:51:51 +08:00
fluidSynthModel . setCurrentProgram ( index ) ;
2018-02-27 08:17:12 +08:00
}
2019-07-29 05:51:51 +08:00
const String JuicySFAudioProcessor : : getProgramName ( int index )
2018-02-27 08:17:12 +08:00
{
2019-07-29 05:51:51 +08:00
return fluidSynthModel . getProgramName ( index ) ;
2018-02-27 08:17:12 +08:00
}
2018-02-27 08:39:50 +08:00
void JuicySFAudioProcessor : : changeProgramName ( int index , const String & newName )
2018-02-27 08:17:12 +08:00
{
}
//==============================================================================
2018-02-27 08:39:50 +08:00
void JuicySFAudioProcessor : : prepareToPlay ( double sampleRate , int /*samplesPerBlock*/ )
2018-02-27 08:17:12 +08:00
{
// Use this method as the place to do any pre-playback
// initialisation that you need..
2018-02-27 08:25:20 +08:00
synth . setCurrentPlaybackSampleRate ( sampleRate ) ;
keyboardState . reset ( ) ;
2018-04-16 04:32:26 +08:00
fluidSynthModel . setSampleRate ( static_cast < float > ( sampleRate ) ) ;
2018-02-27 08:25:20 +08:00
reset ( ) ;
2018-02-27 08:17:12 +08:00
}
2018-02-27 08:39:50 +08:00
void JuicySFAudioProcessor : : releaseResources ( )
2018-02-27 08:17:12 +08:00
{
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
2018-02-27 08:25:20 +08:00
keyboardState . reset ( ) ;
2018-02-27 08:17:12 +08:00
}
2018-02-27 08:39:50 +08:00
bool JuicySFAudioProcessor : : isBusesLayoutSupported ( const BusesLayout & layouts ) const
2018-02-27 08:17:12 +08:00
{
2018-02-27 08:25:20 +08:00
// Only mono/stereo and input/output must have same layout
const AudioChannelSet & mainOutput = layouts . getMainOutputChannelSet ( ) ;
const AudioChannelSet & mainInput = layouts . getMainInputChannelSet ( ) ;
// input and output layout must either be the same or the input must be disabled altogether
if ( ! mainInput . isDisabled ( ) & & mainInput ! = mainOutput )
2018-02-27 08:17:12 +08:00
return false ;
2018-02-27 08:25:20 +08:00
// do not allow disabling the main buses
if ( mainOutput . isDisabled ( ) )
2018-02-27 08:17:12 +08:00
return false ;
2018-02-27 08:25:20 +08:00
// only allow stereo and mono
return mainOutput . size ( ) < = 2 ;
2018-02-27 08:17:12 +08:00
}
2018-02-27 08:39:50 +08:00
AudioProcessor : : BusesProperties JuicySFAudioProcessor : : getBusesProperties ( ) {
2019-06-23 18:13:25 +08:00
return BusesProperties ( )
2018-02-27 08:25:20 +08:00
. withOutput ( " Output " , AudioChannelSet : : stereo ( ) , true ) ;
}
2019-07-29 03:17:03 +08:00
void JuicySFAudioProcessor : : processBlock ( AudioBuffer < float > & buffer , MidiBuffer & midiMessages ) {
2018-02-27 08:25:20 +08:00
jassert ( ! isUsingDoublePrecision ( ) ) ;
// 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
2019-07-29 05:51:51 +08:00
keyboardState . processNextMidiBuffer ( midiMessages , 0 , buffer . getNumSamples ( ) , true ) ;
2019-06-24 01:12:25 +08:00
2019-07-29 05:22:25 +08:00
fluidSynthModel . processBlock ( buffer , midiMessages ) ;
2018-02-27 08:25:20 +08:00
// and now get our synth to process these midi events and generate its output.
2019-07-29 05:22:25 +08:00
// synth.renderNextBlock(buffer, midiMessages, 0, numSamples);
2018-02-27 08:17:12 +08:00
2018-02-28 07:33:19 +08:00
// (see juce_VST3_Wrapper.cpp for the assertion this would trip otherwise)
// we are !JucePlugin_ProducesMidiOutput, so clear remaining MIDI messages from our buffer
midiMessages . clear ( ) ;
2018-02-27 08:17:12 +08:00
// In case we have more outputs than inputs, this code clears any output
// channels that didn't contain input data, (because these aren't
// guaranteed to be empty - they may contain garbage).
// This is here to avoid people getting screaming feedback
// when they first compile a plugin, but obviously you don't need to keep
// this code if your algorithm always overwrites all the output channels.
2018-02-27 08:25:20 +08:00
// for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i)
// buffer.clear (i, 0, numSamples);
2018-02-27 08:17:12 +08:00
}
//==============================================================================
2018-02-27 08:39:50 +08:00
bool JuicySFAudioProcessor : : hasEditor ( ) const
2018-02-27 08:17:12 +08:00
{
return true ; // (change this to false if you choose to not supply an editor)
}
2018-02-27 08:39:50 +08:00
AudioProcessorEditor * JuicySFAudioProcessor : : createEditor ( )
2018-02-27 08:17:12 +08:00
{
2018-04-11 06:29:32 +08:00
// grab a raw pointer to it for our own use
2019-07-04 06:38:56 +08:00
return /*pluginEditor = */ new JuicySFAudioProcessorEditor ( * this , valueTreeState ) ;
2018-02-27 08:17:12 +08:00
}
//==============================================================================
2018-02-27 08:39:50 +08:00
void JuicySFAudioProcessor : : getStateInformation ( MemoryBlock & destData )
2018-02-27 08:17:12 +08:00
{
// You should use this method to store your parameters in the memory block.
// You could do that either as raw data, or use the XML or ValueTree classes
// as intermediaries to make it easy to save and load complex data.
2018-02-27 08:25:20 +08:00
// Create an outer XML element..
2019-07-28 06:04:20 +08:00
XmlElement xml { " MYPLUGINSETTINGS " } ;
2018-02-27 08:25:20 +08:00
// Store the values of all our parameters, using their param ID as the XML attribute
2019-07-28 06:04:20 +08:00
XmlElement * params { xml . createNewChildElement ( " params " ) } ;
for ( auto * param : getParameters ( ) ) {
if ( auto * p = dynamic_cast < AudioProcessorParameterWithID * > ( param ) ) {
params - > setAttribute ( p - > paramID , p - > getValue ( ) ) ;
}
}
{
ValueTree tree { valueTreeState . state . getChildWithName ( " uiState " ) } ;
XmlElement * newElement { xml . createNewChildElement ( " uiState " ) } ;
{
double value { tree . getProperty ( " width " , GuiConstants : : minWidth ) } ;
newElement - > setAttribute ( " width " , value ) ;
}
{
double value { tree . getProperty ( " height " , GuiConstants : : minHeight ) } ;
newElement - > setAttribute ( " height " , value ) ;
}
}
{
ValueTree tree { valueTreeState . state . getChildWithName ( " soundFont " ) } ;
XmlElement * newElement { xml . createNewChildElement ( " soundFont " ) } ;
{
2019-08-01 06:16:31 +08:00
String value = tree . getProperty ( " path " , " " ) ;
2019-07-28 06:04:20 +08:00
newElement - > setAttribute ( " path " , value ) ;
}
}
DEBUG_PRINT ( xml . createDocument ( " " , false , false ) ) ;
2019-07-09 06:36:27 +08:00
2019-07-28 06:04:20 +08:00
copyXmlToBinary ( xml , destData ) ;
2018-02-27 08:17:12 +08:00
}
2018-02-27 08:39:50 +08:00
void JuicySFAudioProcessor : : setStateInformation ( const void * data , int sizeInBytes )
2018-02-27 08:17:12 +08:00
{
// You should use this method to restore your parameters from this memory block,
// whose contents will have been created by the getStateInformation() call.
2018-02-27 08:25:20 +08:00
// This getXmlFromBinary() helper function retrieves our XML from the binary blob..
2019-07-02 06:55:14 +08:00
shared_ptr < XmlElement > xmlState { getXmlFromBinary ( data , sizeInBytes ) } ;
2019-07-11 06:13:58 +08:00
DEBUG_PRINT ( xmlState - > createDocument ( " " , false , false ) ) ;
2019-07-31 04:36:54 +08:00
2019-07-07 07:22:47 +08:00
if ( xmlState . get ( ) ! = nullptr ) {
2018-02-27 08:25:20 +08:00
// make sure that it's actually our type of XML object..
2019-07-07 07:22:47 +08:00
if ( xmlState - > hasTagName ( valueTreeState . state . getType ( ) ) ) {
2019-07-28 06:04:20 +08:00
XmlElement * params { xmlState - > getChildByName ( " params " ) } ;
2019-07-31 04:36:54 +08:00
if ( params )
2019-07-28 06:04:20 +08:00
for ( auto * param : getParameters ( ) )
if ( auto * p = dynamic_cast < AudioProcessorParameterWithID * > ( param ) )
p - > setValue ( static_cast < float > ( params - > getDoubleAttribute ( p - > paramID , p - > getValue ( ) ) ) ) ;
2019-07-09 06:36:27 +08:00
{
2019-07-11 06:13:58 +08:00
XmlElement * xmlElement { xmlState - > getChildByName ( " soundFont " ) } ;
if ( xmlElement ) {
2019-07-28 06:04:20 +08:00
ValueTree tree { valueTreeState . state . getChildWithName ( " soundFont " ) } ;
2019-07-11 06:13:58 +08:00
Value value { tree . getPropertyAsValue ( " path " , nullptr ) } ;
2019-07-29 03:17:03 +08:00
value = xmlElement - > getStringAttribute ( " path " , value . getValue ( ) ) ;
2019-07-11 06:13:58 +08:00
}
2019-07-09 06:36:27 +08:00
}
{
ValueTree tree { valueTreeState . state . getChildWithName ( " uiState " ) } ;
2019-07-11 06:13:58 +08:00
XmlElement * xmlElement { xmlState - > getChildByName ( " uiState " ) } ;
if ( xmlElement ) {
2019-07-09 06:36:27 +08:00
{
Value value { tree . getPropertyAsValue ( " width " , nullptr ) } ;
2019-07-11 06:13:58 +08:00
value = xmlElement - > getIntAttribute ( " width " , value . getValue ( ) ) ;
2019-07-09 06:36:27 +08:00
}
{
Value value { tree . getPropertyAsValue ( " height " , nullptr ) } ;
2019-07-11 06:13:58 +08:00
value = xmlElement - > getIntAttribute ( " height " , value . getValue ( ) ) ;
2019-07-09 06:36:27 +08:00
}
}
}
2018-02-27 08:25:20 +08:00
}
}
}
// FluidSynth only supports float in its process function, so that's all we can support.
2018-02-27 08:39:50 +08:00
bool JuicySFAudioProcessor : : supportsDoublePrecisionProcessing ( ) const {
2018-02-27 08:25:20 +08:00
return false ;
}
2019-07-07 07:22:47 +08:00
FluidSynthModel & JuicySFAudioProcessor : : getFluidSynthModel ( ) {
return fluidSynthModel ;
}
2018-02-27 08:17:12 +08:00
//==============================================================================
// This creates new instances of the plugin..
AudioProcessor * JUCE_CALLTYPE createPluginFilter ( )
{
2018-02-27 08:39:50 +08:00
return new JuicySFAudioProcessor ( ) ;
2018-02-27 08:25:20 +08:00
}