mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-22 01:34:21 +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
|
||||
------
|
||||
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
|
||||
---------------
|
||||
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.
|
||||
Plug-ins: 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.
|
||||
|
||||
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
|
||||
----------
|
||||
|
|
@ -21,8 +51,8 @@ Add fps23976 to your switch statement and handle it appropriately.
|
|||
|
||||
Rationale
|
||||
---------
|
||||
JUCE should be able to handle all popular frame rate codes but was
|
||||
missing support for 23.976.
|
||||
JUCE should be able to handle all popular frame rate codes but was missing
|
||||
support for 23.976.
|
||||
|
||||
|
||||
Change
|
||||
|
|
@ -120,7 +150,7 @@ or
|
|||
|
||||
2. Override the Look&Feel method
|
||||
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
|
||||
|
||||
Rationale
|
||||
|
|
|
|||
|
|
@ -1319,12 +1319,14 @@ namespace AAXClasses
|
|||
|
||||
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)
|
||||
: String (parameterIndex));
|
||||
|
||||
AAX_CString paramName (audioProcessor.getParameterName (parameterIndex, 31).toRawUTF8());
|
||||
AAX_CString paramName (processorParam->getName (31).toRawUTF8());
|
||||
AAX_CParamID paramID = aaxParamIDs.getReference (parameterIndex).getCharPointer();
|
||||
|
||||
paramMap.set (AAXClasses::getAAXParamHash (paramID), parameterIndex);
|
||||
|
|
@ -1339,19 +1341,25 @@ namespace AAXClasses
|
|||
AAX_IParameter* parameter
|
||||
= new AAX_CParameter<float> (paramID,
|
||||
paramName,
|
||||
audioProcessor.getParameterDefaultValue (parameterIndex),
|
||||
processorParam->getDefaultValue(),
|
||||
AAX_CLinearTaperDelegate<float, 0>(),
|
||||
AAX_CNumberDisplayDelegate<float, 3>(),
|
||||
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);
|
||||
|
||||
#if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
|
||||
parameter->SetType (parameterNumSteps > 1000 ? AAX_eParameterType_Continuous
|
||||
: 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_RotarySingleDotMode | AAX_eParameterOrientation_RotaryRightMinLeftMax)
|
||||
: (AAX_eParameterOrientation_LeftMinRightMax | AAX_eParameterOrientation_BottomMinTopMax
|
||||
|
|
|
|||
|
|
@ -525,7 +525,7 @@ public:
|
|||
const String text (String::fromCFString (pv->inString));
|
||||
|
||||
if (AudioProcessorParameter* param = juceFilter->getParameters() [paramID])
|
||||
pv->outValue = param->getValueForText (text);
|
||||
pv->outValue = param->getValueForText (text) * getMaximumParameterValue (param);
|
||||
else
|
||||
pv->outValue = text.getFloatValue();
|
||||
|
||||
|
|
@ -546,11 +546,12 @@ public:
|
|||
String text;
|
||||
|
||||
if (AudioProcessorParameter* param = juceFilter->getParameters() [paramID])
|
||||
text = param->getText (value, 0);
|
||||
text = param->getText (value / getMaximumParameterValue (param), 0);
|
||||
else
|
||||
text = String (value);
|
||||
|
||||
pv->outString = text.toCFString();
|
||||
|
||||
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,
|
||||
AudioUnitParameterID inParameterID,
|
||||
AudioUnitParameterInfo& outParameterInfo) override
|
||||
|
|
@ -811,7 +823,7 @@ public:
|
|||
|
||||
if (inScope == kAudioUnitScope_Global
|
||||
&& juceFilter != nullptr
|
||||
&& index < juceFilter->getNumParameters())
|
||||
&& isPositiveAndBelow (index, juceFilter->getNumParameters()))
|
||||
{
|
||||
outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
|
||||
outParameterInfo.flags = (UInt32) (kAudioUnitParameterFlag_IsWritable
|
||||
|
|
@ -823,28 +835,40 @@ public:
|
|||
outParameterInfo.flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution;
|
||||
#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)
|
||||
if (name.isEmpty() || ! juceFilter->isParameterAutomatable (index))
|
||||
const String name (param->getName (512));
|
||||
|
||||
// Set whether the param is automatable (unnamed parameters aren't allowed to be automated)
|
||||
if (name.isEmpty() || ! param->isAutomatable())
|
||||
outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime;
|
||||
|
||||
if (juceFilter->isMetaParameter (index))
|
||||
if (! param->isDiscrete())
|
||||
outParameterInfo.flags |= kAudioUnitParameterFlag_CanRamp;
|
||||
|
||||
if (param->isMetaParameter())
|
||||
outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;
|
||||
|
||||
// is this a meter?
|
||||
if (((juceFilter->getParameterCategory (index) & 0xffff0000) >> 16) == 2)
|
||||
// Is this a meter?
|
||||
if (((param->getCategory() & 0xffff0000) >> 16) == 2)
|
||||
{
|
||||
outParameterInfo.flags &= ~kAudioUnitParameterFlag_IsWritable;
|
||||
outParameterInfo.flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic;
|
||||
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);
|
||||
|
||||
outParameterInfo.minValue = 0.0f;
|
||||
outParameterInfo.maxValue = 1.0f;
|
||||
outParameterInfo.defaultValue = juceFilter->getParameterDefaultValue (index);
|
||||
outParameterInfo.maxValue = getMaximumParameterValue (param);
|
||||
outParameterInfo.defaultValue = param->getDefaultValue();
|
||||
jassert (outParameterInfo.defaultValue >= outParameterInfo.minValue
|
||||
&& outParameterInfo.defaultValue <= outParameterInfo.maxValue);
|
||||
|
||||
|
|
@ -861,9 +885,9 @@ public:
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -878,9 +902,9 @@ public:
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1727,6 +1751,14 @@ private:
|
|||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
info.stepCount = (Steinberg::int32) (numSteps > 0 && numSteps < 0x7fffffff ? numSteps - 1 : 0);
|
||||
info.defaultNormalizedValue = p.getParameterDefaultValue (index);
|
||||
auto* param = p.getParameters().getUnchecked (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);
|
||||
info.unitId = Vst::kRootUnitId;
|
||||
|
||||
// is this a meter?
|
||||
if (((p.getParameterCategory (index) & 0xffff0000) >> 16) == 2)
|
||||
// Is this a meter?
|
||||
if (((param->getCategory() & 0xffff0000) >> 16) == 2)
|
||||
info.flags = Vst::ParameterInfo::kIsReadOnly;
|
||||
else
|
||||
info.flags = p.isParameterAutomatable (index) ? Vst::ParameterInfo::kCanAutomate : 0;
|
||||
info.flags = param->isAutomatable() ? Vst::ParameterInfo::kCanAutomate : 0;
|
||||
|
||||
valueNormalized = info.defaultNormalizedValue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,15 +65,28 @@
|
|||
#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 0
|
||||
#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
|
||||
|
||||
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.
|
||||
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
|
||||
incompatible to old automation data.
|
||||
*/
|
||||
*/
|
||||
#ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS
|
||||
#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -986,6 +986,22 @@ public:
|
|||
|
||||
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
|
||||
{
|
||||
if (auto* p = parameters[index])
|
||||
|
|
@ -1178,6 +1194,8 @@ public:
|
|||
param->minValue = info.minValue;
|
||||
param->maxValue = info.maxValue;
|
||||
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)
|
||||
{
|
||||
|
|
@ -1263,7 +1281,8 @@ private:
|
|||
UInt32 paramID;
|
||||
String name;
|
||||
AudioUnitParameterValue minValue, maxValue;
|
||||
bool automatable;
|
||||
bool automatable, discrete;
|
||||
int numSteps;
|
||||
};
|
||||
|
||||
OwnedArray<ParamInfo> parameters;
|
||||
|
|
|
|||
|
|
@ -2247,17 +2247,6 @@ struct VST3PluginInstance : public AudioPluginInstance
|
|||
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
|
||||
{
|
||||
if (editController != nullptr)
|
||||
|
|
@ -2273,6 +2262,41 @@ struct VST3PluginInstance : public AudioPluginInstance
|
|||
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
|
||||
{
|
||||
if (editController != nullptr)
|
||||
|
|
|
|||
|
|
@ -56,6 +56,18 @@
|
|||
#include <juce_gui_basics/juce_gui_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
|
||||
|
|
|
|||
|
|
@ -625,6 +625,14 @@ int AudioProcessor::getDefaultNumParameterSteps() noexcept
|
|||
return 0x7fffffff;
|
||||
}
|
||||
|
||||
bool AudioProcessor::isParameterDiscrete (int index) const
|
||||
{
|
||||
if (auto* p = managedParameters[index])
|
||||
return p->isDiscrete();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String AudioProcessor::getParameterLabel (int index) const
|
||||
{
|
||||
if (auto* p = managedParameters[index])
|
||||
|
|
@ -1401,11 +1409,12 @@ void AudioProcessorParameter::endChangeGesture()
|
|||
processor->endParameterChangeGesture (parameterIndex);
|
||||
}
|
||||
|
||||
bool AudioProcessorParameter::isOrientationInverted() const { return false; }
|
||||
bool AudioProcessorParameter::isAutomatable() const { return true; }
|
||||
bool AudioProcessorParameter::isMetaParameter() const { return false; }
|
||||
AudioProcessorParameter::Category AudioProcessorParameter::getCategory() const { return genericParameter; }
|
||||
int AudioProcessorParameter::getNumSteps() const { return AudioProcessor::getDefaultNumParameterSteps(); }
|
||||
bool AudioProcessorParameter::isOrientationInverted() const { return false; }
|
||||
bool AudioProcessorParameter::isAutomatable() const { return true; }
|
||||
bool AudioProcessorParameter::isMetaParameter() const { return false; }
|
||||
AudioProcessorParameter::Category AudioProcessorParameter::getCategory() const { return genericParameter; }
|
||||
int AudioProcessorParameter::getNumSteps() const { return AudioProcessor::getDefaultNumParameterSteps(); }
|
||||
bool AudioProcessorParameter::isDiscrete() const { return false; }
|
||||
|
||||
String AudioProcessorParameter::getText (float value, int /*maximumStringLength*/) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1020,13 +1020,22 @@ public:
|
|||
virtual String getParameterText (int parameterIndex, int maximumStringLength);
|
||||
|
||||
/** Returns the number of discrete steps that this parameter can represent.
|
||||
|
||||
The default return value if you don't implement this method is
|
||||
AudioProcessor::getDefaultNumParameterSteps().
|
||||
|
||||
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.
|
||||
|
||||
NOTE! This method will eventually be deprecated! It's recommended that you use
|
||||
AudioProcessorParameter::getNumSteps() instead.
|
||||
|
||||
@see isParameterDiscrete
|
||||
*/
|
||||
virtual int getParameterNumSteps (int parameterIndex);
|
||||
|
||||
|
|
@ -1034,10 +1043,26 @@ public:
|
|||
|
||||
NOTE! This method will eventually be deprecated! It's recommended that you use
|
||||
AudioProcessorParameter::getNumSteps() instead.
|
||||
|
||||
@see getParameterNumSteps
|
||||
*/
|
||||
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.
|
||||
By default, this just returns 0.
|
||||
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;
|
||||
|
||||
/** Returns the number of discrete interval steps that this parameter's range
|
||||
should be quantised into.
|
||||
/** Returns the number of steps that this parameter's range should be quantised into.
|
||||
|
||||
If you want a continuous range of values, don't override this method, and allow
|
||||
the default implementation to return AudioProcessor::getDefaultNumParameterSteps().
|
||||
|
||||
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;
|
||||
|
||||
/** 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.
|
||||
The default implementation just returns the floating point value
|
||||
as a string, but this could do anything you need for a custom type
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ private:
|
|||
void setValue (float newValue) override;
|
||||
float getDefaultValue() const override;
|
||||
int getNumSteps() const override;
|
||||
bool isDiscrete() const override;
|
||||
String getText (float, int) const override;
|
||||
float getValueForText (const String&) const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ private:
|
|||
void setValue (float newValue) override;
|
||||
float getDefaultValue() const override;
|
||||
int getNumSteps() const override;
|
||||
bool isDiscrete() const override;
|
||||
String getText (float, int) const override;
|
||||
float getValueForText (const String&) const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ float AudioParameterBool::getValue() const { retur
|
|||
void AudioParameterBool::setValue (float newValue) { value = newValue; }
|
||||
float AudioParameterBool::getDefaultValue() const { return defaultValue; }
|
||||
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; }
|
||||
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); }
|
||||
float AudioParameterChoice::getDefaultValue() const { return defaultValue; }
|
||||
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)); }
|
||||
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<float (const String&)> textToValue,
|
||||
bool meta,
|
||||
bool automatable)
|
||||
bool automatable,
|
||||
bool discrete)
|
||||
: AudioProcessorParameterWithID (parameterID, paramName, labelText),
|
||||
owner (s), valueToTextFunction (valueToText), textToValueFunction (textToValue),
|
||||
range (r), value (defaultVal), defaultValue (defaultVal),
|
||||
listenersNeedCalling (true),
|
||||
isMetaParam (meta),
|
||||
isAutomatableParam (automatable)
|
||||
isAutomatableParam (automatable),
|
||||
isDiscreteParam (discrete)
|
||||
{
|
||||
state.addListener (this);
|
||||
needsUpdate.set (1);
|
||||
|
|
@ -150,6 +152,7 @@ struct AudioProcessorValueTreeState::Parameter : public AudioProcessorParamete
|
|||
|
||||
bool isMetaParameter() const override { return isMetaParam; }
|
||||
bool isAutomatable() const override { return isAutomatableParam; }
|
||||
bool isDiscrete() const override { return isDiscreteParam; }
|
||||
|
||||
AudioProcessorValueTreeState& owner;
|
||||
ValueTree state;
|
||||
|
|
@ -160,7 +163,7 @@ struct AudioProcessorValueTreeState::Parameter : public AudioProcessorParamete
|
|||
float value, defaultValue;
|
||||
Atomic<int> needsUpdate;
|
||||
bool listenersNeedCalling;
|
||||
const bool isMetaParam, isAutomatableParam;
|
||||
const bool isMetaParam, isAutomatableParam, isDiscreteParam;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Parameter)
|
||||
};
|
||||
|
|
@ -185,7 +188,8 @@ AudioProcessorParameterWithID* AudioProcessorValueTreeState::createAndAddParamet
|
|||
float defaultVal, std::function<String (float)> valueToTextFunction,
|
||||
std::function<float (const String&)> textToValueFunction,
|
||||
bool isMetaParameter,
|
||||
bool isAutomatableParameter)
|
||||
bool isAutomatableParameter,
|
||||
bool isDiscreteParameter)
|
||||
{
|
||||
// All parameters must be created before giving this manager a ValueTree state!
|
||||
jassert (! state.isValid());
|
||||
|
|
@ -195,7 +199,8 @@ AudioProcessorParameterWithID* AudioProcessorValueTreeState::createAndAddParamet
|
|||
|
||||
Parameter* p = new Parameter (*this, paramID, paramName, labelText, r,
|
||||
defaultVal, valueToTextFunction, textToValueFunction,
|
||||
isMetaParameter, isAutomatableParameter);
|
||||
isMetaParameter, isAutomatableParameter,
|
||||
isDiscreteParameter);
|
||||
processor.addParameter (p);
|
||||
return p;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ public:
|
|||
@param textToValueFunction The inverse of valueToTextFunction
|
||||
@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 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
|
||||
*/
|
||||
|
|
@ -88,7 +90,8 @@ public:
|
|||
std::function<String (float)> valueToTextFunction,
|
||||
std::function<float (const String&)> textToValueFunction,
|
||||
bool isMetaParameter = false,
|
||||
bool isAutomatableParameter = true);
|
||||
bool isAutomatableParameter = true,
|
||||
bool isDiscrete = false);
|
||||
|
||||
/** Returns a parameter by its ID string. */
|
||||
AudioProcessorParameterWithID* getParameter (StringRef parameterID) const noexcept;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue