mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-17 00:44:19 +00:00
Added an option to declare plug-in parameters as either continuous or discrete, irrespective of their number of steps
This commit is contained in:
parent
11d031f9f0
commit
4dcce5083c
16 changed files with 278 additions and 67 deletions
|
|
@ -6,14 +6,44 @@ Develop Branch
|
||||||
|
|
||||||
Change
|
Change
|
||||||
------
|
------
|
||||||
A new FrameRateType fps23976 has been added to AudioPlayHead
|
The method used to classify AudioUnit, VST3 and AAX plug-in parameters as
|
||||||
|
either continuous or discrete has changed.
|
||||||
|
|
||||||
Possible Issues
|
Possible Issues
|
||||||
---------------
|
---------------
|
||||||
Previously JUCE would report the FrameRateType fps24 for both 24 and
|
Plug-ins: DAW projects with automation data written by an AudioUnit, VST3 or
|
||||||
23.976 fps. If your code uses switch statements (or similar) to handle
|
AAX plug-in built with JUCE version 5.1.1 or earlier may load incorrectly when
|
||||||
all possible frame rate types, then this change may cause it to fall
|
opened by an AudioUnit, VST3 or AAX plug-in built with JUCE version 5.2.0 and
|
||||||
through.
|
later.
|
||||||
|
|
||||||
|
Hosts: The AudioPluginInstance::getParameterNumSteps method now returns correct
|
||||||
|
values for AU and VST3 plug-ins.
|
||||||
|
|
||||||
|
Workaround
|
||||||
|
----------
|
||||||
|
Plug-ins: Enable JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE in the
|
||||||
|
juce_audio_plugin_client module config page in the Projucer.
|
||||||
|
|
||||||
|
Hosts: Use AudioPluginInstance::getDefaultNumParameterSteps as the number of
|
||||||
|
steps for all parameters.
|
||||||
|
|
||||||
|
Rationale
|
||||||
|
---------
|
||||||
|
The old system for presenting plug-in parameters to a host as either continuous
|
||||||
|
or discrete is inconsistent between plug-in types and lacks sufficient
|
||||||
|
flexibility. This change harmonises the behaviour and allows individual
|
||||||
|
parameters to be marked as continuous or discrete.
|
||||||
|
|
||||||
|
|
||||||
|
Change
|
||||||
|
------
|
||||||
|
A new FrameRateType fps23976 has been added to AudioPlayHead,
|
||||||
|
|
||||||
|
Possible Issues
|
||||||
|
---------------
|
||||||
|
Previously JUCE would report the FrameRateType fps24 for both 24 and 23.976
|
||||||
|
fps. If your code uses switch statements (or similar) to handle all possible
|
||||||
|
frame rate types, then this change may cause it to fall through.
|
||||||
|
|
||||||
Workaround
|
Workaround
|
||||||
----------
|
----------
|
||||||
|
|
@ -21,8 +51,8 @@ Add fps23976 to your switch statement and handle it appropriately.
|
||||||
|
|
||||||
Rationale
|
Rationale
|
||||||
---------
|
---------
|
||||||
JUCE should be able to handle all popular frame rate codes but was
|
JUCE should be able to handle all popular frame rate codes but was missing
|
||||||
missing support for 23.976.
|
support for 23.976.
|
||||||
|
|
||||||
|
|
||||||
Change
|
Change
|
||||||
|
|
@ -120,7 +150,7 @@ or
|
||||||
|
|
||||||
2. Override the Look&Feel method
|
2. Override the Look&Feel method
|
||||||
PopupMenu::LookAndFeelMethods::shouldPopupMenuScaleWithTargetComponent and
|
PopupMenu::LookAndFeelMethods::shouldPopupMenuScaleWithTargetComponent and
|
||||||
return false. See
|
return false. See
|
||||||
https://github.com/WeAreROLI/JUCE/blob/c288c94c2914af20f36c03ca9c5401fcb555e4e9/modules/juce_gui_basics/menus/juce_PopupMenu.h#725
|
https://github.com/WeAreROLI/JUCE/blob/c288c94c2914af20f36c03ca9c5401fcb555e4e9/modules/juce_gui_basics/menus/juce_PopupMenu.h#725
|
||||||
|
|
||||||
Rationale
|
Rationale
|
||||||
|
|
|
||||||
|
|
@ -1319,12 +1319,14 @@ namespace AAXClasses
|
||||||
|
|
||||||
for (int parameterIndex = 0; parameterIndex < numParameters; ++parameterIndex)
|
for (int parameterIndex = 0; parameterIndex < numParameters; ++parameterIndex)
|
||||||
{
|
{
|
||||||
const AudioProcessorParameter::Category category = audioProcessor.getParameterCategory (parameterIndex);
|
auto* processorParam = audioProcessor.getParameters().getUnchecked (parameterIndex);
|
||||||
|
|
||||||
|
const AudioProcessorParameter::Category category = processorParam->getCategory();
|
||||||
|
|
||||||
aaxParamIDs.add (usingManagedParameters ? audioProcessor.getParameterID (parameterIndex)
|
aaxParamIDs.add (usingManagedParameters ? audioProcessor.getParameterID (parameterIndex)
|
||||||
: String (parameterIndex));
|
: String (parameterIndex));
|
||||||
|
|
||||||
AAX_CString paramName (audioProcessor.getParameterName (parameterIndex, 31).toRawUTF8());
|
AAX_CString paramName (processorParam->getName (31).toRawUTF8());
|
||||||
AAX_CParamID paramID = aaxParamIDs.getReference (parameterIndex).getCharPointer();
|
AAX_CParamID paramID = aaxParamIDs.getReference (parameterIndex).getCharPointer();
|
||||||
|
|
||||||
paramMap.set (AAXClasses::getAAXParamHash (paramID), parameterIndex);
|
paramMap.set (AAXClasses::getAAXParamHash (paramID), parameterIndex);
|
||||||
|
|
@ -1339,19 +1341,25 @@ namespace AAXClasses
|
||||||
AAX_IParameter* parameter
|
AAX_IParameter* parameter
|
||||||
= new AAX_CParameter<float> (paramID,
|
= new AAX_CParameter<float> (paramID,
|
||||||
paramName,
|
paramName,
|
||||||
audioProcessor.getParameterDefaultValue (parameterIndex),
|
processorParam->getDefaultValue(),
|
||||||
AAX_CLinearTaperDelegate<float, 0>(),
|
AAX_CLinearTaperDelegate<float, 0>(),
|
||||||
AAX_CNumberDisplayDelegate<float, 3>(),
|
AAX_CNumberDisplayDelegate<float, 3>(),
|
||||||
audioProcessor.isParameterAutomatable (parameterIndex));
|
audioProcessor.isParameterAutomatable (parameterIndex));
|
||||||
|
|
||||||
parameter->AddShortenedName (audioProcessor.getParameterName (parameterIndex, 4).toRawUTF8());
|
parameter->AddShortenedName (processorParam->getName (4).toRawUTF8());
|
||||||
|
|
||||||
const int parameterNumSteps = audioProcessor.getParameterNumSteps (parameterIndex);
|
const int parameterNumSteps = processorParam->getNumSteps();
|
||||||
parameter->SetNumberOfSteps ((uint32_t) parameterNumSteps);
|
parameter->SetNumberOfSteps ((uint32_t) parameterNumSteps);
|
||||||
|
|
||||||
|
#if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
|
||||||
parameter->SetType (parameterNumSteps > 1000 ? AAX_eParameterType_Continuous
|
parameter->SetType (parameterNumSteps > 1000 ? AAX_eParameterType_Continuous
|
||||||
: AAX_eParameterType_Discrete);
|
: AAX_eParameterType_Discrete);
|
||||||
|
#else
|
||||||
|
parameter->SetType (processorParam->isDiscrete() ? AAX_eParameterType_Discrete
|
||||||
|
: AAX_eParameterType_Continuous);
|
||||||
|
#endif
|
||||||
|
|
||||||
parameter->SetOrientation (audioProcessor.isParameterOrientationInverted (parameterIndex)
|
parameter->SetOrientation (processorParam->isOrientationInverted()
|
||||||
? (AAX_eParameterOrientation_RightMinLeftMax | AAX_eParameterOrientation_TopMinBottomMax
|
? (AAX_eParameterOrientation_RightMinLeftMax | AAX_eParameterOrientation_TopMinBottomMax
|
||||||
| AAX_eParameterOrientation_RotarySingleDotMode | AAX_eParameterOrientation_RotaryRightMinLeftMax)
|
| AAX_eParameterOrientation_RotarySingleDotMode | AAX_eParameterOrientation_RotaryRightMinLeftMax)
|
||||||
: (AAX_eParameterOrientation_LeftMinRightMax | AAX_eParameterOrientation_BottomMinTopMax
|
: (AAX_eParameterOrientation_LeftMinRightMax | AAX_eParameterOrientation_BottomMinTopMax
|
||||||
|
|
|
||||||
|
|
@ -525,7 +525,7 @@ public:
|
||||||
const String text (String::fromCFString (pv->inString));
|
const String text (String::fromCFString (pv->inString));
|
||||||
|
|
||||||
if (AudioProcessorParameter* param = juceFilter->getParameters() [paramID])
|
if (AudioProcessorParameter* param = juceFilter->getParameters() [paramID])
|
||||||
pv->outValue = param->getValueForText (text);
|
pv->outValue = param->getValueForText (text) * getMaximumParameterValue (param);
|
||||||
else
|
else
|
||||||
pv->outValue = text.getFloatValue();
|
pv->outValue = text.getFloatValue();
|
||||||
|
|
||||||
|
|
@ -546,11 +546,12 @@ public:
|
||||||
String text;
|
String text;
|
||||||
|
|
||||||
if (AudioProcessorParameter* param = juceFilter->getParameters() [paramID])
|
if (AudioProcessorParameter* param = juceFilter->getParameters() [paramID])
|
||||||
text = param->getText (value, 0);
|
text = param->getText (value / getMaximumParameterValue (param), 0);
|
||||||
else
|
else
|
||||||
text = String (value);
|
text = String (value);
|
||||||
|
|
||||||
pv->outString = text.toCFString();
|
pv->outString = text.toCFString();
|
||||||
|
|
||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -803,6 +804,17 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
// When parameters are discrete we need to use integer values.
|
||||||
|
static float getMaximumParameterValue (AudioProcessorParameter* param)
|
||||||
|
{
|
||||||
|
#if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
|
||||||
|
ignoreUnused (param);
|
||||||
|
return 1.0f;
|
||||||
|
#else
|
||||||
|
return param->isDiscrete() ? (float) (param->getNumSteps() - 1) : 1.0f;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
ComponentResult GetParameterInfo (AudioUnitScope inScope,
|
ComponentResult GetParameterInfo (AudioUnitScope inScope,
|
||||||
AudioUnitParameterID inParameterID,
|
AudioUnitParameterID inParameterID,
|
||||||
AudioUnitParameterInfo& outParameterInfo) override
|
AudioUnitParameterInfo& outParameterInfo) override
|
||||||
|
|
@ -811,7 +823,7 @@ public:
|
||||||
|
|
||||||
if (inScope == kAudioUnitScope_Global
|
if (inScope == kAudioUnitScope_Global
|
||||||
&& juceFilter != nullptr
|
&& juceFilter != nullptr
|
||||||
&& index < juceFilter->getNumParameters())
|
&& isPositiveAndBelow (index, juceFilter->getNumParameters()))
|
||||||
{
|
{
|
||||||
outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
|
outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
|
||||||
outParameterInfo.flags = (UInt32) (kAudioUnitParameterFlag_IsWritable
|
outParameterInfo.flags = (UInt32) (kAudioUnitParameterFlag_IsWritable
|
||||||
|
|
@ -823,28 +835,40 @@ public:
|
||||||
outParameterInfo.flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution;
|
outParameterInfo.flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const String name (juceFilter->getParameterName (index));
|
auto* param = juceFilter->getParameters().getUnchecked (index);
|
||||||
|
|
||||||
// set whether the param is automatable (unnamed parameters aren't allowed to be automated)
|
const String name (param->getName (512));
|
||||||
if (name.isEmpty() || ! juceFilter->isParameterAutomatable (index))
|
|
||||||
|
// Set whether the param is automatable (unnamed parameters aren't allowed to be automated)
|
||||||
|
if (name.isEmpty() || ! param->isAutomatable())
|
||||||
outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime;
|
outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime;
|
||||||
|
|
||||||
if (juceFilter->isMetaParameter (index))
|
if (! param->isDiscrete())
|
||||||
|
outParameterInfo.flags |= kAudioUnitParameterFlag_CanRamp;
|
||||||
|
|
||||||
|
if (param->isMetaParameter())
|
||||||
outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;
|
outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;
|
||||||
|
|
||||||
// is this a meter?
|
// Is this a meter?
|
||||||
if (((juceFilter->getParameterCategory (index) & 0xffff0000) >> 16) == 2)
|
if (((param->getCategory() & 0xffff0000) >> 16) == 2)
|
||||||
{
|
{
|
||||||
outParameterInfo.flags &= ~kAudioUnitParameterFlag_IsWritable;
|
outParameterInfo.flags &= ~kAudioUnitParameterFlag_IsWritable;
|
||||||
outParameterInfo.flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic;
|
outParameterInfo.flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic;
|
||||||
outParameterInfo.unit = kAudioUnitParameterUnit_LinearGain;
|
outParameterInfo.unit = kAudioUnitParameterUnit_LinearGain;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
|
||||||
|
outParameterInfo.unit = param->isDiscrete() ? kAudioUnitParameterUnit_Indexed
|
||||||
|
: kAudioUnitParameterUnit_Generic;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
MusicDeviceBase::FillInParameterName (outParameterInfo, name.toCFString(), true);
|
MusicDeviceBase::FillInParameterName (outParameterInfo, name.toCFString(), true);
|
||||||
|
|
||||||
outParameterInfo.minValue = 0.0f;
|
outParameterInfo.minValue = 0.0f;
|
||||||
outParameterInfo.maxValue = 1.0f;
|
outParameterInfo.maxValue = getMaximumParameterValue (param);
|
||||||
outParameterInfo.defaultValue = juceFilter->getParameterDefaultValue (index);
|
outParameterInfo.defaultValue = param->getDefaultValue();
|
||||||
jassert (outParameterInfo.defaultValue >= outParameterInfo.minValue
|
jassert (outParameterInfo.defaultValue >= outParameterInfo.minValue
|
||||||
&& outParameterInfo.defaultValue <= outParameterInfo.maxValue);
|
&& outParameterInfo.defaultValue <= outParameterInfo.maxValue);
|
||||||
|
|
||||||
|
|
@ -861,9 +885,9 @@ public:
|
||||||
{
|
{
|
||||||
if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
|
if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
|
||||||
{
|
{
|
||||||
const int index = getJuceIndexForAUParameterID (inID);
|
auto* param = juceFilter->getParameters().getUnchecked (getJuceIndexForAUParameterID (inID));
|
||||||
|
|
||||||
outValue = juceFilter->getParameter (index);
|
outValue = param->getValue() * getMaximumParameterValue (param);
|
||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -878,9 +902,9 @@ public:
|
||||||
{
|
{
|
||||||
if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
|
if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
|
||||||
{
|
{
|
||||||
const int index = getJuceIndexForAUParameterID (inID);
|
auto* param = juceFilter->getParameters().getUnchecked (getJuceIndexForAUParameterID (inID));
|
||||||
|
|
||||||
juceFilter->setParameter (index, inValue);
|
param->setValue (inValue / getMaximumParameterValue (param));
|
||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1727,6 +1751,14 @@ private:
|
||||||
{
|
{
|
||||||
Globals()->UseIndexedParameters (numParams);
|
Globals()->UseIndexedParameters (numParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if JUCE_DEBUG
|
||||||
|
// Some hosts can't handle the huge numbers of discrete parameter values created when
|
||||||
|
// using the default number of steps.
|
||||||
|
for (auto* param : juceFilter->getParameters())
|
||||||
|
if (param->isDiscrete())
|
||||||
|
jassert (param->getNumSteps() != juceFilter->getDefaultNumParameterSteps());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
|
||||||
|
|
@ -204,21 +204,32 @@ public:
|
||||||
Param (AudioProcessor& p, int index, Vst::ParamID paramID) : owner (p), paramIndex (index)
|
Param (AudioProcessor& p, int index, Vst::ParamID paramID) : owner (p), paramIndex (index)
|
||||||
{
|
{
|
||||||
info.id = paramID;
|
info.id = paramID;
|
||||||
toString128 (info.title, p.getParameterName (index));
|
|
||||||
toString128 (info.shortTitle, p.getParameterName (index, 8));
|
|
||||||
toString128 (info.units, p.getParameterLabel (index));
|
|
||||||
|
|
||||||
const int numSteps = p.getParameterNumSteps (index);
|
auto* param = p.getParameters().getUnchecked (index);
|
||||||
info.stepCount = (Steinberg::int32) (numSteps > 0 && numSteps < 0x7fffffff ? numSteps - 1 : 0);
|
|
||||||
info.defaultNormalizedValue = p.getParameterDefaultValue (index);
|
toString128 (info.title, param->getName (128));
|
||||||
|
toString128 (info.shortTitle, param->getName (8));
|
||||||
|
toString128 (info.units, param->getLabel());
|
||||||
|
|
||||||
|
info.stepCount = (Steinberg::int32) 0;
|
||||||
|
|
||||||
|
#if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
|
||||||
|
if (param->isDiscrete())
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
const int numSteps = param->getNumSteps();
|
||||||
|
info.stepCount = (Steinberg::int32) (numSteps > 0 && numSteps < 0x7fffffff ? numSteps - 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
info.defaultNormalizedValue = param->getDefaultValue();
|
||||||
jassert (info.defaultNormalizedValue >= 0 && info.defaultNormalizedValue <= 1.0f);
|
jassert (info.defaultNormalizedValue >= 0 && info.defaultNormalizedValue <= 1.0f);
|
||||||
info.unitId = Vst::kRootUnitId;
|
info.unitId = Vst::kRootUnitId;
|
||||||
|
|
||||||
// is this a meter?
|
// Is this a meter?
|
||||||
if (((p.getParameterCategory (index) & 0xffff0000) >> 16) == 2)
|
if (((param->getCategory() & 0xffff0000) >> 16) == 2)
|
||||||
info.flags = Vst::ParameterInfo::kIsReadOnly;
|
info.flags = Vst::ParameterInfo::kIsReadOnly;
|
||||||
else
|
else
|
||||||
info.flags = p.isParameterAutomatable (index) ? Vst::ParameterInfo::kCanAutomate : 0;
|
info.flags = param->isAutomatable() ? Vst::ParameterInfo::kCanAutomate : 0;
|
||||||
|
|
||||||
valueNormalized = info.defaultNormalizedValue;
|
valueNormalized = info.defaultNormalizedValue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,15 +65,28 @@
|
||||||
#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 0
|
#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** Config: JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
|
||||||
|
|
||||||
|
Enable this if you want to force JUCE to use a legacy scheme for
|
||||||
|
identifying plug-in parameters as either continuous or discrete.
|
||||||
|
DAW projects with automation data written by an AudioUnit, VST3 or
|
||||||
|
AAX plug-in built with JUCE version 5.1.1 or earlier may load
|
||||||
|
incorrectly when opened by an AudioUnit, VST3 or AAX plug-in built
|
||||||
|
with JUCE version 5.2.0 and later.
|
||||||
|
*/
|
||||||
|
#ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
|
||||||
|
#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Config: JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS
|
/** Config: JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS
|
||||||
|
|
||||||
Enable this if you want JUCE to use parameter ids which are compatible
|
Enable this if you want JUCE to use parameter ids which are compatible
|
||||||
to Studio One. Studio One ignores any parameter ids which are negative.
|
with Studio One. Studio One ignores any parameter ids which are negative.
|
||||||
Enabling this option will make JUCE generate only positive parameter ids.
|
Enabling this option will make JUCE generate only positive parameter ids.
|
||||||
Note that if you have already released a plug-in prio to JUCE 4.3.0 then
|
Note that if you have already released a plug-in prior to JUCE 4.3.0 then
|
||||||
enabling this will change your parameter ids making your plug-in
|
enabling this will change your parameter ids making your plug-in
|
||||||
incompatible to old automation data.
|
incompatible to old automation data.
|
||||||
*/
|
*/
|
||||||
#ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS
|
#ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS
|
||||||
#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1
|
#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -986,6 +986,22 @@ public:
|
||||||
|
|
||||||
const String getParameterText (int index) override { return String (getParameter (index)); }
|
const String getParameterText (int index) override { return String (getParameter (index)); }
|
||||||
|
|
||||||
|
int getParameterNumSteps (int index) override
|
||||||
|
{
|
||||||
|
if (auto* p = parameters[index])
|
||||||
|
return p->numSteps;
|
||||||
|
|
||||||
|
return AudioProcessor::getDefaultNumParameterSteps();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isParameterDiscrete (int index) const override
|
||||||
|
{
|
||||||
|
if (auto* p = parameters[index])
|
||||||
|
return p->discrete;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool isParameterAutomatable (int index) const override
|
bool isParameterAutomatable (int index) const override
|
||||||
{
|
{
|
||||||
if (auto* p = parameters[index])
|
if (auto* p = parameters[index])
|
||||||
|
|
@ -1178,6 +1194,8 @@ public:
|
||||||
param->minValue = info.minValue;
|
param->minValue = info.minValue;
|
||||||
param->maxValue = info.maxValue;
|
param->maxValue = info.maxValue;
|
||||||
param->automatable = (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0;
|
param->automatable = (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0;
|
||||||
|
param->discrete = (info.unit == kAudioUnitParameterUnit_Indexed);
|
||||||
|
param->numSteps = param->discrete ? (int) (info.maxValue + 1.0f) : AudioProcessor::getDefaultNumParameterSteps();
|
||||||
|
|
||||||
if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0)
|
if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0)
|
||||||
{
|
{
|
||||||
|
|
@ -1263,7 +1281,8 @@ private:
|
||||||
UInt32 paramID;
|
UInt32 paramID;
|
||||||
String name;
|
String name;
|
||||||
AudioUnitParameterValue minValue, maxValue;
|
AudioUnitParameterValue minValue, maxValue;
|
||||||
bool automatable;
|
bool automatable, discrete;
|
||||||
|
int numSteps;
|
||||||
};
|
};
|
||||||
|
|
||||||
OwnedArray<ParamInfo> parameters;
|
OwnedArray<ParamInfo> parameters;
|
||||||
|
|
|
||||||
|
|
@ -2247,17 +2247,6 @@ struct VST3PluginInstance : public AudioPluginInstance
|
||||||
return toString (getParameterInfoForIndex (parameterIndex).title);
|
return toString (getParameterInfoForIndex (parameterIndex).title);
|
||||||
}
|
}
|
||||||
|
|
||||||
float getParameter (int parameterIndex) override
|
|
||||||
{
|
|
||||||
if (editController != nullptr)
|
|
||||||
{
|
|
||||||
auto id = getParameterInfoForIndex (parameterIndex).id;
|
|
||||||
return (float) editController->getParamNormalized (id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
const String getParameterText (int parameterIndex) override
|
const String getParameterText (int parameterIndex) override
|
||||||
{
|
{
|
||||||
if (editController != nullptr)
|
if (editController != nullptr)
|
||||||
|
|
@ -2273,6 +2262,41 @@ struct VST3PluginInstance : public AudioPluginInstance
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getParameterNumSteps (int parameterIndex) override
|
||||||
|
{
|
||||||
|
if (editController != nullptr)
|
||||||
|
{
|
||||||
|
const auto numSteps = getParameterInfoForIndex (parameterIndex).stepCount;
|
||||||
|
|
||||||
|
if (numSteps > 0)
|
||||||
|
return numSteps;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AudioProcessor::getDefaultNumParameterSteps();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isParameterDiscrete (int parameterIndex) const override
|
||||||
|
{
|
||||||
|
if (editController != nullptr)
|
||||||
|
{
|
||||||
|
const auto numSteps = getParameterInfoForIndex (parameterIndex).stepCount;
|
||||||
|
return numSteps > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getParameter (int parameterIndex) override
|
||||||
|
{
|
||||||
|
if (editController != nullptr)
|
||||||
|
{
|
||||||
|
auto id = getParameterInfoForIndex (parameterIndex).id;
|
||||||
|
return (float) editController->getParamNormalized (id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
void setParameter (int parameterIndex, float newValue) override
|
void setParameter (int parameterIndex, float newValue) override
|
||||||
{
|
{
|
||||||
if (editController != nullptr)
|
if (editController != nullptr)
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,18 @@
|
||||||
#include <juce_gui_basics/juce_gui_basics.h>
|
#include <juce_gui_basics/juce_gui_basics.h>
|
||||||
#include <juce_audio_basics/juce_audio_basics.h>
|
#include <juce_audio_basics/juce_audio_basics.h>
|
||||||
|
|
||||||
|
/** Config: JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
|
||||||
|
|
||||||
|
Enable this if you want to force JUCE to use an old scheme for selecting
|
||||||
|
when parameters are classified as discrete or continuous in a host. If this
|
||||||
|
is not enabled then DAW projects with automation data written by an
|
||||||
|
AudioUnit, VST3 or AAX plug-in built with JUCE version 5.1.1 or earlier may
|
||||||
|
load incorrectly when opened by an AudioUnit, VST3 or AAX plug-in built
|
||||||
|
with JUCE version 5.2.0 and later.
|
||||||
|
*/
|
||||||
|
#ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
|
||||||
|
#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/** Config: JUCE_PLUGINHOST_VST
|
/** Config: JUCE_PLUGINHOST_VST
|
||||||
|
|
|
||||||
|
|
@ -625,6 +625,14 @@ int AudioProcessor::getDefaultNumParameterSteps() noexcept
|
||||||
return 0x7fffffff;
|
return 0x7fffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AudioProcessor::isParameterDiscrete (int index) const
|
||||||
|
{
|
||||||
|
if (auto* p = managedParameters[index])
|
||||||
|
return p->isDiscrete();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
String AudioProcessor::getParameterLabel (int index) const
|
String AudioProcessor::getParameterLabel (int index) const
|
||||||
{
|
{
|
||||||
if (auto* p = managedParameters[index])
|
if (auto* p = managedParameters[index])
|
||||||
|
|
@ -1401,11 +1409,12 @@ void AudioProcessorParameter::endChangeGesture()
|
||||||
processor->endParameterChangeGesture (parameterIndex);
|
processor->endParameterChangeGesture (parameterIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioProcessorParameter::isOrientationInverted() const { return false; }
|
bool AudioProcessorParameter::isOrientationInverted() const { return false; }
|
||||||
bool AudioProcessorParameter::isAutomatable() const { return true; }
|
bool AudioProcessorParameter::isAutomatable() const { return true; }
|
||||||
bool AudioProcessorParameter::isMetaParameter() const { return false; }
|
bool AudioProcessorParameter::isMetaParameter() const { return false; }
|
||||||
AudioProcessorParameter::Category AudioProcessorParameter::getCategory() const { return genericParameter; }
|
AudioProcessorParameter::Category AudioProcessorParameter::getCategory() const { return genericParameter; }
|
||||||
int AudioProcessorParameter::getNumSteps() const { return AudioProcessor::getDefaultNumParameterSteps(); }
|
int AudioProcessorParameter::getNumSteps() const { return AudioProcessor::getDefaultNumParameterSteps(); }
|
||||||
|
bool AudioProcessorParameter::isDiscrete() const { return false; }
|
||||||
|
|
||||||
String AudioProcessorParameter::getText (float value, int /*maximumStringLength*/) const
|
String AudioProcessorParameter::getText (float value, int /*maximumStringLength*/) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1020,13 +1020,22 @@ public:
|
||||||
virtual String getParameterText (int parameterIndex, int maximumStringLength);
|
virtual String getParameterText (int parameterIndex, int maximumStringLength);
|
||||||
|
|
||||||
/** Returns the number of discrete steps that this parameter can represent.
|
/** Returns the number of discrete steps that this parameter can represent.
|
||||||
|
|
||||||
The default return value if you don't implement this method is
|
The default return value if you don't implement this method is
|
||||||
AudioProcessor::getDefaultNumParameterSteps().
|
AudioProcessor::getDefaultNumParameterSteps().
|
||||||
|
|
||||||
If your parameter is boolean, then you may want to make this return 2.
|
If your parameter is boolean, then you may want to make this return 2.
|
||||||
|
|
||||||
|
If you want the host to display stepped automation values, rather than a
|
||||||
|
continuous interpolation between successive values, you should ensure that
|
||||||
|
isParameterDiscrete returns true.
|
||||||
|
|
||||||
The value that is returned may or may not be used, depending on the host.
|
The value that is returned may or may not be used, depending on the host.
|
||||||
|
|
||||||
NOTE! This method will eventually be deprecated! It's recommended that you use
|
NOTE! This method will eventually be deprecated! It's recommended that you use
|
||||||
AudioProcessorParameter::getNumSteps() instead.
|
AudioProcessorParameter::getNumSteps() instead.
|
||||||
|
|
||||||
|
@see isParameterDiscrete
|
||||||
*/
|
*/
|
||||||
virtual int getParameterNumSteps (int parameterIndex);
|
virtual int getParameterNumSteps (int parameterIndex);
|
||||||
|
|
||||||
|
|
@ -1034,10 +1043,26 @@ public:
|
||||||
|
|
||||||
NOTE! This method will eventually be deprecated! It's recommended that you use
|
NOTE! This method will eventually be deprecated! It's recommended that you use
|
||||||
AudioProcessorParameter::getNumSteps() instead.
|
AudioProcessorParameter::getNumSteps() instead.
|
||||||
|
|
||||||
@see getParameterNumSteps
|
@see getParameterNumSteps
|
||||||
*/
|
*/
|
||||||
static int getDefaultNumParameterSteps() noexcept;
|
static int getDefaultNumParameterSteps() noexcept;
|
||||||
|
|
||||||
|
/** Returns true if the parameter should take discrete, rather than continuous
|
||||||
|
values.
|
||||||
|
|
||||||
|
If the parameter is boolean, this should return true (with getParameterNumSteps
|
||||||
|
returning 2).
|
||||||
|
|
||||||
|
The value that is returned may or may not be used, depending on the host.
|
||||||
|
|
||||||
|
NOTE! This method will eventually be deprecated! It's recommended that you use
|
||||||
|
AudioProcessorParameter::isDiscrete() instead.
|
||||||
|
|
||||||
|
@see getParameterNumSteps
|
||||||
|
*/
|
||||||
|
virtual bool isParameterDiscrete (int parameterIndex) const;
|
||||||
|
|
||||||
/** Returns the default value for the parameter.
|
/** Returns the default value for the parameter.
|
||||||
By default, this just returns 0.
|
By default, this just returns 0.
|
||||||
The value that is returned may or may not be used, depending on the host.
|
The value that is returned may or may not be used, depending on the host.
|
||||||
|
|
|
||||||
|
|
@ -106,16 +106,32 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual String getLabel() const = 0;
|
virtual String getLabel() const = 0;
|
||||||
|
|
||||||
/** Returns the number of discrete interval steps that this parameter's range
|
/** Returns the number of steps that this parameter's range should be quantised into.
|
||||||
should be quantised into.
|
|
||||||
|
|
||||||
If you want a continuous range of values, don't override this method, and allow
|
If you want a continuous range of values, don't override this method, and allow
|
||||||
the default implementation to return AudioProcessor::getDefaultNumParameterSteps().
|
the default implementation to return AudioProcessor::getDefaultNumParameterSteps().
|
||||||
|
|
||||||
If your parameter is boolean, then you may want to make this return 2.
|
If your parameter is boolean, then you may want to make this return 2.
|
||||||
The value that is returned may or may not be used, depending on the host.
|
|
||||||
|
The value that is returned may or may not be used, depending on the host. If you
|
||||||
|
want the host to display stepped automation values, rather than a continuous
|
||||||
|
interpolation between successive values, you should override isDiscrete to return true.
|
||||||
|
|
||||||
|
@see isDiscrete
|
||||||
*/
|
*/
|
||||||
virtual int getNumSteps() const;
|
virtual int getNumSteps() const;
|
||||||
|
|
||||||
|
/** Returns whether the parameter uses discrete values, based on the result of
|
||||||
|
getNumSteps, or allows the host to select values continuously.
|
||||||
|
|
||||||
|
This information may or may not be used, depending on the host. If you
|
||||||
|
want the host to display stepped automation values, rather than a continuous
|
||||||
|
interpolation between successive values, override this method to return true.
|
||||||
|
|
||||||
|
@see getNumSteps
|
||||||
|
*/
|
||||||
|
virtual bool isDiscrete() const;
|
||||||
|
|
||||||
/** Returns a textual version of the supplied parameter value.
|
/** Returns a textual version of the supplied parameter value.
|
||||||
The default implementation just returns the floating point value
|
The default implementation just returns the floating point value
|
||||||
as a string, but this could do anything you need for a custom type
|
as a string, but this could do anything you need for a custom type
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ private:
|
||||||
void setValue (float newValue) override;
|
void setValue (float newValue) override;
|
||||||
float getDefaultValue() const override;
|
float getDefaultValue() const override;
|
||||||
int getNumSteps() const override;
|
int getNumSteps() const override;
|
||||||
|
bool isDiscrete() const override;
|
||||||
String getText (float, int) const override;
|
String getText (float, int) const override;
|
||||||
float getValueForText (const String&) const override;
|
float getValueForText (const String&) const override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ private:
|
||||||
void setValue (float newValue) override;
|
void setValue (float newValue) override;
|
||||||
float getDefaultValue() const override;
|
float getDefaultValue() const override;
|
||||||
int getNumSteps() const override;
|
int getNumSteps() const override;
|
||||||
|
bool isDiscrete() const override;
|
||||||
String getText (float, int) const override;
|
String getText (float, int) const override;
|
||||||
float getValueForText (const String&) const override;
|
float getValueForText (const String&) const override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@ float AudioParameterBool::getValue() const { retur
|
||||||
void AudioParameterBool::setValue (float newValue) { value = newValue; }
|
void AudioParameterBool::setValue (float newValue) { value = newValue; }
|
||||||
float AudioParameterBool::getDefaultValue() const { return defaultValue; }
|
float AudioParameterBool::getDefaultValue() const { return defaultValue; }
|
||||||
int AudioParameterBool::getNumSteps() const { return 2; }
|
int AudioParameterBool::getNumSteps() const { return 2; }
|
||||||
|
bool AudioParameterBool::isDiscrete() const { return true; }
|
||||||
float AudioParameterBool::getValueForText (const String& text) const { return text.getIntValue() != 0 ? 1.0f : 0.0f; }
|
float AudioParameterBool::getValueForText (const String& text) const { return text.getIntValue() != 0 ? 1.0f : 0.0f; }
|
||||||
String AudioParameterBool::getText (float v, int /*length*/) const { return String ((int) (v > 0.5f ? 1 : 0)); }
|
String AudioParameterBool::getText (float v, int /*length*/) const { return String ((int) (v > 0.5f ? 1 : 0)); }
|
||||||
|
|
||||||
|
|
@ -156,6 +157,7 @@ float AudioParameterChoice::getValue() const { retur
|
||||||
void AudioParameterChoice::setValue (float newValue) { value = (float) convertFrom0to1 (newValue); }
|
void AudioParameterChoice::setValue (float newValue) { value = (float) convertFrom0to1 (newValue); }
|
||||||
float AudioParameterChoice::getDefaultValue() const { return defaultValue; }
|
float AudioParameterChoice::getDefaultValue() const { return defaultValue; }
|
||||||
int AudioParameterChoice::getNumSteps() const { return choices.size(); }
|
int AudioParameterChoice::getNumSteps() const { return choices.size(); }
|
||||||
|
bool AudioParameterChoice::isDiscrete() const { return true; }
|
||||||
float AudioParameterChoice::getValueForText (const String& text) const { return convertTo0to1 (choices.indexOf (text)); }
|
float AudioParameterChoice::getValueForText (const String& text) const { return convertTo0to1 (choices.indexOf (text)); }
|
||||||
String AudioParameterChoice::getText (float v, int /*length*/) const { return choices [convertFrom0to1 (v)]; }
|
String AudioParameterChoice::getText (float v, int /*length*/) const { return choices [convertFrom0to1 (v)]; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,15 @@ struct AudioProcessorValueTreeState::Parameter : public AudioProcessorParamete
|
||||||
std::function<String (float)> valueToText,
|
std::function<String (float)> valueToText,
|
||||||
std::function<float (const String&)> textToValue,
|
std::function<float (const String&)> textToValue,
|
||||||
bool meta,
|
bool meta,
|
||||||
bool automatable)
|
bool automatable,
|
||||||
|
bool discrete)
|
||||||
: AudioProcessorParameterWithID (parameterID, paramName, labelText),
|
: AudioProcessorParameterWithID (parameterID, paramName, labelText),
|
||||||
owner (s), valueToTextFunction (valueToText), textToValueFunction (textToValue),
|
owner (s), valueToTextFunction (valueToText), textToValueFunction (textToValue),
|
||||||
range (r), value (defaultVal), defaultValue (defaultVal),
|
range (r), value (defaultVal), defaultValue (defaultVal),
|
||||||
listenersNeedCalling (true),
|
listenersNeedCalling (true),
|
||||||
isMetaParam (meta),
|
isMetaParam (meta),
|
||||||
isAutomatableParam (automatable)
|
isAutomatableParam (automatable),
|
||||||
|
isDiscreteParam (discrete)
|
||||||
{
|
{
|
||||||
state.addListener (this);
|
state.addListener (this);
|
||||||
needsUpdate.set (1);
|
needsUpdate.set (1);
|
||||||
|
|
@ -150,6 +152,7 @@ struct AudioProcessorValueTreeState::Parameter : public AudioProcessorParamete
|
||||||
|
|
||||||
bool isMetaParameter() const override { return isMetaParam; }
|
bool isMetaParameter() const override { return isMetaParam; }
|
||||||
bool isAutomatable() const override { return isAutomatableParam; }
|
bool isAutomatable() const override { return isAutomatableParam; }
|
||||||
|
bool isDiscrete() const override { return isDiscreteParam; }
|
||||||
|
|
||||||
AudioProcessorValueTreeState& owner;
|
AudioProcessorValueTreeState& owner;
|
||||||
ValueTree state;
|
ValueTree state;
|
||||||
|
|
@ -160,7 +163,7 @@ struct AudioProcessorValueTreeState::Parameter : public AudioProcessorParamete
|
||||||
float value, defaultValue;
|
float value, defaultValue;
|
||||||
Atomic<int> needsUpdate;
|
Atomic<int> needsUpdate;
|
||||||
bool listenersNeedCalling;
|
bool listenersNeedCalling;
|
||||||
const bool isMetaParam, isAutomatableParam;
|
const bool isMetaParam, isAutomatableParam, isDiscreteParam;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Parameter)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Parameter)
|
||||||
};
|
};
|
||||||
|
|
@ -185,7 +188,8 @@ AudioProcessorParameterWithID* AudioProcessorValueTreeState::createAndAddParamet
|
||||||
float defaultVal, std::function<String (float)> valueToTextFunction,
|
float defaultVal, std::function<String (float)> valueToTextFunction,
|
||||||
std::function<float (const String&)> textToValueFunction,
|
std::function<float (const String&)> textToValueFunction,
|
||||||
bool isMetaParameter,
|
bool isMetaParameter,
|
||||||
bool isAutomatableParameter)
|
bool isAutomatableParameter,
|
||||||
|
bool isDiscreteParameter)
|
||||||
{
|
{
|
||||||
// All parameters must be created before giving this manager a ValueTree state!
|
// All parameters must be created before giving this manager a ValueTree state!
|
||||||
jassert (! state.isValid());
|
jassert (! state.isValid());
|
||||||
|
|
@ -195,7 +199,8 @@ AudioProcessorParameterWithID* AudioProcessorValueTreeState::createAndAddParamet
|
||||||
|
|
||||||
Parameter* p = new Parameter (*this, paramID, paramName, labelText, r,
|
Parameter* p = new Parameter (*this, paramID, paramName, labelText, r,
|
||||||
defaultVal, valueToTextFunction, textToValueFunction,
|
defaultVal, valueToTextFunction, textToValueFunction,
|
||||||
isMetaParameter, isAutomatableParameter);
|
isMetaParameter, isAutomatableParameter,
|
||||||
|
isDiscreteParameter);
|
||||||
processor.addParameter (p);
|
processor.addParameter (p);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,8 @@ public:
|
||||||
@param textToValueFunction The inverse of valueToTextFunction
|
@param textToValueFunction The inverse of valueToTextFunction
|
||||||
@param isMetaParameter Set this value to true if this should be a meta parameter
|
@param isMetaParameter Set this value to true if this should be a meta parameter
|
||||||
@param isAutomatableParameter Set this value to false if this parameter should not be automatable
|
@param isAutomatableParameter Set this value to false if this parameter should not be automatable
|
||||||
|
@param isDiscrete Set this value to true to make this parameter take discrete values in a host.
|
||||||
|
@see AudioProcessorParameter::isDiscrete
|
||||||
|
|
||||||
@returns the parameter object that was created
|
@returns the parameter object that was created
|
||||||
*/
|
*/
|
||||||
|
|
@ -88,7 +90,8 @@ public:
|
||||||
std::function<String (float)> valueToTextFunction,
|
std::function<String (float)> valueToTextFunction,
|
||||||
std::function<float (const String&)> textToValueFunction,
|
std::function<float (const String&)> textToValueFunction,
|
||||||
bool isMetaParameter = false,
|
bool isMetaParameter = false,
|
||||||
bool isAutomatableParameter = true);
|
bool isAutomatableParameter = true,
|
||||||
|
bool isDiscrete = false);
|
||||||
|
|
||||||
/** Returns a parameter by its ID string. */
|
/** Returns a parameter by its ID string. */
|
||||||
AudioProcessorParameterWithID* getParameter (StringRef parameterID) const noexcept;
|
AudioProcessorParameterWithID* getParameter (StringRef parameterID) const noexcept;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue