1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Added support for level meter parameter categories

This commit is contained in:
hogliux 2017-01-11 18:02:40 +00:00
parent 2a983067ab
commit 7897331403
13 changed files with 208 additions and 37 deletions

View file

@ -43,6 +43,10 @@ public:
{
if (const AudioParameterFloat* param = dynamic_cast<AudioParameterFloat*>(params[i]))
{
const bool isLevelMeter = (((param->category & 0xffff0000) >> 16) == 2);
if (isLevelMeter)
continue;
Slider* aSlider;
paramSliders.add (aSlider = new Slider (param->name));

View file

@ -299,29 +299,33 @@ namespace AAXClasses
PluginInstanceInfo* pluginInstance;
int32_t* isPrepared;
int32_t* sideChainBuffers;
float* const* meterTapBuffers;
};
struct JUCEAlgorithmIDs
{
enum
{
inputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, inputChannels),
outputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, outputChannels),
bufferSize = AAX_FIELD_INDEX (JUCEAlgorithmContext, bufferSize),
bypass = AAX_FIELD_INDEX (JUCEAlgorithmContext, bypass),
inputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, inputChannels),
outputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, outputChannels),
bufferSize = AAX_FIELD_INDEX (JUCEAlgorithmContext, bufferSize),
bypass = AAX_FIELD_INDEX (JUCEAlgorithmContext, bypass),
#if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
midiNodeIn = AAX_FIELD_INDEX (JUCEAlgorithmContext, midiNodeIn),
midiNodeIn = AAX_FIELD_INDEX (JUCEAlgorithmContext, midiNodeIn),
#endif
#if JucePlugin_ProducesMidiOutput || JucePlugin_IsSynth || JucePlugin_IsMidiEffect
midiNodeOut = AAX_FIELD_INDEX (JUCEAlgorithmContext, midiNodeOut),
midiNodeOut = AAX_FIELD_INDEX (JUCEAlgorithmContext, midiNodeOut),
#endif
pluginInstance = AAX_FIELD_INDEX (JUCEAlgorithmContext, pluginInstance),
preparedFlag = AAX_FIELD_INDEX (JUCEAlgorithmContext, isPrepared),
pluginInstance = AAX_FIELD_INDEX (JUCEAlgorithmContext, pluginInstance),
preparedFlag = AAX_FIELD_INDEX (JUCEAlgorithmContext, isPrepared),
sideChainBuffers = AAX_FIELD_INDEX (JUCEAlgorithmContext, sideChainBuffers)
sideChainBuffers = AAX_FIELD_INDEX (JUCEAlgorithmContext, sideChainBuffers),
meterTapBuffers = AAX_FIELD_INDEX (JUCEAlgorithmContext, meterTapBuffers)
};
};
@ -655,7 +659,8 @@ namespace AAXClasses
const int numParameters = pluginInstance->getNumParameters();
for (int i = 0; i < numParameters; ++i)
SetParameterNormalizedValue (getAAXParamIDFromJuceIndex (i), (double) pluginInstance->getParameter(i));
if (AAX_CParamID paramID = getAAXParamIDFromJuceIndex(i))
SetParameterNormalizedValue (paramID, (double) pluginInstance->getParameter(i));
return AAX_SUCCESS;
}
@ -893,7 +898,8 @@ namespace AAXClasses
void audioProcessorParameterChanged (AudioProcessor* /*processor*/, int parameterIndex, float newValue) override
{
SetParameterNormalizedValue (getAAXParamIDFromJuceIndex (parameterIndex), (double) newValue);
if (AAX_CParamID paramID = getAAXParamIDFromJuceIndex (parameterIndex))
SetParameterNormalizedValue (paramID, (double) newValue);
}
void audioProcessorChanged (AudioProcessor* processor) override
@ -904,12 +910,14 @@ namespace AAXClasses
void audioProcessorParameterChangeGestureBegin (AudioProcessor* /*processor*/, int parameterIndex) override
{
TouchParameter (getAAXParamIDFromJuceIndex (parameterIndex));
if (AAX_CParamID paramID = getAAXParamIDFromJuceIndex (parameterIndex))
TouchParameter (paramID);
}
void audioProcessorParameterChangeGestureEnd (AudioProcessor* /*processor*/, int parameterIndex) override
{
ReleaseParameter (getAAXParamIDFromJuceIndex (parameterIndex));
if (AAX_CParamID paramID = getAAXParamIDFromJuceIndex (parameterIndex))
ReleaseParameter (paramID);
}
AAX_Result NotificationReceived (AAX_CTypeID type, const void* data, uint32_t size) override
@ -932,15 +940,20 @@ namespace AAXClasses
void process (const float* const* inputs, float* const* outputs, const int sideChainBufferIdx,
const int bufferSize, const bool bypass,
AAX_IMIDINode* midiNodeIn, AAX_IMIDINode* midiNodesOut)
AAX_IMIDINode* midiNodeIn, AAX_IMIDINode* midiNodesOut,
float* const meterBuffers)
{
const int numIns = pluginInstance->getTotalNumInputChannels();
const int numOuts = pluginInstance->getTotalNumOutputChannels();
const int numIns = pluginInstance->getTotalNumInputChannels();
const int numOuts = pluginInstance->getTotalNumOutputChannels();
const int numMeters = aaxMeters.size();
if (pluginInstance->isSuspended())
{
for (int i = 0; i < numOuts; ++i)
FloatVectorOperations::clear (outputs[i], bufferSize);
if (meterBuffers != nullptr)
FloatVectorOperations::clear (meterBuffers, numMeters);
}
else
{
@ -981,6 +994,12 @@ namespace AAXClasses
process (channels, numIns, bufferSize, bypass, midiNodeIn, midiNodesOut);
}
if (meterBuffers != nullptr)
{
for (int i = 0; i < numMeters; ++i)
meterBuffers[i] = pluginInstance->getParameter (aaxMeters[i]);
}
}
}
@ -1253,6 +1272,8 @@ namespace AAXClasses
for (int parameterIndex = 0; parameterIndex < numParameters; ++parameterIndex)
{
const AudioProcessorParameter::Category category = audioProcessor.getParameterCategory (parameterIndex);
aaxParamIDs.add (usingManagedParameters ? audioProcessor.getParameterID (parameterIndex)
: String (parameterIndex));
@ -1261,6 +1282,13 @@ namespace AAXClasses
paramMap.set (AAXClasses::getAAXParamHash (paramID), parameterIndex);
// is this a meter?
if (((category & 0xffff0000) >> 16) == 2)
{
aaxMeters.add (parameterIndex);
continue;
}
AAX_IParameter* parameter
= new AAX_CParameter<float> (paramID,
paramName,
@ -1441,9 +1469,12 @@ namespace AAXClasses
if (sideChainBufferIdx <= 0)
sideChainBufferIdx = -1;
float* const meterTapBuffers = (i.meterTapBuffers != nullptr ? *i.meterTapBuffers : nullptr);
i.pluginInstance->parameters.process (i.inputChannels, i.outputChannels, sideChainBufferIdx,
*(i.bufferSize), *(i.bypass) != 0,
getMidiNodeIn(i), getMidiNodeOut(i));
getMidiNodeIn(i), getMidiNodeOut(i),
meterTapBuffers);
}
}
@ -1511,6 +1542,8 @@ namespace AAXClasses
Array<String> aaxParamIDs;
HashMap<int32, int> paramMap;
Array<int> aaxMeters;
struct ChunkMemoryBlock : public ReferenceCountedObject
{
juce::MemoryBlock data;
@ -1547,7 +1580,7 @@ namespace AAXClasses
if (const JuceAAX_Processor* params = dynamic_cast<const JuceAAX_Processor*> (GetEffectParameters()))
return params->getParamIndexFromID (paramID);
return -1;
return -1;
}
AAX_CParamID JuceAAX_GUI::getAAXParamIDFromJuceIndex (int index) const noexcept
@ -1555,7 +1588,7 @@ namespace AAXClasses
if (const JuceAAX_Processor* params = dynamic_cast<const JuceAAX_Processor*> (GetEffectParameters()))
return params->getAAXParamIDFromJuceIndex (index);
return nullptr;
return nullptr;
}
//==============================================================================
@ -1578,7 +1611,57 @@ namespace AAXClasses
};
//==============================================================================
static void createDescriptor (AAX_IComponentDescriptor& desc, int configIndex, const AudioProcessor::BusesLayout& fullLayout, AudioProcessor& processor)
static int addAAXMeters (AudioProcessor& p, AAX_IEffectDescriptor& descriptor)
{
const int n = p.getNumParameters();
int meterIdx = 0;
for (int i = 0; i < n; ++i)
{
const AudioProcessorParameter::Category category = p.getParameterCategory (i);
// is this a meter?
if (((category & 0xffff0000) >> 16) == 2)
{
if (AAX_IPropertyMap* meterProperties = descriptor.NewPropertyMap())
{
AAX_EMeterType aaxMeterType;
switch (category)
{
case AudioProcessorParameter::inputMeter:
aaxMeterType = AAX_eMeterType_Input;
break;
case AudioProcessorParameter::outputMeter:
aaxMeterType = AAX_eMeterType_Output;
break;
case AudioProcessorParameter::compressorLimiterGainReductionMeter:
aaxMeterType = AAX_eMeterType_CLGain;
break;
case AudioProcessorParameter::expanderGateGainReductionMeter:
aaxMeterType = AAX_eMeterType_EGGain;
break;
case AudioProcessorParameter::analysisMeter:
aaxMeterType = AAX_eMeterType_Analysis;
break;
default:
aaxMeterType = AAX_eMeterType_Other;
}
meterProperties->AddProperty (AAX_eProperty_Meter_Type, aaxMeterType);
meterProperties->AddProperty (AAX_eProperty_Meter_Orientation, AAX_eMeterOrientation_TopRight);
descriptor.AddMeterDescription ('Metr' + static_cast<AAX_CTypeID> (meterIdx++),
p.getParameterName (i).toRawUTF8(), meterProperties);
}
}
}
return meterIdx;
}
static void createDescriptor (AAX_IComponentDescriptor& desc, int configIndex,
const AudioProcessor::BusesLayout& fullLayout, AudioProcessor& processor,
const int numMeters)
{
AAX_EStemFormat aaxInputFormat = getFormatForAudioChannelSet (fullLayout.getMainInputChannelSet(), false);
AAX_EStemFormat aaxOutputFormat = getFormatForAudioChannelSet (fullLayout.getMainOutputChannelSet(), false);
@ -1611,6 +1694,13 @@ namespace AAXClasses
check (desc.AddPrivateData (JUCEAlgorithmIDs::pluginInstance, sizeof (PluginInstanceInfo)));
check (desc.AddPrivateData (JUCEAlgorithmIDs::preparedFlag, sizeof (int32_t)));
HeapBlock<AAX_CTypeID> meterIDs (static_cast<size_t> (numMeters));
for (int i = 0; i < numMeters; ++i)
meterIDs[i] = 'Metr' + static_cast<AAX_CTypeID> (i);
check (desc.AddMeters (JUCEAlgorithmIDs::meterTapBuffers, meterIDs.getData(), static_cast<uint32_t> (numMeters)));
// Create a property map
AAX_IPropertyMap* const properties = desc.NewPropertyMap();
jassert (properties != nullptr);
@ -1657,7 +1747,6 @@ namespace AAXClasses
const int maxAuxBuses = jmax (0, jmin (15, fullLayout.outputBuses.size() - 1));
// add the output buses
// This is incrdibly dumb: the output bus format must be well defined
// for every main bus in/out format pair. This means that there cannot
@ -1690,6 +1779,8 @@ namespace AAXClasses
descriptor.AddName (JucePlugin_Name);
descriptor.AddCategory (JucePlugin_AAXCategory);
const int numMeters = addAAXMeters (*plugin, descriptor);
#ifdef JucePlugin_AAXPageTableFile
// optional page table setting - define this macro in your project if you want
// to set this value - see Avid documentation for details about its format.
@ -1705,7 +1796,7 @@ namespace AAXClasses
if (AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor())
{
createDescriptor (*desc, 0, plugin->getBusesLayout(), *plugin);
createDescriptor (*desc, 0, plugin->getBusesLayout(), *plugin, numMeters);
check (descriptor.AddComponent (desc));
}
@ -1731,7 +1822,7 @@ namespace AAXClasses
if (AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor())
{
createDescriptor (*desc, configIndex++, fullLayout, *plugin);
createDescriptor (*desc, configIndex++, fullLayout, *plugin, numMeters);
check (descriptor.AddComponent (desc));
}
}

View file

@ -800,6 +800,7 @@ public:
&& juceFilter != nullptr
&& index < juceFilter->getNumParameters())
{
outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
outParameterInfo.flags = (UInt32) (kAudioUnitParameterFlag_IsWritable
| kAudioUnitParameterFlag_IsReadable
| kAudioUnitParameterFlag_HasCFNameString
@ -818,6 +819,14 @@ public:
if (juceFilter->isMetaParameter (index))
outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;
// is this a meter?
if (((juceFilter->getParameterCategory (index) & 0xffff0000) >> 16) == 2)
{
outParameterInfo.flags &= ~kAudioUnitParameterFlag_IsWritable;
outParameterInfo.flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic;
outParameterInfo.unit = kAudioUnitParameterUnit_LinearGain;
}
MusicDeviceBase::FillInParameterName (outParameterInfo, name.toCFString(), true);
outParameterInfo.minValue = 0.0f;
@ -825,7 +834,6 @@ public:
outParameterInfo.defaultValue = juceFilter->getParameterDefaultValue (index);
jassert (outParameterInfo.defaultValue >= outParameterInfo.minValue
&& outParameterInfo.defaultValue <= outParameterInfo.maxValue);
outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
return noErr;
}

View file

@ -913,6 +913,7 @@ private:
const String identifier (idx);
const String name = processor.getParameterName (idx);
AudioUnitParameterUnit unit = kAudioUnitParameterUnit_Generic;
AudioUnitParameterOptions flags = (UInt32) (kAudioUnitParameterFlag_IsWritable
| kAudioUnitParameterFlag_IsReadable
| kAudioUnitParameterFlag_HasCFNameString
@ -929,6 +930,14 @@ private:
if (processor.isMetaParameter (idx))
flags |= kAudioUnitParameterFlag_IsGlobalMeta;
// is this a meter?
if (((processor.getParameterCategory (index) & 0xffff0000) >> 16) == 2)
{
flags &= ~kAudioUnitParameterFlag_IsWritable;
flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic;
unit = kAudioUnitParameterUnit_LinearGain;
}
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS
AUParameterAddress address = static_cast<AUParameterAddress> (idx);
#else

View file

@ -1750,7 +1750,11 @@ private:
pointer_sized_int handleIsParameterAutomatable (VstOpCodeArguments args)
{
return (filter != nullptr && filter->isParameterAutomatable (args.index)) ? 1 : 0;
if (filter == nullptr)
return 0;
const bool isMeter = (((filter->getParameterCategory (args.index) & 0xffff0000) >> 16) == 2);
return (filter->isParameterAutomatable (args.index) && (! isMeter) ? 1 : 0);
}
pointer_sized_int handleParameterValueForText (VstOpCodeArguments args)

View file

@ -221,7 +221,12 @@ public:
info.defaultNormalizedValue = p.getParameterDefaultValue (index);
jassert (info.defaultNormalizedValue >= 0 && info.defaultNormalizedValue <= 1.0f);
info.unitId = Vst::kRootUnitId;
info.flags = p.isParameterAutomatable (index) ? Vst::ParameterInfo::kCanAutomate : 0;
// is this a meter?
if (((p.getParameterCategory (index) & 0xffff0000) >> 16) == 2)
info.flags = Vst::ParameterInfo::kIsReadOnly;
else
info.flags = p.isParameterAutomatable (index) ? Vst::ParameterInfo::kCanAutomate : 0;
}
virtual ~Param() {}

View file

@ -648,6 +648,14 @@ bool AudioProcessor::isMetaParameter (int index) const
return false;
}
AudioProcessorParameter::Category AudioProcessor::getParameterCategory (int index) const
{
if (AudioProcessorParameter* p = managedParameters[index])
return p->getCategory();
return AudioProcessorParameter::generic;
}
AudioProcessorParameter* AudioProcessor::getParamChecked (int index) const noexcept
{
AudioProcessorParameter* p = managedParameters[index];
@ -1301,9 +1309,10 @@ void AudioProcessorParameter::endChangeGesture()
processor->endParameterChangeGesture (parameterIndex);
}
bool AudioProcessorParameter::isOrientationInverted() const { return false; }
bool AudioProcessorParameter::isAutomatable() const { return true; }
bool AudioProcessorParameter::isMetaParameter() const { return false; }
bool AudioProcessorParameter::isOrientationInverted() const { return false; }
bool AudioProcessorParameter::isAutomatable() const { return true; }
bool AudioProcessorParameter::isMetaParameter() const { return false; }
AudioProcessorParameter::Category AudioProcessorParameter::getCategory() const { return generic; }
int AudioProcessorParameter::getNumSteps() const { return AudioProcessor::getDefaultNumParameterSteps(); }
String AudioProcessorParameter::getText (float value, int /*maximumStringLength*/) const

View file

@ -1099,6 +1099,14 @@ public:
*/
virtual bool isMetaParameter (int parameterIndex) const;
/** Should return the parameter's category.
By default, this returns the "generic" category.
NOTE! This method will eventually be deprecated! It's recommended that you use
AudioProcessorParameter::isMetaParameter() instead.
*/
virtual AudioProcessorParameter::Category getParameterCategory (int parameterIndex) const;
/** Sends a signal to the host to tell it that the user is about to start changing this
parameter.

View file

@ -143,6 +143,28 @@ public:
*/
virtual bool isMetaParameter() const;
enum Category
{
generic = (0 << 16) | 0, /** If your parameter is not a meter then you should use this category */
inputGain = (1 << 16) | 0, /** Currently not used */
outputGain = (1 << 16) | 1,
/** The following categories tell the host that this parameter is a meter level value
and therefore read-only. Most hosts will display these type of parameters as
a meter in the generic view of your plug-in. Pro-Tools will also show the meter
in the mixer view. */
inputMeter = (2 << 16) | 0,
outputMeter = (2 << 16) | 1,
compressorLimiterGainReductionMeter = (2 << 16) | 2,
expanderGateGainReductionMeter = (2 << 16) | 3,
analysisMeter = (2 << 16) | 4,
otherMeter = (2 << 16) | 5
};
/** Returns the parameter's category. */
virtual Category getCategory() const;
/** Returns the index of this parameter in its parent processor's parameter list. */
int getParameterIndex() const noexcept { return parameterIndex; }

View file

@ -83,12 +83,15 @@ private:
ParamSlider (AudioProcessor& p, int paramIndex) : owner (p), index (paramIndex)
{
const int steps = owner.getParameterNumSteps (index);
const AudioProcessorParameter::Category category = p.getParameterCategory (index);
const bool isLevelMeter = (((category & 0xffff0000) >> 16) == 2);
if (steps > 1 && steps < 0x7fffffff)
setRange (0.0, 1.0, 1.0 / (steps - 1.0));
else
setRange (0.0, 1.0);
setEnabled (! isLevelMeter);
setSliderStyle (Slider::LinearBar);
setTextBoxIsEditable (false);
setScrollWheelEnabled (true);

View file

@ -37,7 +37,8 @@ public:
AudioParameterFloat (const String& parameterID, const String& name,
NormalisableRange<float> normalisableRange,
float defaultValue,
const String& label = String());
const String& label = String(),
Category category = AudioProcessorParameter::generic);
/** Creates a AudioParameterFloat with an ID, name, and range.
On creation, its value is set to the default value.

View file

@ -35,7 +35,8 @@ public:
*/
AudioProcessorParameterWithID (const String& parameterID,
const String& name,
const String& label = String());
const String& label = String(),
Category category = AudioProcessorParameter::generic);
/** Destructor. */
~AudioProcessorParameterWithID();
@ -49,9 +50,13 @@ public:
/** Provides access to the parameter's label. */
const String label;
/** Provides access to the parameter's category. */
const Category category;
private:
String getName (int) const override;
String getLabel() const override;
Category getCategory() const override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameterWithID)
};

View file

@ -27,19 +27,21 @@
AudioProcessorParameterWithID::AudioProcessorParameterWithID (const String& idToUse,
const String& nameToUse,
const String& labelToUse)
: paramID (idToUse), name (nameToUse), label (labelToUse) {}
const String& labelToUse,
AudioProcessorParameter::Category categoryToUse)
: paramID (idToUse), name (nameToUse), label (labelToUse), category (categoryToUse) {}
AudioProcessorParameterWithID::~AudioProcessorParameterWithID() {}
String AudioProcessorParameterWithID::getName (int maximumStringLength) const { return name.substring (0, maximumStringLength); }
String AudioProcessorParameterWithID::getLabel() const { return label; }
String AudioProcessorParameterWithID::getName (int maximumStringLength) const { return name.substring (0, maximumStringLength); }
String AudioProcessorParameterWithID::getLabel() const { return label; }
AudioProcessorParameter::Category AudioProcessorParameterWithID::getCategory() const { return category; }
//==============================================================================
AudioParameterFloat::AudioParameterFloat (const String& idToUse, const String& nameToUse,
NormalisableRange<float> r, float def,
const String& labelToUse)
: AudioProcessorParameterWithID (idToUse, nameToUse, labelToUse),
const String& labelToUse, Category categoryToUse)
: AudioProcessorParameterWithID (idToUse, nameToUse, labelToUse, categoryToUse),
range (r), value (def), defaultValue (def)
{
}