No. In fact the default constructor is now exactly the same as it was before we released the multibus stuff (pre 4.1). Before multibus, every AudioProcessor had one main in and one main out bus, but the default number of channels was zero on both buses. Either the DAW would change the number of channels (if your AudioProcessor was wrapped in a plug-in) - or - if you are using the AudioProcessor yourself - you would need to call setPlayConfigDetails. This hasn’t changed with the new multibus API if you are using the default constructor. This way old JUCE projects will still work.
For easy migrating look at the AudioProcessor::containsLayout
convenience function. With this you can use the old channel configuration maps. Just make sure to remove it from the channel configuration field in the Projucer. I’ve put the most relevant functions - the constructor and isBusesLayoutSupported at the top.
#define LegacyChannelConfiguration {{2, 2}, {1, 1}, {1, 2}}
class LegacyPlugin : public AudioProcessor
{
public:
LegacyPlugin()
: AudioProcessor (LegacyChannelConfiguration),
state (*this, nullptr)
{
state.createAndAddParameter ("gain", "Volume", "Volume",
NormalisableRange<float> (0.0f, 1.0f),
0.5f,
[] (float value) { return String (value); },
[] (const String& s) { return s.getFloatValue(); });
}
bool isBusesLayoutSupported (const BusesLayout& layouts) const override
{
return containsLayout (layouts, LegacyChannelConfiguration);
}
//==============================================================================
const String getName() const override { return "LegacyPlugin"; }
bool acceptsMidi() const override { return false; }
bool producesMidi() const override { return false; }
double getTailLengthSeconds() const override { return 0.0; }
int getNumPrograms() override { return 1; }
int getCurrentProgram() override { return 0; }
void setCurrentProgram (int index) override {}
const String getProgramName (int index) override { return String(); }
void changeProgramName (int, const String&) override {}
bool supportsDoublePrecisionProcessing() const override { return true; }
//==============================================================================
void prepareToPlay (double /*sampleRate*/, int /*samplesPerBlock*/) override {}
void releaseResources() override {}
template <typename FloatType>
void process (AudioBuffer<FloatType>& buffer)
{
if (const float* gainValue = state.getRawParameterValue ("gain"))
{
FloatType gain = static_cast<FloatType> (*gainValue);
AudioBuffer<FloatType> inputBuffer = getBusBuffer (buffer, true, 0);
AudioBuffer<FloatType> outputBuffer = getBusBuffer (buffer, false, 0);
const int n = buffer.getNumSamples();
const int outChannels = outputBuffer.getNumChannels();
const int inChannels = inputBuffer .getNumChannels();
const FloatType* inLeft = inputBuffer.getReadPointer(0);
const FloatType* inRight = inputBuffer.getReadPointer(jmin (inChannels - 1, 1));
FloatType* outLeft = outputBuffer.getWritePointer(0);
FloatType* outRight = outputBuffer.getWritePointer(jmin (outChannels - 1, 1));
for (int i = 0; i < n; ++i)
{
FloatType left = *inLeft++;
FloatType right = inChannels > 1 ? *inRight++ : left;
if (outChannels > 1)
{
*outLeft++ = left * gain;
*outRight++ = right * gain;
}
else
{
*outLeft++ = static_cast<FloatType> (0.5) * gain * (left + right);
}
}
}
else
jassertfalse;
}
void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override { process (buffer); }
void processBlock (AudioBuffer<double>& buffer, MidiBuffer&) override { process (buffer); }
//==============================================================================
bool hasEditor() const override { return false; }
AudioProcessorEditor* createEditor() override { return nullptr; }
//==============================================================================
void getStateInformation (juce::MemoryBlock& destData) override
{
MemoryOutputStream stream (destData, true);
state.state.writeToStream (stream);
}
void setStateInformation (const void* data, int sizeInBytes) override
{
state.state.readFromData (data, sizeInBytes);
}
private:
AudioProcessorValueTreeState state;
};
If you want to do it the idiomatic and recommended way, you would use the following code (see my inline comments):
class ShinyNewPlugin : public AudioProcessor
{
public:
ShinyNewPlugin()
// Here we add the buses. We don't want any sidechains or aux buses so we are just adding
// a single input and a single output bus. The default layout of both is stereo. Remember
// this is just the default layout. The DAW may want to change this. Restrict this with
// the isBusesLayoutSupported callback.
: AudioProcessor (BusesProperties().withInput ("Input", AudioChannelSet::stereo(), true)
.withOutput ("Output", AudioChannelSet::stereo(), true)),
state (*this, nullptr)
{
state.createAndAddParameter ("gain", "Volume", "Volume",
NormalisableRange<float> (0.0f, 1.0f),
0.5f,
[] (float value) { return String (value); },
[] (const String& s) { return s.getFloatValue(); });
}
// Here we restrict the layouts of the busses. Use getMainInputChannels and
// getMainOutputChannels to check if the layout is supported or not
bool isBusesLayoutSupported (const BusesLayout& layouts) const override
{
// we only support stereo and mono
if (layouts.getMainInputChannels() == 0 || layouts.getMainInputChannels() > 2)
return false;
if (layouts.getMainOutputChannels() == 0 || layouts.getMainOutputChannels() > 2)
return false;
// we don't allow the narrowing the number of channels
if (layouts.getMainInputChannels() > layouts.getMainOutputChannels())
return false;
return true;
}
//==============================================================================
const String getName() const override { return "ShinyNewPlugin"; }
bool acceptsMidi() const override { return false; }
bool producesMidi() const override { return false; }
double getTailLengthSeconds() const override { return 0.0; }
int getNumPrograms() override { return 1; }
int getCurrentProgram() override { return 0; }
void setCurrentProgram (int index) override {}
const String getProgramName (int index) override { return String(); }
void changeProgramName (int, const String&) override {}
bool supportsDoublePrecisionProcessing() const override { return true; }
//==============================================================================
void prepareToPlay (double /*sampleRate*/, int /*samplesPerBlock*/) override {}
void releaseResources() override {}
template <typename FloatType>
void process (AudioBuffer<FloatType>& buffer)
{
if (const float* gainValue = state.getRawParameterValue ("gain"))
{
FloatType gain = static_cast<FloatType> (*gainValue);
AudioBuffer<FloatType> inputBuffer = getBusBuffer (buffer, true, 0);
AudioBuffer<FloatType> outputBuffer = getBusBuffer (buffer, false, 0);
const int n = buffer.getNumSamples();
const int outChannels = outputBuffer.getNumChannels();
const int inChannels = inputBuffer .getNumChannels();
const FloatType* inLeft = inputBuffer.getReadPointer(0);
const FloatType* inRight = inputBuffer.getReadPointer(jmin (inChannels - 1, 1));
FloatType* outLeft = outputBuffer.getWritePointer(0);
FloatType* outRight = outputBuffer.getWritePointer(jmin (outChannels - 1, 1));
for (int i = 0; i < n; ++i)
{
FloatType left = *inLeft++;
FloatType right = inChannels > 1 ? *inRight++ : left;
if (outChannels > 1)
{
*outLeft++ = left * gain;
*outRight++ = right * gain;
}
else
{
*outLeft++ = static_cast<FloatType> (0.5) * gain * (left + right);
}
}
}
else
jassertfalse;
}
void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override { process (buffer); }
void processBlock (AudioBuffer<double>& buffer, MidiBuffer&) override { process (buffer); }
//==============================================================================
bool hasEditor() const override { return false; }
AudioProcessorEditor* createEditor() override { return nullptr; }
//==============================================================================
void getStateInformation (juce::MemoryBlock& destData) override
{
MemoryOutputStream stream (destData, true);
state.state.writeToStream (stream);
}
void setStateInformation (const void* data, int sizeInBytes) override
{
state.state.readFromData (data, sizeInBytes);
}
private:
AudioProcessorValueTreeState state;
};
A named the later code “ShinyNewPlugin” but both examples will work.