mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Added host-side AudioProcessorParameter implementations, deprecated the old methods for managing parameters, and updated the GenericAudioProcessorEditor
This commit is contained in:
parent
40d0d74ce4
commit
611971181f
16 changed files with 2113 additions and 492 deletions
|
|
@ -6,36 +6,60 @@ Develop
|
|||
|
||||
Change
|
||||
------
|
||||
InAppPurchases class is now a JUCE Singleton. This means that you need
|
||||
to get an instance via InAppPurchases::getInstance(), instead of storing a
|
||||
InAppPurchases object yourself.
|
||||
When hosting plug-ins all AudioProcessor methods of managing parameters that
|
||||
take a parameter index as an argument have been deprecated.
|
||||
|
||||
Possible Issues
|
||||
---------------
|
||||
A single assertion will be fired in debug builds on the first use of a
|
||||
deprecated function.
|
||||
|
||||
Workaround
|
||||
----------
|
||||
When hosting plug-ins you should use the AudioProcessor::getParameters() method
|
||||
and interact with parameters via the returned array of
|
||||
AudioProcessorParameters. For a short-term fix you can also continue past the
|
||||
assertion in your debugger, or temporarily modify the JUCE source code to
|
||||
remove it.
|
||||
|
||||
Rationale
|
||||
---------
|
||||
Given the structure of JUCE's API it is impossible to deprecate these functions
|
||||
using only complile-time messages. Therefore a single assertion, which can be
|
||||
safely ignored, serves to indicate that these functions should no longer be
|
||||
used. The move away from the AudioProcessor methods both improves the interface
|
||||
to that class and makes ongoing development work much easier.
|
||||
|
||||
|
||||
Change
|
||||
------
|
||||
This InAppPurchases class is now a JUCE Singleton. This means that you need
|
||||
to get an instance via InAppPurchases::getInstance(), instead of storing a
|
||||
InAppPurchases object yourself.
|
||||
|
||||
Possible Issues
|
||||
---------------
|
||||
Any code using InAppPurchases needs to be updated to retrieve a singleton pointer
|
||||
to InAppPurchases.
|
||||
|
||||
|
||||
Workaround
|
||||
----------
|
||||
Instead of holding a InAppPurchase member yourself, you should get an instance
|
||||
Instead of holding a InAppPurchase member yourself, you should get an instance
|
||||
via InAppPurchases::getInstance(), e.g.
|
||||
|
||||
instead of:
|
||||
|
||||
InAppPurchases iap;
|
||||
iap.purchaseProduct (…);
|
||||
iap.purchaseProduct (...);
|
||||
|
||||
call:
|
||||
|
||||
InAppPurchases::getInstance()->purchaseProduct (…);
|
||||
|
||||
InAppPurchases::getInstance()->purchaseProduct (...);
|
||||
|
||||
Rationale
|
||||
---------
|
||||
This change was required to fix an issue on Android where on failed transaction
|
||||
a listener would not get called.
|
||||
a listener would not get called.
|
||||
|
||||
|
||||
Change
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ class AUv3SynthEditor : public AudioProcessorEditor,
|
|||
public:
|
||||
//==============================================================================
|
||||
AUv3SynthEditor (AudioProcessor& processor)
|
||||
: AudioProcessorEditor (processor),
|
||||
recordButton ("Record"),
|
||||
roomSizeSlider (Slider::LinearHorizontal, Slider::NoTextBox)
|
||||
: AudioProcessorEditor (processor),
|
||||
recordButton ("Record"),
|
||||
roomSizeSlider (Slider::LinearHorizontal, Slider::NoTextBox)
|
||||
{
|
||||
LookAndFeel::setDefaultLookAndFeel (&materialLookAndFeel);
|
||||
|
||||
|
|
|
|||
|
|
@ -1835,7 +1835,7 @@ private:
|
|||
// using the default number of steps.
|
||||
for (auto* param : juceFilter->getParameters())
|
||||
if (param->isDiscrete())
|
||||
jassert (param->getNumSteps() != juceFilter->getDefaultNumParameterSteps());
|
||||
jassert (param->getNumSteps() != AudioProcessor::getDefaultNumParameterSteps());
|
||||
#endif
|
||||
|
||||
parameterValueStringArrays.ensureStorageAllocated (numParams);
|
||||
|
|
|
|||
|
|
@ -1483,7 +1483,7 @@ private:
|
|||
return 0;
|
||||
}
|
||||
|
||||
void valueChangedForObserver(AUParameterAddress, AUValue)
|
||||
void valueChangedForObserver (AUParameterAddress, AUValue)
|
||||
{
|
||||
// this will have already been handled by valueChangedFromHost
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,6 +302,229 @@ class AudioUnitPluginWindowCocoa;
|
|||
class AudioUnitPluginInstance : public AudioPluginInstance
|
||||
{
|
||||
public:
|
||||
struct AUInstanceParameter final : public Parameter
|
||||
{
|
||||
AUInstanceParameter (AudioUnitPluginInstance& parent,
|
||||
UInt32 parameterID,
|
||||
const String& parameterName,
|
||||
AudioUnitParameterValue minParameterValue,
|
||||
AudioUnitParameterValue maxParameterValue,
|
||||
AudioUnitParameterValue defaultParameterValue,
|
||||
bool parameterIsAutomatable,
|
||||
bool parameterIsDiscrete,
|
||||
int numParameterSteps,
|
||||
bool isBoolean,
|
||||
const String& label,
|
||||
bool parameterValuesHaveStrings)
|
||||
: pluginInstance (parent),
|
||||
paramID (parameterID),
|
||||
name (parameterName),
|
||||
minValue (minParameterValue),
|
||||
maxValue (maxParameterValue),
|
||||
range (maxValue - minValue),
|
||||
automatable (parameterIsAutomatable),
|
||||
discrete (parameterIsDiscrete),
|
||||
numSteps (numParameterSteps),
|
||||
valuesHaveStrings (parameterValuesHaveStrings),
|
||||
isSwitch (isBoolean),
|
||||
valueLabel (label),
|
||||
defaultValue (normaliseParamValue (defaultParameterValue))
|
||||
{
|
||||
auValueStrings = Parameter::getAllValueStrings();
|
||||
}
|
||||
|
||||
virtual float getValue() const override
|
||||
{
|
||||
const ScopedLock sl (pluginInstance.lock);
|
||||
|
||||
AudioUnitParameterValue value = 0;
|
||||
|
||||
if (auto* au = pluginInstance.audioUnit)
|
||||
{
|
||||
AudioUnitGetParameter (au,
|
||||
paramID,
|
||||
kAudioUnitScope_Global,
|
||||
0,
|
||||
&value);
|
||||
|
||||
value = normaliseParamValue (value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
virtual void setValue (float newValue) override
|
||||
{
|
||||
const ScopedLock sl (pluginInstance.lock);
|
||||
|
||||
if (auto* au = pluginInstance.audioUnit)
|
||||
{
|
||||
AudioUnitSetParameter (au,
|
||||
paramID,
|
||||
kAudioUnitScope_Global,
|
||||
0,
|
||||
scaleParamValue (newValue),
|
||||
0);
|
||||
|
||||
sendParameterChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
float getDefaultValue() const override
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
String getName (int /*maximumStringLength*/) const override
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
String getLabel() const override
|
||||
{
|
||||
return valueLabel;
|
||||
}
|
||||
|
||||
String getText (float value, int maximumLength) const override
|
||||
{
|
||||
if (! auValueStrings.isEmpty())
|
||||
{
|
||||
auto index = roundToInt (jlimit (0.0f, 1.0f, value) * (auValueStrings.size() - 1));
|
||||
return auValueStrings[index];
|
||||
}
|
||||
|
||||
auto scaledValue = scaleParamValue (value);
|
||||
|
||||
if (valuesHaveStrings)
|
||||
{
|
||||
if (auto* au = pluginInstance.audioUnit)
|
||||
{
|
||||
AudioUnitParameterStringFromValue stringValue;
|
||||
stringValue.inParamID = paramID;
|
||||
stringValue.inValue = &scaledValue;
|
||||
stringValue.outString = nullptr;
|
||||
|
||||
UInt32 propertySize = sizeof (stringValue);
|
||||
|
||||
OSStatus err = AudioUnitGetProperty (au,
|
||||
kAudioUnitProperty_ParameterStringFromValue,
|
||||
kAudioUnitScope_Global,
|
||||
0,
|
||||
&stringValue,
|
||||
&propertySize);
|
||||
|
||||
if (! err && stringValue.outString != nullptr)
|
||||
return String::fromCFString (stringValue.outString).substring (0, maximumLength);
|
||||
}
|
||||
}
|
||||
|
||||
return Parameter::getText (scaledValue, maximumLength);
|
||||
}
|
||||
|
||||
float getValueForText (const String& text) const override
|
||||
{
|
||||
if (! auValueStrings.isEmpty())
|
||||
{
|
||||
auto index = auValueStrings.indexOf (text);
|
||||
|
||||
if (index != -1)
|
||||
return ((float) index) / (auValueStrings.size() - 1);
|
||||
}
|
||||
|
||||
if (valuesHaveStrings)
|
||||
{
|
||||
if (auto* au = pluginInstance.audioUnit)
|
||||
{
|
||||
AudioUnitParameterValueFromString valueString;
|
||||
valueString.inParamID = paramID;
|
||||
valueString.inString = text.toCFString();
|
||||
|
||||
UInt32 propertySize = sizeof (valueString);
|
||||
|
||||
OSStatus err = AudioUnitGetProperty (au,
|
||||
kAudioUnitProperty_ParameterValueFromString,
|
||||
kAudioUnitScope_Global,
|
||||
0,
|
||||
&valueString,
|
||||
&propertySize);
|
||||
|
||||
if (! err)
|
||||
return normaliseParamValue (valueString.outValue);
|
||||
}
|
||||
}
|
||||
|
||||
return Parameter::getValueForText (text);
|
||||
}
|
||||
|
||||
bool isAutomatable() const override
|
||||
{
|
||||
return automatable;
|
||||
}
|
||||
|
||||
bool isDiscrete() const override
|
||||
{
|
||||
return discrete;
|
||||
}
|
||||
|
||||
bool isBoolean() const override
|
||||
{
|
||||
return isSwitch;
|
||||
}
|
||||
|
||||
int getNumSteps() const override
|
||||
{
|
||||
return numSteps;
|
||||
}
|
||||
|
||||
StringArray getAllValueStrings() const override
|
||||
{
|
||||
return auValueStrings;
|
||||
}
|
||||
|
||||
void sendParameterChangeEvent()
|
||||
{
|
||||
#if JUCE_MAC
|
||||
jassert (pluginInstance.audioUnit != nullptr);
|
||||
|
||||
AudioUnitEvent ev;
|
||||
ev.mEventType = kAudioUnitEvent_ParameterValueChange;
|
||||
ev.mArgument.mParameter.mAudioUnit = pluginInstance.audioUnit;
|
||||
ev.mArgument.mParameter.mParameterID = paramID;
|
||||
ev.mArgument.mParameter.mScope = kAudioUnitScope_Global;
|
||||
ev.mArgument.mParameter.mElement = 0;
|
||||
|
||||
AUEventListenerNotify (pluginInstance.eventListenerRef, nullptr, &ev);
|
||||
#endif
|
||||
}
|
||||
|
||||
float normaliseParamValue (float scaledValue) const noexcept
|
||||
{
|
||||
if (discrete)
|
||||
return scaledValue / (getNumSteps() - 1);
|
||||
|
||||
return (scaledValue - minValue) / range;
|
||||
}
|
||||
|
||||
float scaleParamValue (float normalisedValue) const noexcept
|
||||
{
|
||||
if (discrete)
|
||||
return normalisedValue * (getNumSteps() - 1);
|
||||
|
||||
return minValue + (range * normalisedValue);
|
||||
}
|
||||
|
||||
AudioUnitPluginInstance& pluginInstance;
|
||||
const UInt32 paramID;
|
||||
const String name;
|
||||
const AudioUnitParameterValue minValue, maxValue, range;
|
||||
const bool automatable, discrete;
|
||||
const int numSteps;
|
||||
const bool valuesHaveStrings, isSwitch;
|
||||
const String valueLabel;
|
||||
const AudioUnitParameterValue defaultValue;
|
||||
StringArray auValueStrings;
|
||||
};
|
||||
|
||||
AudioUnitPluginInstance (AudioComponentInstance au)
|
||||
: AudioPluginInstance (getBusesProperties (au)),
|
||||
auComponent (AudioComponentInstanceGetComponent (au)),
|
||||
|
|
@ -937,66 +1160,6 @@ public:
|
|||
bool isOutputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getTotalNumOutputChannels()); }
|
||||
|
||||
//==============================================================================
|
||||
int getNumParameters() override { return parameters.size(); }
|
||||
|
||||
float getParameter (int index) override
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
AudioUnitParameterValue value = 0;
|
||||
|
||||
if (audioUnit != nullptr)
|
||||
{
|
||||
if (const ParamInfo* p = parameters[index])
|
||||
{
|
||||
AudioUnitGetParameter (audioUnit,
|
||||
p->paramID,
|
||||
kAudioUnitScope_Global, 0,
|
||||
&value);
|
||||
|
||||
value = (value - p->minValue) / (p->maxValue - p->minValue);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void setParameter (int index, float newValue) override
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (audioUnit != nullptr)
|
||||
{
|
||||
if (const ParamInfo* p = parameters[index])
|
||||
{
|
||||
AudioUnitSetParameter (audioUnit, p->paramID, kAudioUnitScope_Global, 0,
|
||||
p->minValue + (p->maxValue - p->minValue) * newValue, 0);
|
||||
|
||||
sendParameterChangeEvent (index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendParameterChangeEvent (int index)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
jassert (audioUnit != nullptr);
|
||||
|
||||
const ParamInfo& p = *parameters.getUnchecked (index);
|
||||
|
||||
AudioUnitEvent ev;
|
||||
ev.mEventType = kAudioUnitEvent_ParameterValueChange;
|
||||
ev.mArgument.mParameter.mAudioUnit = audioUnit;
|
||||
ev.mArgument.mParameter.mParameterID = p.paramID;
|
||||
ev.mArgument.mParameter.mScope = kAudioUnitScope_Global;
|
||||
ev.mArgument.mParameter.mElement = 0;
|
||||
|
||||
AUEventListenerNotify (eventListenerRef, nullptr, &ev);
|
||||
#else
|
||||
ignoreUnused (index);
|
||||
#endif
|
||||
}
|
||||
|
||||
void sendAllParametersChangedEvents()
|
||||
{
|
||||
#if JUCE_MAC
|
||||
|
|
@ -1010,40 +1173,6 @@ public:
|
|||
#endif
|
||||
}
|
||||
|
||||
const String getParameterName (int index) override
|
||||
{
|
||||
if (auto* p = parameters[index])
|
||||
return p->name;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
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])
|
||||
return p->automatable;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int getNumPrograms() override
|
||||
{
|
||||
|
|
@ -1192,7 +1321,7 @@ public:
|
|||
|
||||
void refreshParameterList() override
|
||||
{
|
||||
parameters.clear();
|
||||
managedParameters.clear();
|
||||
paramIDToIndex.clear();
|
||||
|
||||
if (audioUnit != nullptr)
|
||||
|
|
@ -1221,27 +1350,60 @@ public:
|
|||
kAudioUnitScope_Global,
|
||||
ids[i], &info, &sz) == noErr)
|
||||
{
|
||||
ParamInfo* const param = new ParamInfo();
|
||||
parameters.add (param);
|
||||
param->paramID = ids[i];
|
||||
paramIDToIndex.getReference (ids[i]) = i;
|
||||
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();
|
||||
String paramName;
|
||||
|
||||
if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0)
|
||||
{
|
||||
param->name = String::fromCFString (info.cfNameString);
|
||||
paramName = String::fromCFString (info.cfNameString);
|
||||
|
||||
if ((info.flags & kAudioUnitParameterFlag_CFNameRelease) != 0)
|
||||
CFRelease (info.cfNameString);
|
||||
}
|
||||
else
|
||||
{
|
||||
param->name = String (info.name, sizeof (info.name));
|
||||
paramName = String (info.name, sizeof (info.name));
|
||||
}
|
||||
|
||||
bool isDiscrete = (info.unit == kAudioUnitParameterUnit_Indexed
|
||||
|| info.unit == kAudioUnitParameterUnit_Boolean);
|
||||
bool isBoolean = info.unit == kAudioUnitParameterUnit_Boolean;
|
||||
|
||||
String label;
|
||||
|
||||
switch (info.unit)
|
||||
{
|
||||
case kAudioUnitParameterUnit_Percent:
|
||||
label = "%";
|
||||
break;
|
||||
case kAudioUnitParameterUnit_Seconds:
|
||||
label = "s";
|
||||
break;
|
||||
case kAudioUnitParameterUnit_Hertz:
|
||||
label = "Hz";
|
||||
break;
|
||||
case kAudioUnitParameterUnit_Decibels:
|
||||
label = "dB";
|
||||
break;
|
||||
case kAudioUnitParameterUnit_Milliseconds:
|
||||
label = "ms";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
addParameter (new AUInstanceParameter (*this,
|
||||
ids[i],
|
||||
paramName,
|
||||
info.minValue,
|
||||
info.maxValue,
|
||||
info.defaultValue,
|
||||
(info.flags & kAudioUnitParameterFlag_NonRealTime) == 0,
|
||||
isDiscrete,
|
||||
isDiscrete ? (int) (info.maxValue + 1.0f) : AudioProcessor::getDefaultNumParameterSteps(),
|
||||
isBoolean,
|
||||
label,
|
||||
(info.flags & kAudioUnitParameterFlag_ValuesHaveStrings) != 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1310,16 +1472,6 @@ private:
|
|||
AUEventListenerRef eventListenerRef;
|
||||
#endif
|
||||
|
||||
struct ParamInfo
|
||||
{
|
||||
UInt32 paramID;
|
||||
String name;
|
||||
AudioUnitParameterValue minValue, maxValue;
|
||||
bool automatable, discrete;
|
||||
int numSteps;
|
||||
};
|
||||
|
||||
OwnedArray<ParamInfo> parameters;
|
||||
HashMap<uint32, size_t> paramIDToIndex;
|
||||
|
||||
MidiDataConcatenator midiConcatenator;
|
||||
|
|
@ -1360,22 +1512,25 @@ private:
|
|||
AUEventListenerCreate (eventListenerCallback, this, CFRunLoopGetMain(),
|
||||
kCFRunLoopDefaultMode, 0, 0, &eventListenerRef);
|
||||
|
||||
for (int i = 0; i < parameters.size(); ++i)
|
||||
for (auto* param : getParameters())
|
||||
{
|
||||
AudioUnitEvent event;
|
||||
event.mArgument.mParameter.mAudioUnit = audioUnit;
|
||||
event.mArgument.mParameter.mParameterID = parameters.getUnchecked(i)->paramID;
|
||||
event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
|
||||
event.mArgument.mParameter.mElement = 0;
|
||||
if (auto* auParam = dynamic_cast<AUInstanceParameter*> (param))
|
||||
{
|
||||
AudioUnitEvent event;
|
||||
event.mArgument.mParameter.mAudioUnit = audioUnit;
|
||||
event.mArgument.mParameter.mParameterID = auParam->paramID;
|
||||
event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
|
||||
event.mArgument.mParameter.mElement = 0;
|
||||
|
||||
event.mEventType = kAudioUnitEvent_ParameterValueChange;
|
||||
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
|
||||
event.mEventType = kAudioUnitEvent_ParameterValueChange;
|
||||
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
|
||||
|
||||
event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
|
||||
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
|
||||
event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
|
||||
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
|
||||
|
||||
event.mEventType = kAudioUnitEvent_EndParameterChangeGesture;
|
||||
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
|
||||
event.mEventType = kAudioUnitEvent_EndParameterChangeGesture;
|
||||
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
|
||||
}
|
||||
}
|
||||
|
||||
addPropertyChangeListener (kAudioUnitProperty_PresentPreset);
|
||||
|
|
@ -1413,25 +1568,36 @@ private:
|
|||
|
||||
paramIndex = static_cast<int> (paramIDToIndex [paramID]);
|
||||
|
||||
if (! isPositiveAndBelow (paramIndex, parameters.size()))
|
||||
if (! isPositiveAndBelow (paramIndex, getParameters().size()))
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.mEventType)
|
||||
{
|
||||
case kAudioUnitEvent_ParameterValueChange:
|
||||
if (auto* param = getParameters().getUnchecked (paramIndex))
|
||||
{
|
||||
auto& p = *parameters.getUnchecked (paramIndex);
|
||||
sendParamChangeMessageToListeners (paramIndex, (newValue - p.minValue) / (p.maxValue - p.minValue));
|
||||
jassert (dynamic_cast<AUInstanceParameter*> (param) != nullptr);
|
||||
auto* auparam = static_cast<AUInstanceParameter*> (param);
|
||||
param->sendValueChangedMessageToListeners (auparam->normaliseParamValue (newValue));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case kAudioUnitEvent_BeginParameterChangeGesture:
|
||||
beginParameterChangeGesture (paramIndex);
|
||||
if (auto* param = getParameters()[paramIndex])
|
||||
param->beginChangeGesture();
|
||||
else
|
||||
jassertfalse; // Invalid parameter index
|
||||
|
||||
break;
|
||||
|
||||
case kAudioUnitEvent_EndParameterChangeGesture:
|
||||
endParameterChangeGesture (paramIndex);
|
||||
if (auto* param = getParameters()[paramIndex])
|
||||
param->endChangeGesture();
|
||||
else
|
||||
jassertfalse; // Invalid parameter index
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -112,11 +112,173 @@ private:
|
|||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAModuleHandle)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class LADSPAPluginInstance : public AudioPluginInstance
|
||||
{
|
||||
public:
|
||||
struct LADSPAParameter final : public Parameter
|
||||
{
|
||||
struct ParameterValue
|
||||
{
|
||||
inline ParameterValue() noexcept : scaled (0), unscaled (0) {}
|
||||
inline ParameterValue (float s, float u) noexcept : scaled (s), unscaled (u) {}
|
||||
|
||||
float scaled, unscaled;
|
||||
};
|
||||
|
||||
LADSPAParameter (LADSPAPluginInstance& parent,
|
||||
int parameterID,
|
||||
const String& parameterName,
|
||||
bool parameterIsAutomatable)
|
||||
: pluginInstance (parent),
|
||||
paramID (parameterID),
|
||||
name (parameterName),
|
||||
automatable (parameterIsAutomatable)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
virtual float getValue() const override
|
||||
{
|
||||
if (pluginInstance.plugin != nullptr)
|
||||
{
|
||||
const ScopedLock sl (pluginInstance.lock);
|
||||
|
||||
return paramValue.unscaled;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
String getCurrentValueAsText() const override
|
||||
{
|
||||
if (auto* interface = pluginInstance.plugin)
|
||||
{
|
||||
const LADSPA_PortRangeHint& hint = interface->PortRangeHints[paramID];
|
||||
|
||||
if (LADSPA_IS_HINT_INTEGER (hint.HintDescriptor))
|
||||
return String ((int) paramValue.scaled);
|
||||
|
||||
return String (paramValue.scaled, 4);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual void setValue (float newValue) override
|
||||
{
|
||||
if (auto* interface = pluginInstance.plugin)
|
||||
{
|
||||
const ScopedLock sl (pluginInstance.lock);
|
||||
|
||||
if (paramValue.unscaled != newValue)
|
||||
paramValue = ParameterValue (getNewParamScaled (interface->PortRangeHints [paramID], newValue), newValue);
|
||||
}
|
||||
}
|
||||
|
||||
float getDefaultValue() const override
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
ParameterValue getDefaultParamValue() const
|
||||
{
|
||||
if (auto* interface = pluginInstance.plugin)
|
||||
{
|
||||
const LADSPA_PortRangeHint& hint = interface->PortRangeHints[paramID];
|
||||
const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor;
|
||||
|
||||
if (LADSPA_IS_HINT_HAS_DEFAULT (desc))
|
||||
{
|
||||
if (LADSPA_IS_HINT_DEFAULT_0 (desc)) return ParameterValue();
|
||||
if (LADSPA_IS_HINT_DEFAULT_1 (desc)) return ParameterValue (1.0f, 1.0f);
|
||||
if (LADSPA_IS_HINT_DEFAULT_100 (desc)) return ParameterValue (100.0f, 0.5f);
|
||||
if (LADSPA_IS_HINT_DEFAULT_440 (desc)) return ParameterValue (440.0f, 0.5f);
|
||||
|
||||
const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) pluginInstance.getSampleRate() : 1.0f;
|
||||
const float lower = hint.LowerBound * scale;
|
||||
const float upper = hint.UpperBound * scale;
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_DEFAULT_MINIMUM (desc)) return ParameterValue (lower, 0.0f);
|
||||
if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc) && LADSPA_IS_HINT_DEFAULT_MAXIMUM (desc)) return ParameterValue (upper, 1.0f);
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc))
|
||||
{
|
||||
const bool useLog = LADSPA_IS_HINT_LOGARITHMIC (desc);
|
||||
|
||||
if (LADSPA_IS_HINT_DEFAULT_LOW (desc)) return ParameterValue (scaledValue (lower, upper, 0.25f, useLog), 0.25f);
|
||||
if (LADSPA_IS_HINT_DEFAULT_MIDDLE (desc)) return ParameterValue (scaledValue (lower, upper, 0.50f, useLog), 0.50f);
|
||||
if (LADSPA_IS_HINT_DEFAULT_HIGH (desc)) return ParameterValue (scaledValue (lower, upper, 0.75f, useLog), 0.75f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ParameterValue();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
paramValue = getDefaultParamValue();
|
||||
defaultValue = paramValue.unscaled;
|
||||
}
|
||||
|
||||
String getName (int /*maximumStringLength*/) const override
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
String getLabel() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool isAutomatable() const override
|
||||
{
|
||||
return automatable;
|
||||
}
|
||||
|
||||
static float scaledValue (float low, float high, float alpha, bool useLog) noexcept
|
||||
{
|
||||
if (useLog && low > 0 && high > 0)
|
||||
return expf (logf (low) * (1.0f - alpha) + logf (high) * alpha);
|
||||
|
||||
return low + (high - low) * alpha;
|
||||
}
|
||||
|
||||
static float toIntIfNecessary (const LADSPA_PortRangeHintDescriptor& desc, float value)
|
||||
{
|
||||
return LADSPA_IS_HINT_INTEGER (desc) ? ((float) (int) value) : value;
|
||||
}
|
||||
|
||||
float getNewParamScaled (const LADSPA_PortRangeHint& hint, float newValue) const
|
||||
{
|
||||
const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor;
|
||||
|
||||
if (LADSPA_IS_HINT_TOGGLED (desc))
|
||||
return (newValue < 0.5f) ? 0.0f : 1.0f;
|
||||
|
||||
const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) pluginInstance.getSampleRate() : 1.0f;
|
||||
const float lower = hint.LowerBound * scale;
|
||||
const float upper = hint.UpperBound * scale;
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_BOUNDED_ABOVE (desc))
|
||||
return toIntIfNecessary (desc, scaledValue (lower, upper, newValue, LADSPA_IS_HINT_LOGARITHMIC (desc)));
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc)) return toIntIfNecessary (desc, newValue);
|
||||
if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc)) return toIntIfNecessary (desc, newValue * upper);
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
LADSPAPluginInstance& pluginInstance;
|
||||
const int paramID;
|
||||
const String name;
|
||||
const bool automatable;
|
||||
|
||||
ParameterValue paramValue;
|
||||
float defaultValue = 0;
|
||||
};
|
||||
|
||||
LADSPAPluginInstance (const LADSPAModuleHandle::Ptr& m)
|
||||
: module (m), plugin (nullptr), handle (nullptr),
|
||||
initialised (false), tempBuffer (1, 1)
|
||||
|
|
@ -180,14 +342,17 @@ public:
|
|||
|
||||
inputs.clear();
|
||||
outputs.clear();
|
||||
parameters.clear();
|
||||
managedParameters.clear();
|
||||
|
||||
for (unsigned int i = 0; i < plugin->PortCount; ++i)
|
||||
{
|
||||
const LADSPA_PortDescriptor portDesc = plugin->PortDescriptors[i];
|
||||
|
||||
if ((portDesc & LADSPA_PORT_CONTROL) != 0)
|
||||
parameters.add (i);
|
||||
addParameter (new LADSPAParameter (*this,
|
||||
i,
|
||||
String (plugin->PortNames[i]).trim(),
|
||||
(portDesc & LADSPA_PORT_INPUT) != 0));
|
||||
|
||||
if ((portDesc & LADSPA_PORT_AUDIO) != 0)
|
||||
{
|
||||
|
|
@ -196,10 +361,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
parameterValues.calloc (parameters.size());
|
||||
|
||||
for (int i = 0; i < parameters.size(); ++i)
|
||||
plugin->connect_port (handle, parameters[i], &(parameterValues[i].scaled));
|
||||
for (auto* param : getParameters())
|
||||
if (auto* ladspaParam = dynamic_cast<LADSPAParameter*> (param))
|
||||
plugin->connect_port (handle, ladspaParam->paramID, &(ladspaParam->paramValue.scaled));
|
||||
|
||||
setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize);
|
||||
|
||||
|
|
@ -266,11 +430,11 @@ public:
|
|||
tempBuffer.setSize (jmax (1, outputs.size()), samplesPerBlockExpected);
|
||||
|
||||
// dodgy hack to force some plugins to initialise the sample rate..
|
||||
if (getNumParameters() > 0)
|
||||
if (auto* firstParam = getParameters()[0])
|
||||
{
|
||||
const float old = getParameter (0);
|
||||
setParameter (0, (old < 0.5f) ? 1.0f : 0.0f);
|
||||
setParameter (0, old);
|
||||
const float old = firstParam->getValue();
|
||||
firstParam->setValue ((old < 0.5f) ? 1.0f : 0.0f);
|
||||
firstParam->setValue (old);
|
||||
}
|
||||
|
||||
if (plugin->activate != nullptr)
|
||||
|
|
@ -349,76 +513,15 @@ public:
|
|||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int getNumParameters() { return handle != nullptr ? parameters.size() : 0; }
|
||||
|
||||
bool isParameterAutomatable (int index) const
|
||||
{
|
||||
return plugin != nullptr
|
||||
&& (plugin->PortDescriptors [parameters[index]] & LADSPA_PORT_INPUT) != 0;
|
||||
}
|
||||
|
||||
float getParameter (int index)
|
||||
{
|
||||
if (plugin != nullptr && isPositiveAndBelow (index, parameters.size()))
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return parameterValues[index].unscaled;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void setParameter (int index, float newValue)
|
||||
{
|
||||
if (plugin != nullptr && isPositiveAndBelow (index, parameters.size()))
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
ParameterValue& p = parameterValues[index];
|
||||
|
||||
if (p.unscaled != newValue)
|
||||
p = ParameterValue (getNewParamScaled (plugin->PortRangeHints [parameters[index]], newValue), newValue);
|
||||
}
|
||||
}
|
||||
|
||||
const String getParameterName (int index)
|
||||
{
|
||||
if (plugin != nullptr)
|
||||
{
|
||||
jassert (isPositiveAndBelow (index, parameters.size()));
|
||||
return String (plugin->PortNames [parameters [index]]).trim();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const String getParameterText (int index)
|
||||
{
|
||||
if (plugin != nullptr)
|
||||
{
|
||||
jassert (index >= 0 && index < parameters.size());
|
||||
|
||||
const LADSPA_PortRangeHint& hint = plugin->PortRangeHints [parameters [index]];
|
||||
|
||||
if (LADSPA_IS_HINT_INTEGER (hint.HintDescriptor))
|
||||
return String ((int) parameterValues[index].scaled);
|
||||
|
||||
return String (parameterValues[index].scaled, 4);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int getNumPrograms() { return 0; }
|
||||
int getCurrentProgram() { return 0; }
|
||||
|
||||
void setCurrentProgram (int newIndex)
|
||||
void setCurrentProgram (int)
|
||||
{
|
||||
if (plugin != nullptr)
|
||||
for (int i = 0; i < parameters.size(); ++i)
|
||||
parameterValues[i] = getParamValue (plugin->PortRangeHints [parameters[i]]);
|
||||
for (auto* param : getParameters())
|
||||
if (auto* ladspaParam = dynamic_cast<LADSPAParameter*> (param))
|
||||
ladspaParam->reset();
|
||||
}
|
||||
|
||||
const String getProgramName (int index)
|
||||
|
|
@ -435,12 +538,15 @@ public:
|
|||
//==============================================================================
|
||||
void getStateInformation (MemoryBlock& destData)
|
||||
{
|
||||
destData.setSize (sizeof (float) * getNumParameters());
|
||||
auto numParameters = getParameters().size();
|
||||
destData.setSize (sizeof (float) * numParameters);
|
||||
destData.fillWith (0);
|
||||
|
||||
float* const p = (float*) ((char*) destData.getData());
|
||||
for (int i = 0; i < getNumParameters(); ++i)
|
||||
p[i] = getParameter(i);
|
||||
|
||||
for (int i = 0; i < numParameters; ++i)
|
||||
if (auto* param = getParameters()[i])
|
||||
p[i] = param->getValue();
|
||||
}
|
||||
|
||||
void getCurrentProgramStateInformation (MemoryBlock& destData)
|
||||
|
|
@ -452,8 +558,9 @@ public:
|
|||
{
|
||||
const float* p = static_cast<const float*> (data);
|
||||
|
||||
for (int i = 0; i < getNumParameters(); ++i)
|
||||
setParameter (i, p[i]);
|
||||
for (int i = 0; i < getParameters().size(); ++i)
|
||||
if (auto* param = getParameters()[i])
|
||||
param->setValue (p[i]);
|
||||
}
|
||||
|
||||
void setCurrentProgramStateInformation (const void* data, int sizeInBytes)
|
||||
|
|
@ -485,82 +592,7 @@ private:
|
|||
CriticalSection lock;
|
||||
bool initialised;
|
||||
AudioBuffer<float> tempBuffer;
|
||||
Array<int> inputs, outputs, parameters;
|
||||
|
||||
struct ParameterValue
|
||||
{
|
||||
inline ParameterValue() noexcept : scaled (0), unscaled (0) {}
|
||||
inline ParameterValue (float s, float u) noexcept : scaled (s), unscaled (u) {}
|
||||
|
||||
float scaled, unscaled;
|
||||
};
|
||||
|
||||
HeapBlock<ParameterValue> parameterValues;
|
||||
|
||||
//==============================================================================
|
||||
static float scaledValue (float low, float high, float alpha, bool useLog) noexcept
|
||||
{
|
||||
if (useLog && low > 0 && high > 0)
|
||||
return expf (logf (low) * (1.0f - alpha) + logf (high) * alpha);
|
||||
|
||||
return low + (high - low) * alpha;
|
||||
}
|
||||
|
||||
static float toIntIfNecessary (const LADSPA_PortRangeHintDescriptor& desc, float value)
|
||||
{
|
||||
return LADSPA_IS_HINT_INTEGER (desc) ? ((float) (int) value) : value;
|
||||
}
|
||||
|
||||
float getNewParamScaled (const LADSPA_PortRangeHint& hint, float newValue) const
|
||||
{
|
||||
const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor;
|
||||
|
||||
if (LADSPA_IS_HINT_TOGGLED (desc))
|
||||
return (newValue < 0.5f) ? 0.0f : 1.0f;
|
||||
|
||||
const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) getSampleRate() : 1.0f;
|
||||
const float lower = hint.LowerBound * scale;
|
||||
const float upper = hint.UpperBound * scale;
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_BOUNDED_ABOVE (desc))
|
||||
return toIntIfNecessary (desc, scaledValue (lower, upper, newValue, LADSPA_IS_HINT_LOGARITHMIC (desc)));
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc)) return toIntIfNecessary (desc, newValue);
|
||||
if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc)) return toIntIfNecessary (desc, newValue * upper);
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
ParameterValue getParamValue (const LADSPA_PortRangeHint& hint) const
|
||||
{
|
||||
const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor;
|
||||
|
||||
if (LADSPA_IS_HINT_HAS_DEFAULT (desc))
|
||||
{
|
||||
if (LADSPA_IS_HINT_DEFAULT_0 (desc)) return ParameterValue();
|
||||
if (LADSPA_IS_HINT_DEFAULT_1 (desc)) return ParameterValue (1.0f, 1.0f);
|
||||
if (LADSPA_IS_HINT_DEFAULT_100 (desc)) return ParameterValue (100.0f, 0.5f);
|
||||
if (LADSPA_IS_HINT_DEFAULT_440 (desc)) return ParameterValue (440.0f, 0.5f);
|
||||
|
||||
const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) getSampleRate() : 1.0f;
|
||||
const float lower = hint.LowerBound * scale;
|
||||
const float upper = hint.UpperBound * scale;
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_DEFAULT_MINIMUM (desc)) return ParameterValue (lower, 0.0f);
|
||||
if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc) && LADSPA_IS_HINT_DEFAULT_MAXIMUM (desc)) return ParameterValue (upper, 1.0f);
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW (desc))
|
||||
{
|
||||
const bool useLog = LADSPA_IS_HINT_LOGARITHMIC (desc);
|
||||
|
||||
if (LADSPA_IS_HINT_DEFAULT_LOW (desc)) return ParameterValue (scaledValue (lower, upper, 0.25f, useLog), 0.25f);
|
||||
if (LADSPA_IS_HINT_DEFAULT_MIDDLE (desc)) return ParameterValue (scaledValue (lower, upper, 0.50f, useLog), 0.50f);
|
||||
if (LADSPA_IS_HINT_DEFAULT_HIGH (desc)) return ParameterValue (scaledValue (lower, upper, 0.75f, useLog), 0.75f);
|
||||
}
|
||||
}
|
||||
|
||||
return ParameterValue();
|
||||
}
|
||||
Array<int> inputs, outputs;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAPluginInstance)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -283,7 +283,10 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
|
|||
if (index < 0)
|
||||
return kResultFalse;
|
||||
|
||||
plugin->beginParameterChangeGesture (index);
|
||||
if (auto* param = plugin->getParameters()[index])
|
||||
param->beginChangeGesture();
|
||||
else
|
||||
jassertfalse; // Invalid parameter index!
|
||||
}
|
||||
|
||||
return kResultTrue;
|
||||
|
|
@ -298,7 +301,10 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
|
|||
if (index < 0)
|
||||
return kResultFalse;
|
||||
|
||||
plugin->sendParamChangeMessageToListeners (index, (float) valueNormalized);
|
||||
if (auto* param = plugin->getParameters()[index])
|
||||
param->sendValueChangedMessageToListeners ((float) valueNormalized);
|
||||
else
|
||||
jassertfalse; // Invalid parameter index!
|
||||
|
||||
{
|
||||
Steinberg::int32 eventIndex;
|
||||
|
|
@ -322,7 +328,10 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
|
|||
if (index < 0)
|
||||
return kResultFalse;
|
||||
|
||||
plugin->endParameterChangeGesture (index);
|
||||
if (auto* param = plugin->getParameters()[index])
|
||||
param->endChangeGesture();
|
||||
else
|
||||
jassertfalse; // Invalid parameter index!
|
||||
}
|
||||
|
||||
return kResultTrue;
|
||||
|
|
@ -1685,6 +1694,118 @@ struct VST3ComponentHolder
|
|||
//==============================================================================
|
||||
struct VST3PluginInstance : public AudioPluginInstance
|
||||
{
|
||||
struct VST3Parameter final : public Parameter
|
||||
{
|
||||
VST3Parameter (VST3PluginInstance& parent,
|
||||
Steinberg::Vst::ParamID parameterID,
|
||||
const String& parameterName,
|
||||
const String& parameterLabel,
|
||||
Steinberg::Vst::ParamValue defaultParameterValue,
|
||||
bool parameterIsAutomatable,
|
||||
bool parameterIsDiscrete,
|
||||
int numParameterSteps)
|
||||
: pluginInstance (parent),
|
||||
paramID (parameterID),
|
||||
name (parameterName),
|
||||
label (parameterLabel),
|
||||
defaultValue (defaultParameterValue),
|
||||
automatable (parameterIsAutomatable),
|
||||
discrete (parameterIsDiscrete),
|
||||
numSteps (numParameterSteps)
|
||||
{
|
||||
}
|
||||
|
||||
virtual float getValue() const override
|
||||
{
|
||||
if (pluginInstance.editController != nullptr)
|
||||
{
|
||||
return (float) pluginInstance.editController->getParamNormalized (paramID);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
virtual void setValue (float newValue) override
|
||||
{
|
||||
if (pluginInstance.editController != nullptr)
|
||||
{
|
||||
pluginInstance.editController->setParamNormalized (paramID, (double) newValue);
|
||||
|
||||
Steinberg::int32 index;
|
||||
pluginInstance.inputParameterChanges->addParameterData (paramID, index)
|
||||
->addPoint (0, newValue, index);
|
||||
}
|
||||
}
|
||||
|
||||
String getText (float value, int maximumLength) const override
|
||||
{
|
||||
if (pluginInstance.editController != nullptr)
|
||||
{
|
||||
Vst::String128 result;
|
||||
|
||||
if (pluginInstance.editController->getParamStringByValue (paramID, value, result) == kResultOk)
|
||||
return toString (result).substring (0, maximumLength);
|
||||
}
|
||||
|
||||
return Parameter::getText (value, maximumLength);
|
||||
}
|
||||
|
||||
float getValueForText (const String& text) const override
|
||||
{
|
||||
if (pluginInstance.editController != nullptr)
|
||||
{
|
||||
Vst::ParamValue result;
|
||||
|
||||
if (pluginInstance.editController->getParamValueByString (paramID, toString (text), result) == kResultOk)
|
||||
return (float) result;
|
||||
}
|
||||
|
||||
return Parameter::getValueForText (text);
|
||||
}
|
||||
|
||||
float getDefaultValue() const override
|
||||
{
|
||||
return (float) defaultValue;
|
||||
}
|
||||
|
||||
String getName (int /*maximumStringLength*/) const override
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
String getLabel() const override
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
bool isAutomatable() const override
|
||||
{
|
||||
return automatable;
|
||||
}
|
||||
|
||||
bool isDiscrete() const override
|
||||
{
|
||||
return discrete;
|
||||
}
|
||||
|
||||
int getNumSteps() const override
|
||||
{
|
||||
return numSteps;
|
||||
}
|
||||
|
||||
StringArray getAllValueStrings() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
VST3PluginInstance& pluginInstance;
|
||||
const Steinberg::Vst::ParamID paramID;
|
||||
const String name, label;
|
||||
const Steinberg::Vst::ParamValue defaultValue;
|
||||
const bool automatable, discrete;
|
||||
const int numSteps;
|
||||
};
|
||||
|
||||
VST3PluginInstance (VST3ComponentHolder* componentHolder)
|
||||
: AudioPluginInstance (getBusProperties (componentHolder->component)),
|
||||
holder (componentHolder),
|
||||
|
|
@ -1752,9 +1873,30 @@ struct VST3PluginInstance : public AudioPluginInstance
|
|||
editController->setComponentHandler (holder->host);
|
||||
grabInformationObjects();
|
||||
interconnectComponentAndController();
|
||||
|
||||
for (int i = 0; i < editController->getParameterCount(); ++i)
|
||||
{
|
||||
Vst::ParameterInfo paramInfo = { 0 };
|
||||
editController->getParameterInfo (i, paramInfo);
|
||||
|
||||
bool isDiscrete = paramInfo.stepCount != 0;
|
||||
int numSteps = isDiscrete ? paramInfo.stepCount + 1
|
||||
: AudioProcessor::getDefaultNumParameterSteps();
|
||||
|
||||
addParameter (new VST3Parameter (*this,
|
||||
paramInfo.id,
|
||||
toString (paramInfo.title),
|
||||
toString (paramInfo.units),
|
||||
paramInfo.defaultNormalizedValue,
|
||||
(paramInfo.flags & Vst::ParameterInfo::kCanAutomate) != 0,
|
||||
isDiscrete,
|
||||
numSteps));
|
||||
}
|
||||
|
||||
synchroniseStates();
|
||||
syncProgramNames();
|
||||
setupIO();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2158,93 +2300,6 @@ struct VST3PluginInstance : public AudioPluginInstance
|
|||
return view != nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int getNumParameters() override
|
||||
{
|
||||
if (editController != nullptr)
|
||||
return (int) editController->getParameterCount();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const String getParameterName (int parameterIndex) override
|
||||
{
|
||||
return toString (getParameterInfoForIndex (parameterIndex).title);
|
||||
}
|
||||
|
||||
const String getParameterText (int parameterIndex) override
|
||||
{
|
||||
if (editController != nullptr)
|
||||
{
|
||||
auto id = getParameterInfoForIndex (parameterIndex).id;
|
||||
|
||||
Vst::String128 result;
|
||||
warnOnFailure (editController->getParamStringByValue (id, editController->getParamNormalized (id), result));
|
||||
|
||||
return toString (result);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool isParameterAutomatable (int parameterIndex) const override
|
||||
{
|
||||
if (editController != nullptr)
|
||||
{
|
||||
auto flags = getParameterInfoForIndex (parameterIndex).flags;
|
||||
return (flags & Steinberg::Vst::ParameterInfo::kCanAutomate) != 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
auto paramID = getParameterInfoForIndex (parameterIndex).id;
|
||||
editController->setParamNormalized (paramID, (double) newValue);
|
||||
|
||||
Steinberg::int32 index;
|
||||
inputParameterChanges->addParameterData (paramID, index)->addPoint (0, newValue, index);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int getNumPrograms() override { return programNames.size(); }
|
||||
const String getProgramName (int index) override { return programNames[index]; }
|
||||
|
|
|
|||
|
|
@ -248,6 +248,330 @@ namespace
|
|||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
class VSTXMLInfo
|
||||
{
|
||||
public:
|
||||
static VSTXMLInfo* createFor (const juce::XmlElement& xml)
|
||||
{
|
||||
if (xml.hasTagName ("VSTParametersStructure"))
|
||||
return new VSTXMLInfo (xml);
|
||||
|
||||
if (const auto* x = xml.getChildByName ("VSTParametersStructure"))
|
||||
return new VSTXMLInfo (*x);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct Group;
|
||||
|
||||
struct Base
|
||||
{
|
||||
Base() noexcept {}
|
||||
virtual ~Base() {}
|
||||
|
||||
Group* parent = nullptr;
|
||||
};
|
||||
|
||||
struct Param : public Base
|
||||
{
|
||||
int paramID;
|
||||
juce::String expr, name, label;
|
||||
juce::StringArray shortNames;
|
||||
juce::String type;
|
||||
int numberOfStates;
|
||||
float defaultValue;
|
||||
};
|
||||
|
||||
struct Group : public Base
|
||||
{
|
||||
juce::String name;
|
||||
juce::OwnedArray<Base> paramTree;
|
||||
};
|
||||
|
||||
struct Range
|
||||
{
|
||||
Range() noexcept {}
|
||||
Range (const juce::String& s) { set (s); }
|
||||
|
||||
void set (const juce::String& s)
|
||||
{
|
||||
inclusiveLow = s.startsWithChar ('[');
|
||||
inclusiveHigh = s.endsWithChar (']');
|
||||
|
||||
auto str = s.removeCharacters ("[]");
|
||||
|
||||
low = str.upToFirstOccurrenceOf (",", false, false).getFloatValue();
|
||||
high = str.fromLastOccurrenceOf (",", false, false).getFloatValue();
|
||||
}
|
||||
|
||||
bool contains (float f) const noexcept
|
||||
{
|
||||
return (inclusiveLow ? (f >= low) : (f > low))
|
||||
&& (inclusiveHigh ? (f <= high) : (f < high));
|
||||
}
|
||||
|
||||
float low = 0;
|
||||
float high = 0;
|
||||
|
||||
bool inclusiveLow = false;
|
||||
bool inclusiveHigh = false;
|
||||
};
|
||||
|
||||
struct Entry
|
||||
{
|
||||
juce::String name;
|
||||
Range range;
|
||||
};
|
||||
|
||||
struct ValueType
|
||||
{
|
||||
juce::String name, label;
|
||||
juce::OwnedArray<Entry> entries;
|
||||
};
|
||||
|
||||
struct Template
|
||||
{
|
||||
juce::String name;
|
||||
juce::OwnedArray<Param> params;
|
||||
};
|
||||
|
||||
const Param* getParamForID (const int paramID, const Group* const grp) const
|
||||
{
|
||||
for (auto item : (grp != nullptr ? grp->paramTree : paramTree))
|
||||
{
|
||||
if (auto param = dynamic_cast<const Param*> (item))
|
||||
if (param->paramID == paramID)
|
||||
return param;
|
||||
|
||||
if (auto group = dynamic_cast<const Group*> (item))
|
||||
if (auto res = getParamForID (paramID, group))
|
||||
return res;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ValueType* getValueType (const juce::String& name) const
|
||||
{
|
||||
for (auto v : valueTypes)
|
||||
if (v->name == name)
|
||||
return v;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
juce::OwnedArray<Base> paramTree;
|
||||
juce::OwnedArray<ValueType> valueTypes;
|
||||
juce::OwnedArray<Template> templates;
|
||||
|
||||
ValueType switchValueType;
|
||||
|
||||
private:
|
||||
VSTXMLInfo (const juce::XmlElement& xml)
|
||||
{
|
||||
switchValueType.entries.add (new Entry({ TRANS("Off"), Range ("[0, 0.5[") }));
|
||||
switchValueType.entries.add (new Entry({ TRANS("On"), Range ("[0.5, 1]") }));
|
||||
|
||||
forEachXmlChildElement (xml, item)
|
||||
{
|
||||
if (item->hasTagName ("Param")) parseParam (*item, nullptr, nullptr);
|
||||
else if (item->hasTagName ("ValueType")) parseValueType (*item);
|
||||
else if (item->hasTagName ("Template")) parseTemplate (*item);
|
||||
else if (item->hasTagName ("Group")) parseGroup (*item, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void parseParam (const juce::XmlElement& item, Group* group, Template* temp)
|
||||
{
|
||||
auto param = new Param();
|
||||
|
||||
if (temp != nullptr)
|
||||
param->expr = item.getStringAttribute ("id");
|
||||
else
|
||||
param->paramID = item.getIntAttribute ("id");
|
||||
|
||||
param->name = item.getStringAttribute ("name");
|
||||
param->label = item.getStringAttribute ("label");
|
||||
param->type = item.getStringAttribute ("type");
|
||||
param->numberOfStates = item.getIntAttribute ("numberOfStates");
|
||||
param->defaultValue = (float) item.getDoubleAttribute ("defaultValue");
|
||||
|
||||
param->shortNames.addTokens (item.getStringAttribute ("shortName"), ",", juce::StringRef());
|
||||
param->shortNames.trim();
|
||||
param->shortNames.removeEmptyStrings();
|
||||
|
||||
if (group != nullptr)
|
||||
{
|
||||
group->paramTree.add (param);
|
||||
param->parent = group;
|
||||
}
|
||||
else if (temp != nullptr)
|
||||
{
|
||||
temp->params.add (param);
|
||||
}
|
||||
else
|
||||
{
|
||||
paramTree.add (param);
|
||||
}
|
||||
}
|
||||
|
||||
void parseValueType (const juce::XmlElement& item)
|
||||
{
|
||||
auto vt = new ValueType();
|
||||
valueTypes.add (vt);
|
||||
|
||||
vt->name = item.getStringAttribute ("name");
|
||||
vt->label = item.getStringAttribute ("label");
|
||||
|
||||
int curEntry = 0;
|
||||
const int numEntries = item.getNumChildElements();
|
||||
|
||||
forEachXmlChildElementWithTagName (item, entryXml, "Entry")
|
||||
{
|
||||
auto entry = new Entry();
|
||||
entry->name = entryXml->getStringAttribute ("name");
|
||||
|
||||
if (entryXml->hasAttribute ("value"))
|
||||
{
|
||||
entry->range.set(entryXml->getStringAttribute ("value"));
|
||||
}
|
||||
else
|
||||
{
|
||||
entry->range.low = curEntry / (float) numEntries;
|
||||
entry->range.high = (curEntry + 1) / (float) numEntries;
|
||||
|
||||
entry->range.inclusiveLow = true;
|
||||
entry->range.inclusiveHigh = (curEntry == numEntries - 1);
|
||||
}
|
||||
|
||||
vt->entries.add (entry);
|
||||
++curEntry;
|
||||
}
|
||||
}
|
||||
|
||||
void parseTemplate (const juce::XmlElement& item)
|
||||
{
|
||||
auto temp = new Template();
|
||||
templates.add (temp);
|
||||
temp->name = item.getStringAttribute ("name");
|
||||
|
||||
forEachXmlChildElement (item, param)
|
||||
parseParam (*param, nullptr, temp);
|
||||
}
|
||||
|
||||
void parseGroup (const juce::XmlElement& item, Group* parentGroup)
|
||||
{
|
||||
auto group = new Group();
|
||||
|
||||
if (parentGroup)
|
||||
{
|
||||
parentGroup->paramTree.add (group);
|
||||
group->parent = parentGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
paramTree.add (group);
|
||||
}
|
||||
|
||||
group->name = item.getStringAttribute ("name");
|
||||
|
||||
if (item.hasAttribute ("template"))
|
||||
{
|
||||
juce::StringArray variables;
|
||||
variables.addTokens (item.getStringAttribute ("values"), ";", juce::StringRef());
|
||||
variables.trim();
|
||||
|
||||
for (auto temp : templates)
|
||||
{
|
||||
if (temp->name == item.getStringAttribute ("template"))
|
||||
{
|
||||
for (int i = 0; i < temp->params.size(); ++i)
|
||||
{
|
||||
auto param = new Param();
|
||||
group->paramTree.add (param);
|
||||
|
||||
param->parent = group;
|
||||
param->paramID = evaluate (temp->params[i]->expr, variables);
|
||||
param->defaultValue = temp->params[i]->defaultValue;
|
||||
param->label = temp->params[i]->label;
|
||||
param->name = temp->params[i]->name;
|
||||
param->numberOfStates = temp->params[i]->numberOfStates;
|
||||
param->shortNames = temp->params[i]->shortNames;
|
||||
param->type = temp->params[i]->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
forEachXmlChildElement (item, subItem)
|
||||
{
|
||||
if (subItem->hasTagName ("Param")) parseParam (*subItem, group, nullptr);
|
||||
else if (subItem->hasTagName ("Group")) parseGroup (*subItem, group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int evaluate (juce::String expr, const juce::StringArray& variables) const
|
||||
{
|
||||
juce::StringArray names;
|
||||
juce::Array<int> vals;
|
||||
|
||||
for (auto& v : variables)
|
||||
{
|
||||
if (v.contains ("="))
|
||||
{
|
||||
names.add (v.upToFirstOccurrenceOf ("=", false, false));
|
||||
vals.add (v.fromFirstOccurrenceOf ("=", false, false).getIntValue());
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < names.size(); ++i)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
const int idx = expr.indexOfWholeWord (names[i]);
|
||||
if (idx < 0)
|
||||
break;
|
||||
|
||||
expr = expr.replaceSection (idx, names[i].length(), juce::String (vals[i]));
|
||||
}
|
||||
}
|
||||
|
||||
expr = expr.retainCharacters ("01234567890-+")
|
||||
.replace ("+", " + ")
|
||||
.replace ("-", " - ");
|
||||
|
||||
juce::StringArray tokens;
|
||||
tokens.addTokens (expr, " ", juce::StringRef());
|
||||
|
||||
bool add = true;
|
||||
int val = 0;
|
||||
|
||||
for (const auto& s : tokens)
|
||||
{
|
||||
if (s == "+")
|
||||
{
|
||||
add = true;
|
||||
}
|
||||
else if (s == "-")
|
||||
{
|
||||
add = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (add)
|
||||
val += s.getIntValue();
|
||||
else
|
||||
val -= s.getIntValue();
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct ModuleHandle : public ReferenceCountedObject
|
||||
{
|
||||
|
|
@ -498,12 +822,158 @@ static const int defaultVSTBlockSizeValue = 512;
|
|||
#pragma warning (disable: 4996) // warning about overriding deprecated methods
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
//==============================================================================
|
||||
struct VSTPluginInstance : public AudioPluginInstance,
|
||||
private Timer,
|
||||
private AsyncUpdater
|
||||
{
|
||||
struct VSTParameter final : public Parameter
|
||||
{
|
||||
VSTParameter (VSTPluginInstance& parent,
|
||||
const String& paramName,
|
||||
const Array<String>& shortParamNames,
|
||||
float paramDefaultValue,
|
||||
const String& paramLabel,
|
||||
bool paramIsAutomatable,
|
||||
bool paramIsDiscrete,
|
||||
int numParamSteps,
|
||||
bool isBoolSwitch,
|
||||
const StringArray& paramValueStrings,
|
||||
const VSTXMLInfo::ValueType* paramValueType)
|
||||
: pluginInstance (parent),
|
||||
name (paramName),
|
||||
shortNames (shortParamNames),
|
||||
defaultValue (paramDefaultValue),
|
||||
label (paramLabel),
|
||||
automatable (paramIsAutomatable),
|
||||
discrete (paramIsDiscrete),
|
||||
numSteps (numParamSteps),
|
||||
isSwitch (isBoolSwitch),
|
||||
vstValueStrings (paramValueStrings),
|
||||
valueType (paramValueType)
|
||||
{
|
||||
}
|
||||
|
||||
virtual float getValue() const override
|
||||
{
|
||||
if (auto* effect = pluginInstance.vstEffect)
|
||||
{
|
||||
const ScopedLock sl (pluginInstance.lock);
|
||||
|
||||
return effect->getParameterValueFunction (effect, getParameterIndex());
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
virtual void setValue (float newValue) override
|
||||
{
|
||||
if (auto* effect = pluginInstance.vstEffect)
|
||||
{
|
||||
const ScopedLock sl (pluginInstance.lock);
|
||||
|
||||
if (effect->getParameterValueFunction (effect, getParameterIndex()) != newValue)
|
||||
effect->setParameterValueFunction (effect, getParameterIndex(), newValue);
|
||||
}
|
||||
}
|
||||
|
||||
String getText (float value, int maximumStringLength) const override
|
||||
{
|
||||
if (valueType != nullptr)
|
||||
{
|
||||
for (auto& v : valueType->entries)
|
||||
if (v->range.contains (value))
|
||||
return v->name;
|
||||
}
|
||||
|
||||
return Parameter::getText (value, maximumStringLength);
|
||||
}
|
||||
|
||||
float getValueForText (const String& text) const override
|
||||
{
|
||||
if (valueType != nullptr)
|
||||
{
|
||||
for (auto& v : valueType->entries)
|
||||
if (v->name == text)
|
||||
return (v->range.high + v->range.low) / 2.0f;
|
||||
}
|
||||
|
||||
return Parameter::getValueForText (text);
|
||||
}
|
||||
|
||||
String getCurrentValueAsText() const override
|
||||
{
|
||||
if (valueType != nullptr || ! vstValueStrings.isEmpty())
|
||||
return getText (getValue(), 1024);
|
||||
|
||||
return pluginInstance.getTextForOpcode (getParameterIndex(), plugInOpcodeGetParameterText);
|
||||
}
|
||||
|
||||
float getDefaultValue() const override
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
String getName (int maximumStringLength) const override
|
||||
{
|
||||
if (name.length() <= maximumStringLength)
|
||||
return name;
|
||||
|
||||
if (! shortNames.isEmpty())
|
||||
{
|
||||
for (auto& n : shortNames)
|
||||
if (n.length() <= maximumStringLength)
|
||||
return n;
|
||||
|
||||
return shortNames.getLast();
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
String getLabel() const override
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
bool isAutomatable() const override
|
||||
{
|
||||
return automatable;
|
||||
}
|
||||
|
||||
bool isDiscrete() const override
|
||||
{
|
||||
return discrete;
|
||||
}
|
||||
|
||||
bool isBoolean() const override
|
||||
{
|
||||
return isSwitch;
|
||||
}
|
||||
|
||||
int getNumSteps() const override
|
||||
{
|
||||
return numSteps;
|
||||
}
|
||||
|
||||
StringArray getAllValueStrings() const override
|
||||
{
|
||||
return vstValueStrings;
|
||||
}
|
||||
|
||||
VSTPluginInstance& pluginInstance;
|
||||
|
||||
const String name;
|
||||
const Array<String> shortNames;
|
||||
const float defaultValue;
|
||||
const String label;
|
||||
const bool automatable, discrete;
|
||||
const int numSteps;
|
||||
const bool isSwitch;
|
||||
const StringArray vstValueStrings;
|
||||
const VSTXMLInfo::ValueType* const valueType;
|
||||
};
|
||||
|
||||
VSTPluginInstance (const ModuleHandle::Ptr& mh, const BusesProperties& ioConfig, VstEffectInterface* effect,
|
||||
double sampleRateToUse, int blockSizeToUse)
|
||||
: AudioPluginInstance (ioConfig),
|
||||
|
|
@ -511,6 +981,88 @@ struct VSTPluginInstance : public AudioPluginInstance,
|
|||
vstModule (mh),
|
||||
name (mh->pluginName)
|
||||
{
|
||||
jassert (vstEffect != nullptr);
|
||||
|
||||
if (auto* xml = vstModule->vstXml.get())
|
||||
xmlInfo.reset (VSTXMLInfo::createFor (*xml));
|
||||
|
||||
for (int i = 0; i < vstEffect->numParameters; ++i)
|
||||
{
|
||||
String paramName (getTextForOpcode (i, plugInOpcodeGetParameterName));
|
||||
Array<String> shortParamNames;
|
||||
float defaultValue = 0;
|
||||
String label (getTextForOpcode (i, plugInOpcodeGetParameterLabel));
|
||||
bool isAutomatable = dispatch (plugInOpcodeIsParameterAutomatable, i, 0, 0, 0) != 0;
|
||||
bool isDiscrete = false;
|
||||
int numSteps = AudioProcessor::getDefaultNumParameterSteps();
|
||||
bool isBoolSwitch = false;
|
||||
StringArray parameterValueStrings;
|
||||
const VSTXMLInfo::ValueType* valueType = nullptr;
|
||||
|
||||
if (xmlInfo != nullptr)
|
||||
{
|
||||
if (auto* param = xmlInfo->getParamForID (i, nullptr))
|
||||
{
|
||||
paramName = param->name;
|
||||
|
||||
for (auto& n : param->shortNames)
|
||||
shortParamNames.add (n);
|
||||
|
||||
struct LengthComparator
|
||||
{
|
||||
static int compareElements (const juce::String& first, const juce::String& second) noexcept
|
||||
{
|
||||
return first.length() - second.length();
|
||||
}
|
||||
};
|
||||
|
||||
LengthComparator comp;
|
||||
shortParamNames.sort (comp);
|
||||
|
||||
defaultValue = param->defaultValue;
|
||||
label = param->label;
|
||||
|
||||
if (param->type == "switch")
|
||||
{
|
||||
isBoolSwitch = true;
|
||||
numSteps = 2;
|
||||
valueType = &xmlInfo->switchValueType;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueType = xmlInfo->getValueType (param->type);
|
||||
}
|
||||
|
||||
if (param->numberOfStates >= 2)
|
||||
{
|
||||
numSteps = param->numberOfStates;
|
||||
|
||||
if (valueType != nullptr)
|
||||
{
|
||||
for (auto* entry : valueType->entries)
|
||||
parameterValueStrings.add (entry->name);
|
||||
|
||||
parameterValueStrings.removeEmptyStrings();
|
||||
}
|
||||
}
|
||||
|
||||
isDiscrete = (numSteps != AudioProcessor::getDefaultNumParameterSteps());
|
||||
}
|
||||
}
|
||||
|
||||
addParameter (new VSTParameter (*this,
|
||||
paramName,
|
||||
shortParamNames,
|
||||
defaultValue,
|
||||
label,
|
||||
isAutomatable,
|
||||
isDiscrete,
|
||||
numSteps,
|
||||
isBoolSwitch,
|
||||
parameterValueStrings,
|
||||
valueType));
|
||||
}
|
||||
|
||||
setRateAndBufferSizeDetails (sampleRateToUse, blockSizeToUse);
|
||||
}
|
||||
|
||||
|
|
@ -802,11 +1354,14 @@ struct VSTPluginInstance : public AudioPluginInstance,
|
|||
setPower (true);
|
||||
|
||||
// dodgy hack to force some plugins to initialise the sample rate..
|
||||
if ((! hasEditor()) && getNumParameters() > 0)
|
||||
if (! hasEditor())
|
||||
{
|
||||
auto old = getParameter (0);
|
||||
setParameter (0, (old < 0.5f) ? 1.0f : 0.0f);
|
||||
setParameter (0, old);
|
||||
if (auto* firstParam = getParameters()[0])
|
||||
{
|
||||
auto old = firstParam->getValue();
|
||||
firstParam->setValue ((old < 0.5f) ? 1.0f : 0.0f);
|
||||
firstParam->setValue (old);
|
||||
}
|
||||
}
|
||||
|
||||
dispatch (plugInOpcodeStartProcess, 0, 0, 0, 0);
|
||||
|
|
@ -943,46 +1498,6 @@ struct VSTPluginInstance : public AudioPluginInstance,
|
|||
: getTotalNumOutputChannels());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int getNumParameters() override { return vstEffect != nullptr ? vstEffect->numParameters : 0; }
|
||||
|
||||
float getParameter (int index) override
|
||||
{
|
||||
if (vstEffect != nullptr && isPositiveAndBelow (index, vstEffect->numParameters))
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return vstEffect->getParameterValueFunction (vstEffect, index);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void setParameter (int index, float newValue) override
|
||||
{
|
||||
if (vstEffect != nullptr && isPositiveAndBelow (index, vstEffect->numParameters))
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (vstEffect->getParameterValueFunction (vstEffect, index) != newValue)
|
||||
vstEffect->setParameterValueFunction (vstEffect, index, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
const String getParameterName (int index) override { return getTextForOpcode (index, plugInOpcodeGetParameterName); }
|
||||
const String getParameterText (int index) override { return getTextForOpcode (index, plugInOpcodeGetParameterText); }
|
||||
String getParameterLabel (int index) const override { return getTextForOpcode (index, plugInOpcodeGetParameterLabel); }
|
||||
|
||||
bool isParameterAutomatable (int index) const override
|
||||
{
|
||||
if (vstEffect != nullptr)
|
||||
{
|
||||
jassert (index >= 0 && index < vstEffect->numParameters);
|
||||
return dispatch (plugInOpcodeIsParameterAutomatable, index, 0, 0, 0) != 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int getNumPrograms() override { return vstEffect != nullptr ? jmax (0, vstEffect->numPrograms) : 0; }
|
||||
|
||||
|
|
@ -1051,7 +1566,14 @@ struct VSTPluginInstance : public AudioPluginInstance,
|
|||
{
|
||||
switch (opcode)
|
||||
{
|
||||
case hostOpcodeParameterChanged: sendParamChangeMessageToListeners (index, opt); break;
|
||||
case hostOpcodeParameterChanged:
|
||||
if (auto* param = getParameters()[index])
|
||||
param->sendValueChangedMessageToListeners (opt);
|
||||
else
|
||||
jassertfalse; // Invalid parameter index!
|
||||
|
||||
break;
|
||||
|
||||
case hostOpcodePreAudioProcessingEvents: handleMidiFromPlugin ((const VstEventBlock*) ptr); break;
|
||||
case hostOpcodeGetTimingInfo: return getVSTTime();
|
||||
case hostOpcodeIdle: handleIdle(); break;
|
||||
|
|
@ -1068,8 +1590,21 @@ struct VSTPluginInstance : public AudioPluginInstance,
|
|||
case hostOpcodeTempoAt: return (pointer_sized_int) (extraFunctions != nullptr ? extraFunctions->getTempoAt ((int64) value) : 0);
|
||||
case hostOpcodeGetAutomationState: return (pointer_sized_int) (extraFunctions != nullptr ? extraFunctions->getAutomationState() : 0);
|
||||
|
||||
case hostOpcodeParameterChangeGestureBegin: beginParameterChangeGesture (index); break;
|
||||
case hostOpcodeParameterChangeGestureEnd: endParameterChangeGesture (index); break;
|
||||
case hostOpcodeParameterChangeGestureBegin:
|
||||
if (auto* param = getParameters()[index])
|
||||
param->beginChangeGesture();
|
||||
else
|
||||
jassertfalse; // Invalid parameter index!
|
||||
|
||||
break;
|
||||
|
||||
case hostOpcodeParameterChangeGestureEnd:
|
||||
if (auto* param = getParameters()[index])
|
||||
param->endChangeGesture();
|
||||
else
|
||||
jassertfalse; // Invalid parameter index!
|
||||
|
||||
break;
|
||||
|
||||
case hostOpcodePinConnected: return isValidChannel (index, value == 0) ? 0 : 1; // (yes, 0 = true)
|
||||
case hostOpcodeGetCurrentAudioProcessingLevel: return isNonRealtime() ? 4 : 0;
|
||||
|
|
@ -1226,7 +1761,8 @@ struct VSTPluginInstance : public AudioPluginInstance,
|
|||
changeProgramName (getCurrentProgram(), prog->prgName);
|
||||
|
||||
for (int i = 0; i < fxbSwap (prog->numParams); ++i)
|
||||
setParameter (i, fxbSwapFloat (prog->params[i]));
|
||||
if (auto* param = getParameters()[i])
|
||||
param->setValue (fxbSwapFloat (prog->params[i]));
|
||||
}
|
||||
else if (compareMagic (set->fxMagic, "FBCh"))
|
||||
{
|
||||
|
|
@ -1261,7 +1797,7 @@ struct VSTPluginInstance : public AudioPluginInstance,
|
|||
bool saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB = 128)
|
||||
{
|
||||
auto numPrograms = getNumPrograms();
|
||||
auto numParams = getNumParameters();
|
||||
auto numParams = getParameters().size();
|
||||
|
||||
if (usesChunks())
|
||||
{
|
||||
|
|
@ -1413,6 +1949,8 @@ private:
|
|||
AudioBuffer<double> tmpBufferDouble;
|
||||
HeapBlock<double*> channelBufferDouble;
|
||||
|
||||
ScopedPointer<VSTXMLInfo> xmlInfo;
|
||||
|
||||
static pointer_sized_int handleCanDo (const char* name)
|
||||
{
|
||||
static const char* canDos[] = { "supplyIdle",
|
||||
|
|
@ -1832,7 +2370,8 @@ private:
|
|||
changeProgramName (getCurrentProgram(), prog->prgName);
|
||||
|
||||
for (int i = 0; i < fxbSwap (prog->numParams); ++i)
|
||||
setParameter (i, fxbSwapFloat (prog->params[i]));
|
||||
if (auto* param = getParameters()[i])
|
||||
param->setValue (fxbSwapFloat (prog->params[i]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1879,7 +2418,7 @@ private:
|
|||
|
||||
void setParamsInProgramBlock (fxProgram* prog)
|
||||
{
|
||||
auto numParams = getNumParameters();
|
||||
auto numParams = getParameters().size();
|
||||
|
||||
prog->chunkMagic = fxbName ("CcnK");
|
||||
prog->byteSize = 0;
|
||||
|
|
@ -1892,7 +2431,8 @@ private:
|
|||
getCurrentProgramName().copyToUTF8 (prog->prgName, sizeof (prog->prgName) - 1);
|
||||
|
||||
for (int i = 0; i < numParams; ++i)
|
||||
prog->params[i] = fxbSwapFloat (getParameter (i));
|
||||
if (auto* param = getParameters()[i])
|
||||
prog->params[i] = fxbSwapFloat (param->getValue());
|
||||
}
|
||||
|
||||
void updateStoredProgramNames()
|
||||
|
|
@ -1932,15 +2472,17 @@ private:
|
|||
//==============================================================================
|
||||
void createTempParameterStore (MemoryBlock& dest)
|
||||
{
|
||||
dest.setSize (64 + 4 * (size_t) getNumParameters());
|
||||
auto numParameters = getParameters().size();
|
||||
dest.setSize (64 + 4 * (size_t) numParameters);
|
||||
dest.fillWith (0);
|
||||
|
||||
getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63);
|
||||
|
||||
auto p = (float*) (((char*) dest.getData()) + 64);
|
||||
|
||||
for (int i = 0; i < getNumParameters(); ++i)
|
||||
p[i] = getParameter(i);
|
||||
for (int i = 0; i < numParameters; ++i)
|
||||
if (auto* param = getParameters()[i])
|
||||
p[i] = param->getValue();
|
||||
}
|
||||
|
||||
void restoreFromTempParameterStore (const MemoryBlock& m)
|
||||
|
|
@ -1948,9 +2490,11 @@ private:
|
|||
changeProgramName (getCurrentProgram(), (const char*) m.getData());
|
||||
|
||||
auto p = (float*) (((char*) m.getData()) + 64);
|
||||
auto numParameters = getParameters().size();
|
||||
|
||||
for (int i = 0; i < getNumParameters(); ++i)
|
||||
setParameter (i, p[i]);
|
||||
for (int i = 0; i < numParameters; ++i)
|
||||
if (auto* param = getParameters()[i])
|
||||
param->setValue (p[i]);
|
||||
}
|
||||
|
||||
pointer_sized_int getVstDirectory() const
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewCompone
|
|||
#include "format/juce_AudioPluginFormat.cpp"
|
||||
#include "format/juce_AudioPluginFormatManager.cpp"
|
||||
#include "processors/juce_AudioProcessor.cpp"
|
||||
#include "processors/juce_AudioPluginInstance.cpp"
|
||||
#include "processors/juce_AudioProcessorEditor.cpp"
|
||||
#include "processors/juce_AudioProcessorGraph.cpp"
|
||||
#include "processors/juce_GenericAudioProcessorEditor.cpp"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
PluginDescription AudioPluginInstance::getPluginDescription() const
|
||||
{
|
||||
PluginDescription desc;
|
||||
fillInPluginDescription (desc);
|
||||
return desc;
|
||||
}
|
||||
|
||||
String AudioPluginInstance::getParameterID (int parameterIndex)
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
return String (parameterIndex);
|
||||
}
|
||||
|
||||
float AudioPluginInstance::getParameter (int parameterIndex)
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->getValue();
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void AudioPluginInstance::setParameter (int parameterIndex, float newValue)
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->setValue (newValue);
|
||||
}
|
||||
|
||||
const String AudioPluginInstance::getParameterName (int parameterIndex)
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->getName (1024);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const String AudioPluginInstance::getParameterText (int parameterIndex)
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->getCurrentValueAsText();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
String AudioPluginInstance::getParameterText (int parameterIndex, int maximumStringLength)
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->getCurrentValueAsText().substring (0, maximumStringLength);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
float AudioPluginInstance::getParameterDefaultValue (int parameterIndex)
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->getDefaultValue();
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
int AudioPluginInstance::getParameterNumSteps (int parameterIndex)
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->getNumSteps();
|
||||
|
||||
return AudioProcessor::getDefaultNumParameterSteps();
|
||||
}
|
||||
|
||||
bool AudioPluginInstance::isParameterDiscrete (int parameterIndex) const
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->isDiscrete();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AudioPluginInstance::isParameterAutomatable (int parameterIndex) const
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->isAutomatable();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String AudioPluginInstance::getParameterLabel (int parameterIndex) const
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->getLabel();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool AudioPluginInstance::isParameterOrientationInverted (int parameterIndex) const
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->isOrientationInverted();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AudioPluginInstance::isMetaParameter (int parameterIndex) const
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->isMetaParameter();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioProcessorParameter::Category AudioPluginInstance::getParameterCategory (int parameterIndex) const
|
||||
{
|
||||
assertOnceOnDeprecatedMethodUse();
|
||||
|
||||
if (auto* param = getParameters()[parameterIndex])
|
||||
return param->getCategory();
|
||||
|
||||
return AudioProcessorParameter::genericParameter;
|
||||
}
|
||||
|
||||
void AudioPluginInstance::assertOnceOnDeprecatedMethodUse() const noexcept
|
||||
{
|
||||
if (! deprecationAssertiontriggered)
|
||||
{
|
||||
// If you hit this assertion then you are using at least one of the
|
||||
// methods marked as deprecated in this class. For now you can simply
|
||||
// continue past this point and subsequent uses of deprecated methods
|
||||
// will not trigger additional assertions. However, we will shortly be
|
||||
// removing these methods so you are strongly advised to look at the
|
||||
// implementation of the corresponding method in this class and use
|
||||
// that approach instead.
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
deprecationAssertiontriggered = true;
|
||||
}
|
||||
|
||||
bool AudioPluginInstance::deprecationAssertiontriggered = false;
|
||||
|
||||
AudioPluginInstance::Parameter::Parameter()
|
||||
{
|
||||
onStrings.add (TRANS("on"));
|
||||
onStrings.add (TRANS("yes"));
|
||||
onStrings.add (TRANS("true"));
|
||||
|
||||
offStrings.add (TRANS("off"));
|
||||
offStrings.add (TRANS("no"));
|
||||
offStrings.add (TRANS("false"));
|
||||
}
|
||||
|
||||
AudioPluginInstance::Parameter::~Parameter() {}
|
||||
|
||||
String AudioPluginInstance::Parameter::getText (float value, int maximumStringLength) const
|
||||
{
|
||||
if (isBoolean())
|
||||
return value < 0.5f ? TRANS("Off") : TRANS("On");
|
||||
|
||||
return String (value).substring (0, maximumStringLength);
|
||||
}
|
||||
|
||||
float AudioPluginInstance::Parameter::getValueForText (const String& text) const
|
||||
{
|
||||
auto floatValue = text.retainCharacters ("-0123456789.").getFloatValue();
|
||||
|
||||
if (isBoolean())
|
||||
{
|
||||
if (onStrings.contains (text, true))
|
||||
return 1.0f;
|
||||
|
||||
if (offStrings.contains (text, true))
|
||||
return 0.0f;
|
||||
|
||||
return floatValue < 0.5f ? 0.0f : 1.0f;
|
||||
}
|
||||
|
||||
return floatValue;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -58,12 +58,7 @@ public:
|
|||
/** Returns a PluginDescription for this plugin.
|
||||
This is just a convenience method to avoid calling fillInPluginDescription.
|
||||
*/
|
||||
PluginDescription getPluginDescription() const
|
||||
{
|
||||
PluginDescription desc;
|
||||
fillInPluginDescription (desc);
|
||||
return desc;
|
||||
}
|
||||
PluginDescription getPluginDescription() const;
|
||||
|
||||
/** Returns a pointer to some kind of platform-specific data about the plugin.
|
||||
E.g. For a VST, this value can be cast to an AEffect*. For an AudioUnit, it can be
|
||||
|
|
@ -76,13 +71,51 @@ public:
|
|||
*/
|
||||
virtual void refreshParameterList() {}
|
||||
|
||||
// Rather than using these methods you should call the corresponding methods
|
||||
// on the AudioProcessorParameter objects returned from getParameters().
|
||||
// See the implementations of the methods below for some examples of how to
|
||||
// do this.
|
||||
//
|
||||
// In addition to being marked as deprecated these methods will assert on
|
||||
// the first call.
|
||||
JUCE_DEPRECATED (String getParameterID (int index) override);
|
||||
JUCE_DEPRECATED (float getParameter (int parameterIndex) override);
|
||||
JUCE_DEPRECATED (void setParameter (int parameterIndex, float newValue) override);
|
||||
JUCE_DEPRECATED (const String getParameterName (int parameterIndex) override);
|
||||
JUCE_DEPRECATED (const String getParameterText (int parameterIndex) override);
|
||||
JUCE_DEPRECATED (String getParameterText (int parameterIndex, int maximumStringLength) override);
|
||||
JUCE_DEPRECATED (int getParameterNumSteps (int parameterIndex) override);
|
||||
JUCE_DEPRECATED (bool isParameterDiscrete (int parameterIndex) const override);
|
||||
JUCE_DEPRECATED (bool isParameterAutomatable (int parameterIndex) const override);
|
||||
JUCE_DEPRECATED (float getParameterDefaultValue (int parameterIndex) override);
|
||||
JUCE_DEPRECATED (String getParameterLabel (int parameterIndex) const override);
|
||||
JUCE_DEPRECATED (bool isParameterOrientationInverted (int parameterIndex) const override);
|
||||
JUCE_DEPRECATED (bool isMetaParameter (int parameterIndex) const override);
|
||||
JUCE_DEPRECATED (AudioProcessorParameter::Category getParameterCategory (int parameterIndex) const override);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
struct Parameter : public AudioProcessorParameter
|
||||
{
|
||||
Parameter();
|
||||
virtual ~Parameter();
|
||||
|
||||
virtual String getText (float value, int maximumStringLength) const override;
|
||||
virtual float getValueForText (const String& text) const override;
|
||||
|
||||
StringArray onStrings, offStrings;
|
||||
};
|
||||
|
||||
AudioPluginInstance() {}
|
||||
AudioPluginInstance (const BusesProperties& ioLayouts) : AudioProcessor (ioLayouts) {}
|
||||
template <int numLayouts>
|
||||
AudioPluginInstance (const short channelLayoutList[numLayouts][2]) : AudioProcessor (channelLayoutList) {}
|
||||
|
||||
private:
|
||||
void assertOnceOnDeprecatedMethodUse() const noexcept;
|
||||
|
||||
static bool deprecationAssertiontriggered;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -673,10 +673,6 @@ void AudioProcessor::addParameter (AudioProcessorParameter* p)
|
|||
p->parameterIndex = managedParameters.size();
|
||||
managedParameters.add (p);
|
||||
|
||||
// if you're using parameter objects, then you must not override the
|
||||
// deprecated getNumParameters() method!
|
||||
jassert (getNumParameters() == AudioProcessor::getNumParameters());
|
||||
|
||||
#ifdef JUCE_DEBUG
|
||||
shouldCheckParamsForDupeIDs = true;
|
||||
#endif
|
||||
|
|
@ -1354,14 +1350,21 @@ int32 AudioProcessor::getAAXPluginIDForMainBusConfig (const AudioChannelSet& mai
|
|||
return (idForAudioSuite ? 0x6a796161 /* 'jyaa' */ : 0x6a636161 /* 'jcaa' */) + uniqueFormatId;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {}
|
||||
void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {}
|
||||
|
||||
//==============================================================================
|
||||
AudioProcessorParameter::AudioProcessorParameter() noexcept {}
|
||||
AudioProcessorParameter::~AudioProcessorParameter() {}
|
||||
|
||||
AudioProcessorParameter::~AudioProcessorParameter()
|
||||
{
|
||||
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
|
||||
// This will fail if you've called beginChangeGesture() without having made
|
||||
// a corresponding call to endChangeGesture...
|
||||
jassert (! isPerformingGesture);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioProcessorParameter::setValueNotifyingHost (float newValue)
|
||||
{
|
||||
|
|
@ -1452,6 +1455,24 @@ String AudioProcessorParameter::getText (float value, int /*maximumStringLength*
|
|||
return String (value, 2);
|
||||
}
|
||||
|
||||
String AudioProcessorParameter::getCurrentValueAsText() const
|
||||
{
|
||||
return getText (getValue(), 1024);
|
||||
}
|
||||
|
||||
StringArray AudioProcessorParameter::getAllValueStrings() const
|
||||
{
|
||||
if (isDiscrete() && valueStrings.isEmpty())
|
||||
{
|
||||
auto maxIndex = getNumSteps() - 1;
|
||||
|
||||
for (int i = 0; i < getNumSteps(); ++i)
|
||||
valueStrings.add (getText ((float) i / maxIndex, 1024));
|
||||
}
|
||||
|
||||
return valueStrings;
|
||||
}
|
||||
|
||||
void AudioProcessorParameter::addListener (AudioProcessorParameter::Listener* newListener)
|
||||
{
|
||||
const ScopedLock sl (listenerLock);
|
||||
|
|
|
|||
|
|
@ -1620,6 +1620,8 @@ private:
|
|||
|
||||
friend class JuceVST3EditController;
|
||||
friend class JuceVST3Component;
|
||||
friend class AudioUnitPluginInstance;
|
||||
friend class LADSPAPluginInstance;
|
||||
|
||||
Atomic<int> vst3IsPlaying { 0 };
|
||||
|
||||
|
|
|
|||
|
|
@ -199,6 +199,28 @@ public:
|
|||
/** Returns the index of this parameter in its parent processor's parameter list. */
|
||||
int getParameterIndex() const noexcept { return parameterIndex; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current value of the parameter as a String.
|
||||
|
||||
This function can be called when you are hosting plug-ins to get a
|
||||
more specialsed textual represenation of the current value from the
|
||||
plug-in, for example "On" rather than "1.0".
|
||||
|
||||
If you are implementing a plug-in then you should ignore this function
|
||||
and instead override getText.
|
||||
*/
|
||||
virtual String getCurrentValueAsText() const;
|
||||
|
||||
/** Returns the set of strings which represent the possible states a parameter
|
||||
can be in.
|
||||
|
||||
If you are hosting a plug-in you can use the result of this funtion to
|
||||
populate a ComboBox listing the allowed values.
|
||||
|
||||
If you are implementing a plug-in then you do not need to override this.
|
||||
*/
|
||||
virtual StringArray getAllValueStrings() const;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for listeners that want to know about changes to an
|
||||
|
|
@ -267,6 +289,7 @@ private:
|
|||
int parameterIndex = -1;
|
||||
CriticalSection listenerLock;
|
||||
Array<Listener*> listeners;
|
||||
mutable StringArray valueStrings;
|
||||
|
||||
#if JUCE_DEBUG
|
||||
bool isPerformingGesture = false;
|
||||
|
|
|
|||
|
|
@ -114,17 +114,17 @@ private:
|
|||
|
||||
void startedDragging() override
|
||||
{
|
||||
owner.beginParameterChangeGesture(index);
|
||||
owner.beginParameterChangeGesture (index);
|
||||
}
|
||||
|
||||
void stoppedDragging() override
|
||||
{
|
||||
owner.endParameterChangeGesture(index);
|
||||
owner.endParameterChangeGesture (index);
|
||||
}
|
||||
|
||||
String getTextFromValue (double /*value*/) override
|
||||
{
|
||||
return owner.getParameterText (index) + " " + owner.getParameterLabel (index).trimEnd();
|
||||
return (owner.getParameterText (index) + " " + owner.getParameterLabel (index)).trimEnd();
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -143,6 +143,508 @@ private:
|
|||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProcessorParameterPropertyComp)
|
||||
};
|
||||
|
||||
struct LegacyParametersPanel : public Component
|
||||
{
|
||||
LegacyParametersPanel (AudioProcessor* const processor)
|
||||
{
|
||||
addAndMakeVisible (panel);
|
||||
|
||||
Array<PropertyComponent*> params;
|
||||
|
||||
auto numParams = processor->getNumParameters();
|
||||
int totalHeight = 0;
|
||||
|
||||
for (int i = 0; i < numParams; ++i)
|
||||
{
|
||||
String name (processor->getParameterName (i));
|
||||
|
||||
if (name.trim().isEmpty())
|
||||
name = "Unnamed";
|
||||
|
||||
auto* pc = new ProcessorParameterPropertyComp (name, *processor, i);
|
||||
params.add (pc);
|
||||
totalHeight += pc->getPreferredHeight();
|
||||
}
|
||||
|
||||
panel.addProperties (params);
|
||||
|
||||
setSize (400, jmax (25, totalHeight));
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
panel.setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
PropertyPanel panel;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LegacyParametersPanel)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ParameterListener : private AudioProcessorParameter::Listener,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
ParameterListener (AudioProcessorParameter& param)
|
||||
: parameter (param)
|
||||
{
|
||||
parameter.addListener (this);
|
||||
|
||||
startTimer (100);
|
||||
}
|
||||
|
||||
virtual ~ParameterListener()
|
||||
{
|
||||
parameter.removeListener (this);
|
||||
}
|
||||
|
||||
AudioProcessorParameter& getParameter() noexcept
|
||||
{
|
||||
return parameter;
|
||||
}
|
||||
|
||||
virtual void handleNewParameterValue() = 0;
|
||||
|
||||
private:
|
||||
void parameterValueChanged (int, float) override
|
||||
{
|
||||
parameterValueHasChanged = 1;
|
||||
}
|
||||
|
||||
void parameterGestureChanged (int, bool) override {}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
if (parameterValueHasChanged.compareAndSetBool (0, 1))
|
||||
{
|
||||
handleNewParameterValue();
|
||||
startTimerHz (50);
|
||||
}
|
||||
else
|
||||
{
|
||||
startTimer (jmin (250, getTimerInterval() + 10));
|
||||
}
|
||||
}
|
||||
|
||||
AudioProcessorParameter& parameter;
|
||||
Atomic<int> parameterValueHasChanged { 0 };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterListener)
|
||||
};
|
||||
|
||||
class BooleanParameterComponent final : public Component,
|
||||
private ParameterListener
|
||||
{
|
||||
public:
|
||||
BooleanParameterComponent (AudioProcessorParameter& param)
|
||||
: ParameterListener (param)
|
||||
{
|
||||
// Set the initial value.
|
||||
handleNewParameterValue();
|
||||
|
||||
button.onClick = [this]() { buttonClicked(); };
|
||||
|
||||
addAndMakeVisible (button);
|
||||
}
|
||||
|
||||
void paint (Graphics&) override {}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto area = getLocalBounds();
|
||||
area.removeFromLeft (8);
|
||||
button.setBounds (area.reduced (0, 10));
|
||||
}
|
||||
|
||||
private:
|
||||
void handleNewParameterValue() override
|
||||
{
|
||||
auto parameterState = getParameterState (getParameter().getValue());
|
||||
|
||||
if (button.getToggleState() != parameterState)
|
||||
button.setToggleState (parameterState, dontSendNotification);
|
||||
}
|
||||
|
||||
void buttonClicked()
|
||||
{
|
||||
if (getParameterState (getParameter().getValue()) != button.getToggleState())
|
||||
{
|
||||
getParameter().beginChangeGesture();
|
||||
getParameter().setValueNotifyingHost (button.getToggleState() ? 1.0f : 0.0f);
|
||||
getParameter().endChangeGesture();
|
||||
}
|
||||
}
|
||||
|
||||
bool getParameterState (float value) const noexcept
|
||||
{
|
||||
return value >= 0.5f;
|
||||
}
|
||||
|
||||
ToggleButton button;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BooleanParameterComponent)
|
||||
};
|
||||
|
||||
class SwitchParameterComponent final : public Component,
|
||||
private ParameterListener
|
||||
{
|
||||
public:
|
||||
SwitchParameterComponent (AudioProcessorParameter& param)
|
||||
: ParameterListener (param)
|
||||
{
|
||||
auto* leftButton = buttons.add (new TextButton());
|
||||
auto* rightButton = buttons.add (new TextButton());
|
||||
|
||||
for (auto* button : buttons)
|
||||
{
|
||||
button->setRadioGroupId (293847);
|
||||
button->setClickingTogglesState (true);
|
||||
}
|
||||
|
||||
leftButton ->setButtonText (getParameter().getText (0.0f, 16));
|
||||
rightButton->setButtonText (getParameter().getText (1.0f, 16));
|
||||
|
||||
leftButton ->setConnectedEdges (Button::ConnectedOnRight);
|
||||
rightButton->setConnectedEdges (Button::ConnectedOnLeft);
|
||||
|
||||
// Set the initial value.
|
||||
leftButton->setToggleState (true, dontSendNotification);
|
||||
handleNewParameterValue();
|
||||
|
||||
rightButton->onStateChange = [this]() { rightButtonChanged(); };
|
||||
|
||||
for (auto* button : buttons)
|
||||
addAndMakeVisible (button);
|
||||
}
|
||||
|
||||
void paint (Graphics&) override {}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto area = getLocalBounds().reduced (0, 8);
|
||||
area.removeFromLeft (8);
|
||||
|
||||
for (auto* button : buttons)
|
||||
button->setBounds (area.removeFromLeft (80));
|
||||
}
|
||||
|
||||
private:
|
||||
void handleNewParameterValue() override
|
||||
{
|
||||
bool newState = getParameterState();
|
||||
|
||||
if (buttons[1]->getToggleState() != newState)
|
||||
{
|
||||
buttons[1]->setToggleState (newState, dontSendNotification);
|
||||
buttons[0]->setToggleState (! newState, dontSendNotification);
|
||||
}
|
||||
}
|
||||
|
||||
void rightButtonChanged()
|
||||
{
|
||||
auto buttonState = buttons[1]->getToggleState();
|
||||
|
||||
if (getParameterState() != buttonState)
|
||||
{
|
||||
getParameter().beginChangeGesture();
|
||||
|
||||
if (getParameter().getAllValueStrings().isEmpty())
|
||||
{
|
||||
getParameter().setValueNotifyingHost (buttonState ? 1.0f : 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When a parameter provides a list of strings we must set its
|
||||
// value using those strings, rather than a float, because VSTs can
|
||||
// have uneven spacing between the different allowed values and we
|
||||
// want the snapping behaviour to be consistent with what we do with
|
||||
// a combo box.
|
||||
String selectedText = buttonState ? buttons[1]->getButtonText() : buttons[0]->getButtonText();
|
||||
getParameter().setValueNotifyingHost (getParameter().getValueForText (selectedText));
|
||||
}
|
||||
|
||||
getParameter().endChangeGesture();
|
||||
}
|
||||
}
|
||||
|
||||
bool getParameterState()
|
||||
{
|
||||
if (getParameter().getAllValueStrings().isEmpty())
|
||||
return getParameter().getValue() > 0.5f;
|
||||
|
||||
auto index = getParameter().getAllValueStrings()
|
||||
.indexOf (getParameter().getCurrentValueAsText());
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
// The parameter is producing some unexpected text, so we'll do
|
||||
// some linear interpolation.
|
||||
index = roundToInt (getParameter().getValue());
|
||||
}
|
||||
|
||||
return index == 1;
|
||||
}
|
||||
|
||||
OwnedArray<TextButton> buttons;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SwitchParameterComponent)
|
||||
};
|
||||
|
||||
class ChoiceParameterComponent final : public Component,
|
||||
private ParameterListener
|
||||
{
|
||||
public:
|
||||
ChoiceParameterComponent (AudioProcessorParameter& param)
|
||||
: ParameterListener (param),
|
||||
parameterValues (getParameter().getAllValueStrings())
|
||||
{
|
||||
box.addItemList (parameterValues, 1);
|
||||
|
||||
// Set the initial value.
|
||||
handleNewParameterValue();
|
||||
|
||||
box.onChange = [this]() { boxChanged(); };
|
||||
addAndMakeVisible (box);
|
||||
}
|
||||
|
||||
void paint (Graphics&) override {}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto area = getLocalBounds();
|
||||
area.removeFromLeft (8);
|
||||
box.setBounds (area.reduced (0, 10));
|
||||
}
|
||||
|
||||
private:
|
||||
void handleNewParameterValue() override
|
||||
{
|
||||
auto index = parameterValues.indexOf (getParameter().getCurrentValueAsText());
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
// The parameter is producing some unexpected text, so we'll do
|
||||
// some linear interpolation.
|
||||
index = roundToInt (getParameter().getValue() * (parameterValues.size() - 1));
|
||||
}
|
||||
|
||||
box.setSelectedItemIndex (index);
|
||||
}
|
||||
|
||||
void boxChanged()
|
||||
{
|
||||
if (getParameter().getCurrentValueAsText() != box.getText())
|
||||
{
|
||||
getParameter().beginChangeGesture();
|
||||
|
||||
// When a parameter provides a list of strings we must set its
|
||||
// value using those strings, rather than a float, because VSTs can
|
||||
// have uneven spacing between the different allowed values.
|
||||
getParameter().setValueNotifyingHost (getParameter().getValueForText (box.getText()));
|
||||
|
||||
getParameter().endChangeGesture();
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox box;
|
||||
const StringArray parameterValues;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChoiceParameterComponent)
|
||||
};
|
||||
|
||||
class SliderParameterComponent final : public Component,
|
||||
private ParameterListener
|
||||
{
|
||||
public:
|
||||
SliderParameterComponent (AudioProcessorParameter& param)
|
||||
: ParameterListener (param)
|
||||
{
|
||||
if (getParameter().getNumSteps() != AudioProcessor::getDefaultNumParameterSteps())
|
||||
slider.setRange (0.0, 1.0, 1.0 / (getParameter().getNumSteps() - 1.0));
|
||||
else
|
||||
slider.setRange (0.0, 1.0);
|
||||
|
||||
slider.setScrollWheelEnabled (false);
|
||||
addAndMakeVisible (slider);
|
||||
|
||||
valueLabel.setColour (Label::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId));
|
||||
valueLabel.setBorderSize ({ 1, 1, 1, 1 });
|
||||
valueLabel.setJustificationType (Justification::centred);
|
||||
addAndMakeVisible (valueLabel);
|
||||
|
||||
// Set the initial value.
|
||||
handleNewParameterValue();
|
||||
|
||||
slider.onValueChange = [this]() { sliderValueChanged(); };
|
||||
slider.onDragStart = [this]() { sliderStartedDragging(); };
|
||||
slider.onDragEnd = [this]() { sliderStoppedDragging(); };
|
||||
}
|
||||
|
||||
void paint (Graphics&) override {}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto area = getLocalBounds().reduced (0, 10);
|
||||
|
||||
valueLabel.setBounds (area.removeFromRight (80));
|
||||
|
||||
area.removeFromLeft (6);
|
||||
slider.setBounds (area);
|
||||
}
|
||||
|
||||
private:
|
||||
void updateTextDisplay()
|
||||
{
|
||||
valueLabel.setText (getParameter().getCurrentValueAsText(), dontSendNotification);
|
||||
}
|
||||
|
||||
void handleNewParameterValue() override
|
||||
{
|
||||
if (! isDragging)
|
||||
{
|
||||
slider.setValue (getParameter().getValue(), dontSendNotification);
|
||||
updateTextDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
void sliderValueChanged()
|
||||
{
|
||||
auto newVal = (float) slider.getValue();
|
||||
|
||||
if (getParameter().getValue() != newVal)
|
||||
{
|
||||
if (! isDragging)
|
||||
getParameter().beginChangeGesture();
|
||||
|
||||
getParameter().setValueNotifyingHost ((float) slider.getValue());
|
||||
updateTextDisplay();
|
||||
|
||||
if (! isDragging)
|
||||
getParameter().endChangeGesture();
|
||||
}
|
||||
}
|
||||
|
||||
void sliderStartedDragging()
|
||||
{
|
||||
isDragging = true;
|
||||
getParameter().beginChangeGesture();
|
||||
}
|
||||
|
||||
void sliderStoppedDragging()
|
||||
{
|
||||
isDragging = false;
|
||||
getParameter().endChangeGesture();
|
||||
}
|
||||
|
||||
Slider slider { Slider::LinearHorizontal, Slider::TextEntryBoxPosition::NoTextBox };
|
||||
Label valueLabel;
|
||||
bool isDragging = false;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderParameterComponent)
|
||||
};
|
||||
|
||||
class ParameterDisplayComponent : public Component
|
||||
{
|
||||
public:
|
||||
ParameterDisplayComponent (AudioProcessorParameter& param)
|
||||
: parameter (param)
|
||||
{
|
||||
parameterName.setText (parameter.getName (128), dontSendNotification);
|
||||
parameterName.setJustificationType (Justification::centredRight);
|
||||
addAndMakeVisible (parameterName);
|
||||
|
||||
parameterLabel.setText (parameter.getLabel(), dontSendNotification);
|
||||
addAndMakeVisible (parameterLabel);
|
||||
|
||||
if (param.isBoolean())
|
||||
{
|
||||
// The AU, AUv3 and VST (only via a .vstxml file) SDKs support
|
||||
// marking a parameter as boolean. If you want consistency across
|
||||
// all formats then it might be best to use a
|
||||
// SwitchParameterComponent instead.
|
||||
parameterComp.reset (new BooleanParameterComponent (param));
|
||||
}
|
||||
else if (param.getNumSteps() == 2)
|
||||
{
|
||||
// Most hosts display any parameter with just two steps as a switch.
|
||||
parameterComp.reset (new SwitchParameterComponent (param));
|
||||
}
|
||||
else if (! param.getAllValueStrings().isEmpty())
|
||||
{
|
||||
// If we have a list of strings to represent the different states a
|
||||
// parameter can be in then we should present a dropdown allowing a
|
||||
// user to pick one of them.
|
||||
parameterComp.reset (new ChoiceParameterComponent (param));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Everything else can be represented as a slider.
|
||||
parameterComp.reset (new SliderParameterComponent (param));
|
||||
}
|
||||
|
||||
addAndMakeVisible (parameterComp);
|
||||
|
||||
setSize (400, 40);
|
||||
}
|
||||
|
||||
void paint (Graphics&) override {}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto area = getLocalBounds();
|
||||
|
||||
parameterName.setBounds (area.removeFromLeft (100));
|
||||
parameterLabel.setBounds (area.removeFromRight (50));
|
||||
parameterComp->setBounds (area);
|
||||
}
|
||||
|
||||
private:
|
||||
AudioProcessorParameter& parameter;
|
||||
Label parameterName, parameterLabel;
|
||||
ScopedPointer<Component> parameterComp;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterDisplayComponent)
|
||||
};
|
||||
|
||||
class ParametersPanel : public Component
|
||||
{
|
||||
public:
|
||||
ParametersPanel (const OwnedArray<AudioProcessorParameter>& parameters)
|
||||
{
|
||||
for (auto* param : parameters)
|
||||
if (param->isAutomatable())
|
||||
addAndMakeVisible (paramComponents.add (new ParameterDisplayComponent (*param)));
|
||||
|
||||
if (auto* comp = paramComponents[0])
|
||||
setSize (comp->getWidth(), comp->getHeight() * paramComponents.size());
|
||||
else
|
||||
setSize (400, 100);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto area = getLocalBounds();
|
||||
|
||||
for (auto* comp : paramComponents)
|
||||
comp->setBounds (area.removeFromTop (comp->getHeight()));
|
||||
}
|
||||
|
||||
private:
|
||||
OwnedArray<ParameterDisplayComponent> paramComponents;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParametersPanel)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const p)
|
||||
|
|
@ -151,42 +653,30 @@ GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const
|
|||
jassert (p != nullptr);
|
||||
setOpaque (true);
|
||||
|
||||
addAndMakeVisible (panel);
|
||||
auto& parameters = p->getParameters();
|
||||
|
||||
Array<PropertyComponent*> params;
|
||||
if (parameters.size() == p->getNumParameters())
|
||||
view.setViewedComponent (new ParametersPanel (parameters));
|
||||
else
|
||||
view.setViewedComponent (new LegacyParametersPanel (p));
|
||||
|
||||
auto numParams = p->getNumParameters();
|
||||
int totalHeight = 0;
|
||||
addAndMakeVisible (view);
|
||||
|
||||
for (int i = 0; i < numParams; ++i)
|
||||
{
|
||||
auto name = p->getParameterName (i);
|
||||
|
||||
if (name.trim().isEmpty())
|
||||
name = "Unnamed";
|
||||
|
||||
auto* pc = new ProcessorParameterPropertyComp (name, *p, i);
|
||||
params.add (pc);
|
||||
totalHeight += pc->getPreferredHeight();
|
||||
}
|
||||
|
||||
panel.addProperties (params);
|
||||
|
||||
setSize (400, jlimit (25, 400, totalHeight));
|
||||
view.setScrollBarsShown (true, false);
|
||||
setSize (view.getViewedComponent()->getWidth() + view.getVerticalScrollBar().getWidth(),
|
||||
jmin (view.getViewedComponent()->getHeight(), 400));
|
||||
}
|
||||
|
||||
GenericAudioProcessorEditor::~GenericAudioProcessorEditor()
|
||||
{
|
||||
}
|
||||
GenericAudioProcessorEditor::~GenericAudioProcessorEditor() {}
|
||||
|
||||
void GenericAudioProcessorEditor::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (Colours::white);
|
||||
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
|
||||
}
|
||||
|
||||
void GenericAudioProcessorEditor::resized()
|
||||
{
|
||||
panel.setBounds (getLocalBounds());
|
||||
view.setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace juce
|
|||
//==============================================================================
|
||||
/**
|
||||
A type of UI component that displays the parameters of an AudioProcessor as
|
||||
a simple list of sliders.
|
||||
a simple list of sliders, combo boxes and switches.
|
||||
|
||||
This can be used for showing an editor for a processor that doesn't supply
|
||||
its own custom editor.
|
||||
|
|
@ -50,7 +50,7 @@ public:
|
|||
|
||||
private:
|
||||
//==============================================================================
|
||||
PropertyPanel panel;
|
||||
Viewport view;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericAudioProcessorEditor)
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue