| @@ -307,6 +307,7 @@ struct GraphEditorPanel::FilterComponent : public Component | |||
| case 12: showWindow (PluginWindow::Type::generic); break; | |||
| case 20: showWindow (PluginWindow::Type::audioIO); break; | |||
| case 21: testStateSaveLoad(); break; | |||
| default: break; | |||
| } | |||
| } | |||
| @@ -34,6 +34,8 @@ | |||
| #include "../utility/juce_WindowsHooks.h" | |||
| #include "../utility/juce_FakeMouseMoveGenerator.h" | |||
| #include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp" | |||
| #ifdef __clang__ | |||
| #pragma clang diagnostic push | |||
| #pragma clang diagnostic ignored "-Wnon-virtual-dtor" | |||
| @@ -635,6 +637,7 @@ namespace AAXClasses | |||
| AAX_Result Uninitialize() override | |||
| { | |||
| cancelPendingUpdate(); | |||
| juceParameters.clear(); | |||
| if (isPrepared && pluginInstance != nullptr) | |||
| { | |||
| @@ -724,11 +727,11 @@ namespace AAXClasses | |||
| // * The preset is loaded in PT 10 using the AAX version. | |||
| // * The session is then saved, and closed. | |||
| // * The saved session is loaded, but acting as if the preset was never loaded. | |||
| auto numParameters = pluginInstance->getNumParameters(); | |||
| auto numParameters = juceParameters.getNumParameters(); | |||
| for (int i = 0; i < numParameters; ++i) | |||
| if (auto paramID = getAAXParamIDFromJuceIndex(i)) | |||
| SetParameterNormalizedValue (paramID, (double) pluginInstance->getParameter(i)); | |||
| SetParameterNormalizedValue (paramID, juceParameters.getParamForIndex (i)->getValue()); | |||
| return AAX_SUCCESS; | |||
| } | |||
| @@ -782,22 +785,20 @@ namespace AAXClasses | |||
| return AAX_SUCCESS; | |||
| } | |||
| void setAudioProcessorParameter (int index, float value) | |||
| void setAudioProcessorParameter (AAX_CParamID paramID, double value) | |||
| { | |||
| if (auto* param = pluginInstance->getParameters()[index]) | |||
| if (auto* param = getParameterFromID (paramID)) | |||
| { | |||
| if (value != param->getValue()) | |||
| auto newValue = static_cast<float> (value); | |||
| if (newValue != param->getValue()) | |||
| { | |||
| param->setValue (value); | |||
| param->setValue (newValue); | |||
| inParameterChangedCallback = true; | |||
| param->sendValueChangedMessageToListeners (value); | |||
| param->sendValueChangedMessageToListeners (newValue); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| pluginInstance->setParameter (index, value); | |||
| } | |||
| } | |||
| AAX_Result UpdateParameterNormalizedValue (AAX_CParamID paramID, double value, AAX_EUpdateSource source) override | |||
| @@ -805,7 +806,7 @@ namespace AAXClasses | |||
| auto result = AAX_CEffectParameters::UpdateParameterNormalizedValue (paramID, value, source); | |||
| if (! isBypassParam (paramID)) | |||
| setAudioProcessorParameter (getParamIndexFromID (paramID), (float) value); | |||
| setAudioProcessorParameter (paramID, value); | |||
| return result; | |||
| } | |||
| @@ -818,10 +819,13 @@ namespace AAXClasses | |||
| return AAX_SUCCESS; | |||
| } | |||
| if (AudioProcessorParameter* param = pluginInstance->getParameters() [getParamIndexFromID (paramID)]) | |||
| if (auto* param = getParameterFromID (paramID)) | |||
| { | |||
| *result = param->getValueForText (text.Get()); | |||
| return AAX_SUCCESS; | |||
| if (! LegacyAudioParameter::isLegacy (param)) | |||
| { | |||
| *result = param->getValueForText (text.Get()); | |||
| return AAX_SUCCESS; | |||
| } | |||
| } | |||
| return AAX_CEffectParameters::GetParameterValueFromString (paramID, result, text); | |||
| @@ -835,15 +839,8 @@ namespace AAXClasses | |||
| } | |||
| else | |||
| { | |||
| auto paramIndex = getParamIndexFromID (paramID); | |||
| juce::String text; | |||
| if (auto* param = pluginInstance->getParameters() [paramIndex]) | |||
| text = param->getText ((float) value, maxLen); | |||
| else | |||
| text = pluginInstance->getParameterText (paramIndex, maxLen); | |||
| result->Set (text.toRawUTF8()); | |||
| if (auto* param = getParameterFromID (paramID)) | |||
| result->Set (param->getText ((float) value, maxLen).toRawUTF8()); | |||
| } | |||
| return AAX_SUCCESS; | |||
| @@ -852,9 +849,14 @@ namespace AAXClasses | |||
| AAX_Result GetParameterNumberofSteps (AAX_CParamID paramID, int32_t* result) const | |||
| { | |||
| if (isBypassParam (paramID)) | |||
| { | |||
| *result = 2; | |||
| } | |||
| else | |||
| *result = pluginInstance->getParameterNumSteps (getParamIndexFromID (paramID)); | |||
| { | |||
| if (auto* param = getParameterFromID (paramID)) | |||
| *result = param->getNumSteps(); | |||
| } | |||
| return AAX_SUCCESS; | |||
| } | |||
| @@ -864,7 +866,11 @@ namespace AAXClasses | |||
| if (isBypassParam (paramID)) | |||
| return AAX_CEffectParameters::GetParameterNormalizedValue (paramID, result); | |||
| *result = pluginInstance->getParameter (getParamIndexFromID (paramID)); | |||
| if (auto* param = getParameterFromID (paramID)) | |||
| *result = (double) param->getValue(); | |||
| else | |||
| *result = 0.0; | |||
| return AAX_SUCCESS; | |||
| } | |||
| @@ -876,7 +882,7 @@ namespace AAXClasses | |||
| if (auto* p = mParameterManager.GetParameterByID (paramID)) | |||
| p->SetValueWithFloat ((float) newValue); | |||
| setAudioProcessorParameter (getParamIndexFromID (paramID), (float) newValue); | |||
| setAudioProcessorParameter (paramID, (float) newValue); | |||
| return AAX_SUCCESS; | |||
| } | |||
| @@ -886,13 +892,15 @@ namespace AAXClasses | |||
| if (isBypassParam (paramID)) | |||
| return AAX_CEffectParameters::SetParameterNormalizedRelative (paramID, newDeltaValue); | |||
| auto paramIndex = getParamIndexFromID (paramID); | |||
| auto newValue = pluginInstance->getParameter (paramIndex) + (float) newDeltaValue; | |||
| if (auto* param = getParameterFromID (paramID)) | |||
| { | |||
| auto newValue = param->getValue() + (float) newDeltaValue; | |||
| setAudioProcessorParameter (paramIndex, jlimit (0.0f, 1.0f, newValue)); | |||
| setAudioProcessorParameter (paramID, jlimit (0.0f, 1.0f, newValue)); | |||
| if (auto* p = mParameterManager.GetParameterByID (paramID)) | |||
| p->SetValueWithFloat (newValue); | |||
| if (auto* p = mParameterManager.GetParameterByID (paramID)) | |||
| p->SetValueWithFloat (newValue); | |||
| } | |||
| return AAX_SUCCESS; | |||
| } | |||
| @@ -900,11 +908,15 @@ namespace AAXClasses | |||
| AAX_Result GetParameterNameOfLength (AAX_CParamID paramID, AAX_IString* result, int32_t maxLen) const override | |||
| { | |||
| if (isBypassParam (paramID)) | |||
| { | |||
| result->Set (maxLen >= 13 ? "Master Bypass" | |||
| : (maxLen >= 8 ? "Mast Byp" | |||
| : (maxLen >= 6 ? "MstByp" : "MByp"))); | |||
| else | |||
| result->Set (pluginInstance->getParameterName (getParamIndexFromID (paramID), maxLen).toRawUTF8()); | |||
| } | |||
| else if (auto* param = getParameterFromID (paramID)) | |||
| { | |||
| result->Set (param->getName (maxLen).toRawUTF8()); | |||
| } | |||
| return AAX_SUCCESS; | |||
| } | |||
| @@ -913,8 +925,8 @@ namespace AAXClasses | |||
| { | |||
| if (isBypassParam (paramID)) | |||
| result->Set ("Master Bypass"); | |||
| else | |||
| result->Set (pluginInstance->getParameterName (getParamIndexFromID (paramID), 31).toRawUTF8()); | |||
| else if (auto* param = getParameterFromID (paramID)) | |||
| result->Set (param->getName (31).toRawUTF8()); | |||
| return AAX_SUCCESS; | |||
| } | |||
| @@ -923,7 +935,10 @@ namespace AAXClasses | |||
| { | |||
| if (! isBypassParam (paramID)) | |||
| { | |||
| *result = (double) pluginInstance->getParameterDefaultValue (getParamIndexFromID (paramID)); | |||
| if (auto* param = getParameterFromID (paramID)) | |||
| *result = (double) param->getDefaultValue(); | |||
| else | |||
| *result = 0.0; | |||
| jassert (*result >= 0 && *result <= 1.0f); | |||
| } | |||
| @@ -1014,13 +1029,13 @@ namespace AAXClasses | |||
| { | |||
| ++mNumPlugInChanges; | |||
| auto numParameters = pluginInstance->getNumParameters(); | |||
| auto numParameters = juceParameters.getNumParameters(); | |||
| for (int i = 0; i < numParameters; ++i) | |||
| { | |||
| if (auto* p = mParameterManager.GetParameterByID (getAAXParamIDFromJuceIndex (i))) | |||
| { | |||
| auto newName = processor->getParameterName (i, 31); | |||
| auto newName = juceParameters.getParamForIndex (i)->getName (31); | |||
| if (p->Name() != newName.toRawUTF8()) | |||
| p->SetName (AAX_CString (newName.toRawUTF8())); | |||
| @@ -1158,7 +1173,7 @@ namespace AAXClasses | |||
| if (meterBuffers != nullptr) | |||
| for (int i = 0; i < numMeters; ++i) | |||
| meterBuffers[i] = pluginInstance->getParameter (aaxMeters[i]); | |||
| meterBuffers[i] = aaxMeters[i]->getValue(); | |||
| } | |||
| } | |||
| @@ -1422,53 +1437,56 @@ namespace AAXClasses | |||
| void addAudioProcessorParameters() | |||
| { | |||
| auto& audioProcessor = getPluginInstance(); | |||
| auto numParameters = audioProcessor.getNumParameters(); | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| const bool usingManagedParameters = false; | |||
| const bool forceLegacyParamIDs = true; | |||
| #else | |||
| const bool usingManagedParameters = (audioProcessor.getParameters().size() == numParameters); | |||
| const bool forceLegacyParamIDs = false; | |||
| #endif | |||
| for (int parameterIndex = 0; parameterIndex < numParameters; ++parameterIndex) | |||
| juceParameters.update (audioProcessor, forceLegacyParamIDs); | |||
| int parameterIndex = 0; | |||
| for (auto* juceParam : juceParameters.params) | |||
| { | |||
| auto category = audioProcessor.getParameterCategory (parameterIndex); | |||
| auto category = juceParam->getCategory(); | |||
| auto paramID = juceParameters.getParamID (audioProcessor, parameterIndex) | |||
| .toRawUTF8(); | |||
| aaxParamIDs.add (usingManagedParameters ? audioProcessor.getParameterID (parameterIndex) | |||
| : String (parameterIndex)); | |||
| aaxParamIDs.add (paramID); | |||
| auto aaxParamID = aaxParamIDs.getReference (parameterIndex++).getCharPointer(); | |||
| auto paramID = aaxParamIDs.getReference (parameterIndex).getCharPointer(); | |||
| paramMap.set (AAXClasses::getAAXParamHash (paramID), parameterIndex); | |||
| paramMap.set (AAXClasses::getAAXParamHash (aaxParamID), juceParam); | |||
| // is this a meter? | |||
| if (((category & 0xffff0000) >> 16) == 2) | |||
| { | |||
| aaxMeters.add (parameterIndex); | |||
| aaxMeters.add (juceParam); | |||
| continue; | |||
| } | |||
| auto parameter = new AAX_CParameter<float> (paramID, | |||
| AAX_CString (audioProcessor.getParameterName (parameterIndex, 31).toRawUTF8()), | |||
| audioProcessor.getParameterDefaultValue (parameterIndex), | |||
| auto parameter = new AAX_CParameter<float> (aaxParamID, | |||
| AAX_CString (juceParam->getName (31).toRawUTF8()), | |||
| juceParam->getDefaultValue(), | |||
| AAX_CLinearTaperDelegate<float, 0>(), | |||
| AAX_CNumberDisplayDelegate<float, 3>(), | |||
| audioProcessor.isParameterAutomatable (parameterIndex)); | |||
| juceParam->isAutomatable()); | |||
| parameter->AddShortenedName (audioProcessor.getParameterName (parameterIndex, 4).toRawUTF8()); | |||
| parameter->AddShortenedName (juceParam->getName (4).toRawUTF8()); | |||
| auto parameterNumSteps = audioProcessor.getParameterNumSteps (parameterIndex); | |||
| auto parameterNumSteps = juceParam->getNumSteps(); | |||
| parameter->SetNumberOfSteps ((uint32_t) parameterNumSteps); | |||
| #if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| parameter->SetType (parameterNumSteps > 1000 ? AAX_eParameterType_Continuous | |||
| : AAX_eParameterType_Discrete); | |||
| #else | |||
| parameter->SetType (audioProcessor.isParameterDiscrete (parameterIndex) ? AAX_eParameterType_Discrete | |||
| : AAX_eParameterType_Continuous); | |||
| parameter->SetType (juceParam->isDiscrete() ? AAX_eParameterType_Discrete | |||
| : AAX_eParameterType_Continuous); | |||
| #endif | |||
| parameter->SetOrientation (audioProcessor.isParameterOrientationInverted (parameterIndex) | |||
| parameter->SetOrientation (juceParam->isOrientationInverted() | |||
| ? (AAX_eParameterOrientation_RightMinLeftMax | AAX_eParameterOrientation_TopMinBottomMax | |||
| | AAX_eParameterOrientation_RotarySingleDotMode | AAX_eParameterOrientation_RotaryRightMinLeftMax) | |||
| : (AAX_eParameterOrientation_LeftMinRightMax | AAX_eParameterOrientation_BottomMinTopMax | |||
| @@ -1696,7 +1714,10 @@ namespace AAXClasses | |||
| //============================================================================== | |||
| inline int getParamIndexFromID (AAX_CParamID paramID) const noexcept | |||
| { | |||
| return paramMap [AAXClasses::getAAXParamHash (paramID)]; | |||
| if (auto* param = getParameterFromID (paramID)) | |||
| return LegacyAudioParameter::getParamIndex (getPluginInstance(), param); | |||
| return -1; | |||
| } | |||
| inline AAX_CParamID getAAXParamIDFromJuceIndex (int index) const noexcept | |||
| @@ -1707,6 +1728,11 @@ namespace AAXClasses | |||
| return nullptr; | |||
| } | |||
| AudioProcessorParameter* getParameterFromID (AAX_CParamID paramID) const noexcept | |||
| { | |||
| return paramMap [AAXClasses::getAAXParamHash (paramID)]; | |||
| } | |||
| //============================================================================== | |||
| static AudioProcessor::BusesLayout getDefaultLayout (const AudioProcessor& p, bool enableAll) | |||
| { | |||
| @@ -1757,9 +1783,10 @@ namespace AAXClasses | |||
| Array<int> inputLayoutMap, outputLayoutMap; | |||
| Array<String> aaxParamIDs; | |||
| HashMap<int32, int> paramMap; | |||
| HashMap<int32, AudioProcessorParameter*> paramMap; | |||
| LegacyAudioParametersWrapper juceParameters; | |||
| Array<int> aaxMeters; | |||
| Array<AudioProcessorParameter*> aaxMeters; | |||
| struct ChunkMemoryBlock | |||
| { | |||
| @@ -1835,12 +1862,21 @@ namespace AAXClasses | |||
| //============================================================================== | |||
| static int addAAXMeters (AudioProcessor& p, AAX_IEffectDescriptor& descriptor) | |||
| { | |||
| const int n = p.getNumParameters(); | |||
| LegacyAudioParametersWrapper params; | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| const bool forceLegacyParamIDs = true; | |||
| #else | |||
| const bool forceLegacyParamIDs = false; | |||
| #endif | |||
| params.update (p, forceLegacyParamIDs); | |||
| int meterIdx = 0; | |||
| for (int i = 0; i < n; ++i) | |||
| for (auto* param : params.params) | |||
| { | |||
| auto category = p.getParameterCategory (i); | |||
| auto category = param->getCategory(); | |||
| // is this a meter? | |||
| if (((category & 0xffff0000) >> 16) == 2) | |||
| @@ -1851,7 +1887,7 @@ namespace AAXClasses | |||
| meterProperties->AddProperty (AAX_eProperty_Meter_Orientation, AAX_eMeterOrientation_TopRight); | |||
| descriptor.AddMeterDescription ('Metr' + static_cast<AAX_CTypeID> (meterIdx++), | |||
| p.getParameterName (i).toRawUTF8(), meterProperties); | |||
| param->getName (1024).toRawUTF8(), meterProperties); | |||
| } | |||
| } | |||
| } | |||
| @@ -81,6 +81,7 @@ | |||
| #include "../utility/juce_CarbonVisibility.h" | |||
| #include "../../juce_audio_basics/native/juce_mac_CoreAudioLayouts.h" | |||
| #include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp" | |||
| #include "../../juce_audio_processors/format_types/juce_AU_Shared.h" | |||
| //============================================================================== | |||
| @@ -527,15 +528,18 @@ public: | |||
| { | |||
| if (juceFilter != nullptr) | |||
| { | |||
| const int i = getJuceIndexForAUParameterID (pv->inParamID); | |||
| const String text (String::fromCFString (pv->inString)); | |||
| if (auto* param = getParameterForAUParameterID (pv->inParamID)) | |||
| { | |||
| const String text (String::fromCFString (pv->inString)); | |||
| if (AudioProcessorParameter* param = juceFilter->getParameters()[i]) | |||
| pv->outValue = param->getValueForText (text) * getMaximumParameterValue (i); | |||
| else | |||
| pv->outValue = text.getFloatValue(); | |||
| if (LegacyAudioParameter::isLegacy (param)) | |||
| pv->outValue = text.getFloatValue(); | |||
| else | |||
| pv->outValue = param->getValueForText (text) * getMaximumParameterValue (param); | |||
| return noErr; | |||
| return noErr; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -547,18 +551,20 @@ public: | |||
| { | |||
| if (juceFilter != nullptr) | |||
| { | |||
| const int i = getJuceIndexForAUParameterID (pv->inParamID); | |||
| const float value = (float) *(pv->inValue); | |||
| String text; | |||
| if (auto* param = getParameterForAUParameterID (pv->inParamID)) | |||
| { | |||
| const float value = (float) *(pv->inValue); | |||
| String text; | |||
| if (AudioProcessorParameter* param = juceFilter->getParameters()[i]) | |||
| text = param->getText (value / getMaximumParameterValue (i), 0); | |||
| else | |||
| text = String (value); | |||
| if (LegacyAudioParameter::isLegacy (param)) | |||
| text = String (value); | |||
| else | |||
| text = param->getText (value / getMaximumParameterValue (param), 0); | |||
| pv->outString = text.toCFString(); | |||
| pv->outString = text.toCFString(); | |||
| return noErr; | |||
| return noErr; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -793,7 +799,7 @@ public: | |||
| if (inLayout == nullptr) | |||
| return kAudioUnitErr_InvalidPropertyValue; | |||
| if (const AUIOElement* ioElement = GetIOElement (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, element)) | |||
| if (const AUIOElement* ioElement = GetIOElement (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, element)) | |||
| { | |||
| const AudioChannelSet newChannelSet = CoreAudioLayouts::fromCoreAudio (*inLayout); | |||
| const int currentNumChannels = static_cast<int> (ioElement->GetStreamFormat().NumberChannels()); | |||
| @@ -825,82 +831,73 @@ public: | |||
| //============================================================================== | |||
| // When parameters are discrete we need to use integer values. | |||
| float getMaximumParameterValue (int parameterIndex) | |||
| float getMaximumParameterValue (AudioProcessorParameter* param) | |||
| { | |||
| #if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| ignoreUnused (parameterIndex); | |||
| return 1.0f; | |||
| #else | |||
| return juceFilter->isParameterDiscrete (parameterIndex) ? (float) (juceFilter->getParameterNumSteps (parameterIndex) - 1) : 1.0f; | |||
| #endif | |||
| return param->isDiscrete() && (! forceUseLegacyParamIDs) ? (float) (param->getNumSteps() - 1) : 1.0f; | |||
| } | |||
| ComponentResult GetParameterInfo (AudioUnitScope inScope, | |||
| AudioUnitParameterID inParameterID, | |||
| AudioUnitParameterInfo& outParameterInfo) override | |||
| { | |||
| const int index = getJuceIndexForAUParameterID (inParameterID); | |||
| if (inScope == kAudioUnitScope_Global | |||
| && juceFilter != nullptr | |||
| && isPositiveAndBelow (index, juceFilter->getNumParameters())) | |||
| if (inScope == kAudioUnitScope_Global && juceFilter != nullptr) | |||
| { | |||
| outParameterInfo.unit = kAudioUnitParameterUnit_Generic; | |||
| outParameterInfo.flags = (UInt32) (kAudioUnitParameterFlag_IsWritable | |||
| | kAudioUnitParameterFlag_IsReadable | |||
| | kAudioUnitParameterFlag_HasCFNameString | |||
| | kAudioUnitParameterFlag_ValuesHaveStrings); | |||
| if (auto* param = getParameterForAUParameterID (inParameterID)) | |||
| { | |||
| outParameterInfo.unit = kAudioUnitParameterUnit_Generic; | |||
| outParameterInfo.flags = (UInt32) (kAudioUnitParameterFlag_IsWritable | |||
| | kAudioUnitParameterFlag_IsReadable | |||
| | kAudioUnitParameterFlag_HasCFNameString | |||
| | kAudioUnitParameterFlag_ValuesHaveStrings); | |||
| #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| outParameterInfo.flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution; | |||
| #endif | |||
| #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| outParameterInfo.flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution; | |||
| #endif | |||
| const String name (juceFilter->getParameterName (index)); | |||
| const String name = param->getName (1024); | |||
| // Set whether the param is automatable (unnamed parameters aren't allowed to be automated) | |||
| if (name.isEmpty() || ! juceFilter->isParameterAutomatable (index)) | |||
| outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime; | |||
| // Set whether the param is automatable (unnamed parameters aren't allowed to be automated) | |||
| if (name.isEmpty() || ! param->isAutomatable()) | |||
| outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime; | |||
| const bool isParameterDiscrete = juceFilter->isParameterDiscrete (index); | |||
| const bool isParameterDiscrete = param->isDiscrete(); | |||
| if (! isParameterDiscrete) | |||
| outParameterInfo.flags |= kAudioUnitParameterFlag_CanRamp; | |||
| if (! isParameterDiscrete) | |||
| outParameterInfo.flags |= kAudioUnitParameterFlag_CanRamp; | |||
| if (juceFilter->isMetaParameter (index)) | |||
| outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta; | |||
| if (param->isMetaParameter()) | |||
| outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta; | |||
| // Is this a meter? | |||
| if (((juceFilter->getParameterCategory (index) & 0xffff0000) >> 16) == 2) | |||
| { | |||
| outParameterInfo.flags &= ~kAudioUnitParameterFlag_IsWritable; | |||
| outParameterInfo.flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic; | |||
| outParameterInfo.unit = kAudioUnitParameterUnit_LinearGain; | |||
| } | |||
| else | |||
| { | |||
| #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| if (isParameterDiscrete) | |||
| // Is this a meter? | |||
| if (((param->getCategory() & 0xffff0000) >> 16) == 2) | |||
| { | |||
| outParameterInfo.unit = kAudioUnitParameterUnit_Indexed; | |||
| if (auto* param = juceFilter->getParameters()[index]) | |||
| outParameterInfo.flags &= ~kAudioUnitParameterFlag_IsWritable; | |||
| outParameterInfo.flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic; | |||
| outParameterInfo.unit = kAudioUnitParameterUnit_LinearGain; | |||
| } | |||
| else | |||
| { | |||
| #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| if (isParameterDiscrete) | |||
| { | |||
| outParameterInfo.unit = kAudioUnitParameterUnit_Indexed; | |||
| if (param->isBoolean()) | |||
| outParameterInfo.unit = kAudioUnitParameterUnit_Boolean; | |||
| } | |||
| #endif | |||
| } | |||
| #endif | |||
| } | |||
| MusicDeviceBase::FillInParameterName (outParameterInfo, name.toCFString(), true); | |||
| MusicDeviceBase::FillInParameterName (outParameterInfo, name.toCFString(), true); | |||
| outParameterInfo.minValue = 0.0f; | |||
| outParameterInfo.maxValue = getMaximumParameterValue (index); | |||
| outParameterInfo.defaultValue = juceFilter->getParameterDefaultValue (index) * getMaximumParameterValue (index); | |||
| jassert (outParameterInfo.defaultValue >= outParameterInfo.minValue | |||
| && outParameterInfo.defaultValue <= outParameterInfo.maxValue); | |||
| outParameterInfo.minValue = 0.0f; | |||
| outParameterInfo.maxValue = getMaximumParameterValue (param); | |||
| outParameterInfo.defaultValue = param->getDefaultValue() * getMaximumParameterValue (param); | |||
| jassert (outParameterInfo.defaultValue >= outParameterInfo.minValue | |||
| && outParameterInfo.defaultValue <= outParameterInfo.maxValue); | |||
| return noErr; | |||
| return noErr; | |||
| } | |||
| } | |||
| return kAudioUnitErr_InvalidParameter; | |||
| @@ -913,21 +910,24 @@ public: | |||
| if (outStrings == nullptr) | |||
| return noErr; | |||
| const int index = getJuceIndexForAUParameterID (inParameterID); | |||
| if (inScope == kAudioUnitScope_Global | |||
| && juceFilter != nullptr | |||
| && isPositiveAndBelow (index, juceFilter->getNumParameters()) | |||
| && juceFilter->isParameterDiscrete (index)) | |||
| if (inScope == kAudioUnitScope_Global && juceFilter != nullptr) | |||
| { | |||
| if (auto* valueStrings = parameterValueStringArrays[index]) | |||
| if (auto* param = getParameterForAUParameterID (inParameterID)) | |||
| { | |||
| *outStrings = CFArrayCreate (NULL, | |||
| (const void **) valueStrings->getRawDataPointer(), | |||
| valueStrings->size(), | |||
| NULL); | |||
| if (param->isDiscrete()) | |||
| { | |||
| auto index = LegacyAudioParameter::getParamIndex (*juceFilter, param); | |||
| return noErr; | |||
| if (auto* valueStrings = parameterValueStringArrays[index]) | |||
| { | |||
| *outStrings = CFArrayCreate (NULL, | |||
| (const void **) valueStrings->getRawDataPointer(), | |||
| valueStrings->size(), | |||
| NULL); | |||
| return noErr; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -941,11 +941,13 @@ public: | |||
| { | |||
| if (inScope == kAudioUnitScope_Global && juceFilter != nullptr) | |||
| { | |||
| const auto index = getJuceIndexForAUParameterID (inID); | |||
| const auto normValue = juceFilter->getParameter (index); | |||
| if (auto* param = getParameterForAUParameterID (inID)) | |||
| { | |||
| const auto normValue = param->getValue(); | |||
| outValue = normValue * getMaximumParameterValue (index); | |||
| return noErr; | |||
| outValue = normValue * getMaximumParameterValue (param); | |||
| return noErr; | |||
| } | |||
| } | |||
| return MusicDeviceBase::GetParameter (inID, inScope, inElement, outValue); | |||
| @@ -959,22 +961,17 @@ public: | |||
| { | |||
| if (inScope == kAudioUnitScope_Global && juceFilter != nullptr) | |||
| { | |||
| auto index = getJuceIndexForAUParameterID (inID); | |||
| auto value = inValue / getMaximumParameterValue (index); | |||
| if (auto* param = juceFilter->getParameters()[index]) | |||
| if (auto* param = getParameterForAUParameterID (inID)) | |||
| { | |||
| auto value = inValue / getMaximumParameterValue (param); | |||
| param->setValue (value); | |||
| inParameterChangedCallback = true; | |||
| param->sendValueChangedMessageToListeners (value); | |||
| } | |||
| else | |||
| { | |||
| juceFilter->setParameter (index, value); | |||
| } | |||
| return noErr; | |||
| return noErr; | |||
| } | |||
| } | |||
| return MusicDeviceBase::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames); | |||
| @@ -1660,12 +1657,17 @@ private: | |||
| bool prepared, isBypassed; | |||
| //============================================================================== | |||
| #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| bool usingManagedParameter; | |||
| Array<AudioUnitParameterID> auParamIDs; | |||
| HashMap<int32, int> paramMap; | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| static constexpr bool forceUseLegacyParamIDs = true; | |||
| #else | |||
| static constexpr bool forceUseLegacyParamIDs = false; | |||
| #endif | |||
| //============================================================================== | |||
| LegacyAudioParametersWrapper juceParameters; | |||
| HashMap<int32, AudioProcessorParameter*> paramMap; | |||
| Array<AudioUnitParameterID> auParamIDs; | |||
| //============================================================================== | |||
| AudioUnitEvent auEvent; | |||
| mutable Array<AUPreset> presetsArray; | |||
| @@ -1814,109 +1816,95 @@ private: | |||
| //============================================================================== | |||
| void addParameters() | |||
| { | |||
| // check if all parameters are managed? | |||
| const int numParams = juceFilter->getNumParameters(); | |||
| juceParameters.update (*juceFilter, forceUseLegacyParamIDs); | |||
| #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| usingManagedParameter = (juceFilter->getParameters().size() == numParams); | |||
| // check if all parameters are managed? | |||
| const int numParams = juceParameters.getNumParameters(); | |||
| if (usingManagedParameter) | |||
| for (auto* param : juceParameters.params) | |||
| { | |||
| for (int i = 0; i < numParams; ++i) | |||
| { | |||
| const AudioUnitParameterID auParamID = generateAUParameterIDForIndex (i); | |||
| const AudioUnitParameterID auParamID = generateAUParameterID (param); | |||
| // Consider yourself very unlucky if you hit this assertion. The hash code of your | |||
| // parameter ids are not unique. | |||
| jassert (! paramMap.contains (static_cast<int32> (auParamID))); | |||
| // Consider yourself very unlucky if you hit this assertion. The hash code of your | |||
| // parameter ids are not unique. | |||
| jassert (! paramMap.contains (static_cast<int32> (auParamID))); | |||
| auParamIDs.add (auParamID); | |||
| paramMap.set (static_cast<int32> (auParamID), i); | |||
| auParamIDs.add (auParamID); | |||
| paramMap.set (static_cast<int32> (auParamID), param); | |||
| Globals()->SetParameter (auParamID, juceFilter->getParameter (i)); | |||
| } | |||
| if (juceParameters.isUsingManagedParameters()) | |||
| Globals()->SetParameter (auParamID, param->getValue()); | |||
| } | |||
| else | |||
| #endif | |||
| { | |||
| if (! juceParameters.isUsingManagedParameters()) | |||
| Globals()->UseIndexedParameters (numParams); | |||
| } | |||
| #if JUCE_DEBUG | |||
| // Some hosts can't handle the huge numbers of discrete parameter values created when | |||
| // using the default number of steps. | |||
| for (auto* param : juceFilter->getParameters()) | |||
| for (auto* param : juceParameters.params) | |||
| if (param->isDiscrete()) | |||
| jassert (param->getNumSteps() != AudioProcessor::getDefaultNumParameterSteps()); | |||
| #endif | |||
| parameterValueStringArrays.ensureStorageAllocated (numParams); | |||
| for (int index = 0; index < numParams; ++index) | |||
| for (auto* param : juceParameters.params) | |||
| { | |||
| OwnedArray<const __CFString>* stringValues = nullptr; | |||
| #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| if (auto* param = juceFilter->getParameters()[index]) | |||
| auto initialValue = param->getValue(); | |||
| if (param->isDiscrete() && (! forceUseLegacyParamIDs)) | |||
| { | |||
| if (param->isDiscrete()) | |||
| { | |||
| const auto numSteps = param->getNumSteps(); | |||
| stringValues = new OwnedArray<const __CFString>(); | |||
| stringValues->ensureStorageAllocated (numSteps); | |||
| const auto numSteps = param->getNumSteps(); | |||
| stringValues = new OwnedArray<const __CFString>(); | |||
| stringValues->ensureStorageAllocated (numSteps); | |||
| const auto maxValue = getMaximumParameterValue (index); | |||
| const auto maxValue = getMaximumParameterValue (param); | |||
| for (int i = 0; i < numSteps; ++i) | |||
| stringValues->add (CFStringCreateCopy (nullptr, (param->getText ((float) i / maxValue, 0)).toCFString())); | |||
| for (int i = 0; i < numSteps; ++i) | |||
| { | |||
| auto value = (float) i / maxValue; | |||
| // Once legacy parameters are deprecated this can be replaced by getText | |||
| param->setValue (value); | |||
| stringValues->add (CFStringCreateCopy (nullptr, (param->getCurrentValueAsText().toCFString()))); | |||
| } | |||
| } | |||
| #endif | |||
| param->setValue (initialValue); | |||
| parameterValueStringArrays.add (stringValues); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| inline AudioUnitParameterID getAUParameterIDForIndex (int paramIndex) const noexcept { return static_cast<AudioUnitParameterID> (paramIndex); } | |||
| inline int getJuceIndexForAUParameterID (AudioUnitParameterID address) const noexcept { return static_cast<int> (address); } | |||
| #else | |||
| AudioUnitParameterID generateAUParameterIDForIndex (int paramIndex) const | |||
| AudioUnitParameterID generateAUParameterID (AudioProcessorParameter* param) const | |||
| { | |||
| const int n = juceFilter->getNumParameters(); | |||
| if (isPositiveAndBelow (paramIndex, n)) | |||
| { | |||
| const String& juceParamID = juceFilter->getParameterID (paramIndex); | |||
| AudioUnitParameterID paramHash = static_cast<AudioUnitParameterID> (juceParamID.hashCode()); | |||
| #if JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| // studio one doesn't like negative parameters | |||
| paramHash &= ~(1 << (sizeof (AudioUnitParameterID) * 8 - 1)); | |||
| #endif | |||
| const String& juceParamID = LegacyAudioParameter::getParamID (param, forceUseLegacyParamIDs); | |||
| AudioUnitParameterID paramHash = static_cast<AudioUnitParameterID> (juceParamID.hashCode()); | |||
| return usingManagedParameter ? paramHash | |||
| : static_cast<AudioUnitParameterID> (juceParamID.getIntValue()); | |||
| } | |||
| #if JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| // studio one doesn't like negative parameters | |||
| paramHash &= ~(1 << (sizeof (AudioUnitParameterID) * 8 - 1)); | |||
| #endif | |||
| return static_cast<AudioUnitParameterID> (-1); | |||
| return juceParameters.isUsingManagedParameters() ? paramHash | |||
| : static_cast<AudioUnitParameterID> (juceParamID.getIntValue()); | |||
| } | |||
| inline AudioUnitParameterID getAUParameterIDForIndex (int paramIndex) const noexcept | |||
| { | |||
| return usingManagedParameter ? auParamIDs.getReference (paramIndex) | |||
| : static_cast<AudioUnitParameterID> (paramIndex); | |||
| return juceParameters.isUsingManagedParameters() ? auParamIDs.getReference (paramIndex) | |||
| : static_cast<AudioUnitParameterID> (paramIndex); | |||
| } | |||
| inline int getJuceIndexForAUParameterID (AudioUnitParameterID address) const noexcept | |||
| AudioProcessorParameter* getParameterForAUParameterID (AudioUnitParameterID address) const noexcept | |||
| { | |||
| return usingManagedParameter ? paramMap[static_cast<int32> (address)] | |||
| : static_cast<int> (address); | |||
| return paramMap[static_cast<int32> (address)]; | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| OSStatus syncAudioUnitWithProcessor() | |||
| @@ -1934,7 +1922,7 @@ private: | |||
| addSupportedLayoutTags(); | |||
| for (int i = 0; i < enabledInputs; ++i) | |||
| if ((err = syncAudioUnitWithChannelSet (true, i, juceFilter->getChannelLayoutOfBus (true, i))) != noErr) return err; | |||
| if ((err = syncAudioUnitWithChannelSet (true, i, juceFilter->getChannelLayoutOfBus (true, i))) != noErr) return err; | |||
| for (int i = 0; i < enabledOutputs; ++i) | |||
| if ((err = syncAudioUnitWithChannelSet (false, i, juceFilter->getChannelLayoutOfBus (false, i))) != noErr) return err; | |||
| @@ -64,6 +64,7 @@ | |||
| #include "../../juce_graphics/native/juce_mac_CoreGraphicsHelpers.h" | |||
| #include "../../juce_audio_basics/native/juce_mac_CoreAudioLayouts.h" | |||
| #include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp" | |||
| #include "../../juce_audio_processors/format_types/juce_AU_Shared.h" | |||
| #define JUCE_VIEWCONTROLLER_OBJC_NAME(x) JUCE_JOIN_MACRO (x, FactoryAUv3) | |||
| @@ -909,7 +910,7 @@ public: | |||
| return; | |||
| } | |||
| if (isPositiveAndBelow (idx, getAudioProcessor().getNumParameters())) | |||
| if (isPositiveAndBelow (idx, juceParameters.getNumParameters())) | |||
| { | |||
| if (AUParameter* param = [paramTree parameterWithAddress: getAUParameterAddressForIndex (idx)]) | |||
| { | |||
| @@ -1121,14 +1122,13 @@ private: | |||
| } | |||
| // When parameters are discrete we need to use integer values. | |||
| float getMaximumParameterValue (int parameterIndex) | |||
| float getMaximumParameterValue (AudioProcessorParameter* juceParam) | |||
| { | |||
| #if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| ignoreUnused (parameterIndex); | |||
| ignoreUnused (juceParam); | |||
| return 1.0f; | |||
| #else | |||
| auto& processor = getAudioProcessor(); | |||
| return processor.isParameterDiscrete (parameterIndex) ? (float) (processor.getParameterNumSteps (parameterIndex) - 1) : 1.0f; | |||
| return juceParam->isDiscrete() ? (float) (juceParam->getNumSteps() - 1) : 1.0f; | |||
| #endif | |||
| } | |||
| @@ -1139,17 +1139,16 @@ private: | |||
| overviewParams = [[NSMutableArray<NSNumber*> alloc] init]; | |||
| auto& processor = getAudioProcessor(); | |||
| const int n = processor.getNumParameters(); | |||
| juceParameters.update (processor, forceLegacyParamIDs); | |||
| #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| // check if all parameters are managed? | |||
| usingManagedParameter = (processor.getParameters().size() == processor.getNumParameters()); | |||
| #endif | |||
| const int n = juceParameters.getNumParameters(); | |||
| for (int idx = 0; idx < n; ++idx) | |||
| { | |||
| auto* juceParam = juceParameters.getParamForIndex (idx); | |||
| const String identifier (idx); | |||
| const String name = processor.getParameterName (idx); | |||
| const String name = juceParam->getName (512); | |||
| AudioUnitParameterUnit unit = kAudioUnitParameterUnit_Generic; | |||
| AudioUnitParameterOptions flags = (UInt32) (kAudioUnitParameterFlag_IsWritable | |||
| @@ -1157,27 +1156,26 @@ private: | |||
| | kAudioUnitParameterFlag_HasCFNameString | |||
| | kAudioUnitParameterFlag_ValuesHaveStrings); | |||
| #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution; | |||
| #endif | |||
| if (! forceLegacyParamIDs) | |||
| flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution; | |||
| // set whether the param is automatable (unnamed parameters aren't allowed to be automated) | |||
| if (name.isEmpty() || ! processor.isParameterAutomatable (idx)) | |||
| if (name.isEmpty() || ! juceParam->isAutomatable()) | |||
| flags |= kAudioUnitParameterFlag_NonRealTime; | |||
| const bool isParameterDiscrete = processor.isParameterDiscrete (idx); | |||
| const bool isParameterDiscrete = juceParam->isDiscrete(); | |||
| if (! isParameterDiscrete) | |||
| flags |= kAudioUnitParameterFlag_CanRamp; | |||
| if (processor.isMetaParameter (idx)) | |||
| if (juceParam->isMetaParameter()) | |||
| flags |= kAudioUnitParameterFlag_IsGlobalMeta; | |||
| auto deleter = [](NSMutableArray* arr) { [arr release]; }; | |||
| std::unique_ptr<NSMutableArray, decltype (deleter)> valueStrings (nullptr, deleter); | |||
| // is this a meter? | |||
| if (((processor.getParameterCategory (idx) & 0xffff0000) >> 16) == 2) | |||
| if (((juceParam->getCategory() & 0xffff0000) >> 16) == 2) | |||
| { | |||
| flags &= ~kAudioUnitParameterFlag_IsWritable; | |||
| flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic; | |||
| @@ -1185,14 +1183,13 @@ private: | |||
| } | |||
| else | |||
| { | |||
| #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| if (auto* param = processor.getParameters()[idx]) | |||
| if (! forceLegacyParamIDs) | |||
| { | |||
| if (param->isDiscrete()) | |||
| if (juceParam->isDiscrete()) | |||
| { | |||
| unit = param->isBoolean() ? kAudioUnitParameterUnit_Boolean : kAudioUnitParameterUnit_Indexed; | |||
| auto maxValue = getMaximumParameterValue (idx); | |||
| auto numSteps = param->getNumSteps(); | |||
| unit = juceParam->isBoolean() ? kAudioUnitParameterUnit_Boolean : kAudioUnitParameterUnit_Indexed; | |||
| auto maxValue = getMaximumParameterValue (juceParam); | |||
| auto numSteps = juceParam->getNumSteps(); | |||
| // Some hosts can't handle the huge numbers of discrete parameter values created when | |||
| // using the default number of steps. | |||
| @@ -1201,24 +1198,22 @@ private: | |||
| valueStrings.reset ([NSMutableArray new]); | |||
| for (int i = 0; i < numSteps; ++i) | |||
| [valueStrings.get() addObject: juceStringToNS (param->getText ((float) i / maxValue, 0))]; | |||
| [valueStrings.get() addObject: juceStringToNS (juceParam->getText ((float) i / maxValue, 0))]; | |||
| } | |||
| } | |||
| #endif | |||
| } | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| AUParameterAddress address = static_cast<AUParameterAddress> (idx); | |||
| #else | |||
| AUParameterAddress address = generateAUParameterAddressForIndex (idx); | |||
| AUParameterAddress address = forceLegacyParamIDs ? static_cast<AUParameterAddress> (idx) | |||
| : generateAUParameterAddress (juceParam); | |||
| #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| // Consider yourself very unlucky if you hit this assertion. The hash codes of your | |||
| // parameter ids are not unique. | |||
| jassert (! paramMap.contains (static_cast<int64> (address))); | |||
| paramAddresses.add (address); | |||
| paramMap.set (static_cast<int64> (address), idx); | |||
| #endif | |||
| #endif | |||
| // create methods in AUParameterTree return unretained objects (!) -> see Apple header AUAudioUnitImplementation.h | |||
| @@ -1226,14 +1221,14 @@ private: | |||
| name: juceStringToNS (name) | |||
| address: address | |||
| min: 0.0f | |||
| max: getMaximumParameterValue (idx) | |||
| max: getMaximumParameterValue (juceParam) | |||
| unit: unit | |||
| unitName: nullptr | |||
| flags: flags | |||
| valueStrings: valueStrings.get() | |||
| dependentParameters: nullptr] retain]; | |||
| [param.get() setValue: processor.getParameterDefaultValue (idx)]; | |||
| [param.get() setValue: juceParam->getDefaultValue()]; | |||
| [params addObject: param]; | |||
| [overviewParams addObject: [NSNumber numberWithUnsignedLongLong:address]]; | |||
| @@ -1259,21 +1254,14 @@ private: | |||
| } | |||
| } | |||
| void setAudioProcessorParameter (int index, float value) | |||
| void setAudioProcessorParameter (AudioProcessorParameter* juceParam, float value) | |||
| { | |||
| if (auto* param = getAudioProcessor().getParameters()[index]) | |||
| if (value != juceParam->getValue()) | |||
| { | |||
| if (value != param->getValue()) | |||
| { | |||
| param->setValue (value); | |||
| juceParam->setValue (value); | |||
| inParameterChangedCallback = true; | |||
| param->sendValueChangedMessageToListeners (value); | |||
| } | |||
| } | |||
| else if (isPositiveAndBelow (index, getAudioProcessor().getNumParameters())) | |||
| { | |||
| getAudioProcessor().setParameter (index, value); | |||
| inParameterChangedCallback = true; | |||
| juceParam->sendValueChangedMessageToListeners (value); | |||
| } | |||
| } | |||
| @@ -1329,9 +1317,9 @@ private: | |||
| case AURenderEventParameterRamp: | |||
| { | |||
| const AUParameterEvent& paramEvent = event->parameter; | |||
| const int idx = getJuceParameterIndexForAUAddress (paramEvent.parameterAddress); | |||
| setAudioProcessorParameter (idx, paramEvent.value); | |||
| if (auto* p = getJuceParameterForAUAddress (paramEvent.parameterAddress)) | |||
| setAudioProcessorParameter (p, paramEvent.value); | |||
| } | |||
| break; | |||
| @@ -1349,7 +1337,7 @@ private: | |||
| jassert (static_cast<int> (frameCount) <= getAudioProcessor().getBlockSize()); | |||
| // process params | |||
| const int numParams = processor.getNumParameters(); | |||
| const int numParams = juceParameters.getNumParameters(); | |||
| processEvents (realtimeEventListHead, numParams, static_cast<AUEventSampleTime> (timestamp->mSampleTime)); | |||
| if (lastTimeStamp.mSampleTime != timestamp->mSampleTime) | |||
| @@ -1477,10 +1465,11 @@ private: | |||
| { | |||
| if (param != nullptr) | |||
| { | |||
| int idx = getJuceParameterIndexForAUAddress ([param address]); | |||
| auto normalisedValue = value / getMaximumParameterValue (idx); | |||
| setAudioProcessorParameter (idx, normalisedValue); | |||
| if (auto* p = getJuceParameterForAUAddress ([param address])) | |||
| { | |||
| auto normalisedValue = value / getMaximumParameterValue (p); | |||
| setAudioProcessorParameter (p, normalisedValue); | |||
| } | |||
| } | |||
| } | |||
| @@ -1488,11 +1477,8 @@ private: | |||
| { | |||
| if (param != nullptr) | |||
| { | |||
| const int idx = getJuceParameterIndexForAUAddress ([param address]); | |||
| auto& processor = getAudioProcessor(); | |||
| if (isPositiveAndBelow (idx, processor.getNumParameters())) | |||
| return processor.getParameter (idx) * getMaximumParameterValue (idx); | |||
| if (auto* p = getJuceParameterForAUAddress ([param address])) | |||
| return p->getValue() * getMaximumParameterValue (p); | |||
| } | |||
| return 0; | |||
| @@ -1509,13 +1495,13 @@ private: | |||
| if (param != nullptr && value != nullptr) | |||
| { | |||
| const int idx = getJuceParameterIndexForAUAddress ([param address]); | |||
| auto& processor = getAudioProcessor(); | |||
| if (auto* p = processor.getParameters()[idx]) | |||
| text = p->getText (*value / getMaximumParameterValue (idx), 0); | |||
| else | |||
| text = String (*value); | |||
| if (auto* p = getJuceParameterForAUAddress ([param address])) | |||
| { | |||
| if (LegacyAudioParameter::isLegacy (p)) | |||
| text = String (*value); | |||
| else | |||
| text = p->getText (*value / getMaximumParameterValue (p), 0); | |||
| } | |||
| } | |||
| return juceStringToNS (text); | |||
| @@ -1525,14 +1511,15 @@ private: | |||
| { | |||
| if (param != nullptr && str != nullptr) | |||
| { | |||
| const int idx = getJuceParameterIndexForAUAddress ([param address]); | |||
| auto& processor = getAudioProcessor(); | |||
| const String text (nsStringToJuce (str)); | |||
| if (auto* p = getJuceParameterForAUAddress ([param address])) | |||
| { | |||
| const String text (nsStringToJuce (str)); | |||
| if (auto* p = processor.getParameters()[idx]) | |||
| return p->getValueForText (text) * getMaximumParameterValue (idx); | |||
| else | |||
| return text.getFloatValue(); | |||
| if (LegacyAudioParameter::isLegacy (p)) | |||
| return text.getFloatValue(); | |||
| else | |||
| return p->getValueForText (text) * getMaximumParameterValue (p); | |||
| } | |||
| } | |||
| return 0; | |||
| @@ -1543,33 +1530,31 @@ private: | |||
| inline AUParameterAddress getAUParameterAddressForIndex (int paramIndex) const noexcept { return static_cast<AUParameterAddress> (paramIndex); } | |||
| inline int getJuceParameterIndexForAUAddress (AUParameterAddress address) const noexcept { return static_cast<int> (address); } | |||
| #else | |||
| AUParameterAddress generateAUParameterAddressForIndex (int paramIndex) const | |||
| inline AUParameterAddress getAUParameterAddressForIndex (int paramIndex) const noexcept | |||
| { | |||
| auto& processor = getAudioProcessor(); | |||
| const int n = processor.getNumParameters(); | |||
| if (isPositiveAndBelow (paramIndex, n)) | |||
| { | |||
| const String& juceParamID = processor.getParameterID (paramIndex); | |||
| return usingManagedParameter ? static_cast<AUParameterAddress> (juceParamID.hashCode64()) | |||
| : static_cast<AUParameterAddress> (juceParamID.getIntValue()); | |||
| } | |||
| return juceParameters.isUsingManagedParameters() ? paramAddresses.getReference (paramIndex) | |||
| : static_cast<AUParameterAddress> (paramIndex); | |||
| } | |||
| return static_cast<AUParameterAddress> (-1); | |||
| inline int getJuceParameterIndexForAUAddress (AUParameterAddress address) const noexcept | |||
| { | |||
| return juceParameters.isUsingManagedParameters() ? paramMap[static_cast<int64> (address)] | |||
| : static_cast<int> (address); | |||
| } | |||
| #endif | |||
| inline AUParameterAddress getAUParameterAddressForIndex (int paramIndex) const noexcept | |||
| AUParameterAddress generateAUParameterAddress (AudioProcessorParameter* param) const | |||
| { | |||
| return usingManagedParameter ? paramAddresses.getReference (paramIndex) | |||
| : static_cast<AUParameterAddress> (paramIndex); | |||
| const String& juceParamID = LegacyAudioParameter::getParamID (param, forceLegacyParamIDs); | |||
| return juceParameters.isUsingManagedParameters() ? static_cast<AUParameterAddress> (juceParamID.hashCode64()) | |||
| : static_cast<AUParameterAddress> (juceParamID.getIntValue()); | |||
| } | |||
| inline int getJuceParameterIndexForAUAddress (AUParameterAddress address) const noexcept | |||
| AudioProcessorParameter* getJuceParameterForAUAddress (AUParameterAddress address) const noexcept | |||
| { | |||
| return usingManagedParameter ? paramMap[static_cast<int64> (address)] | |||
| : static_cast<int> (address); | |||
| return juceParameters.getParamForIndex (getJuceParameterIndexForAUAddress (address)); | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| static const double kDefaultSampleRate; | |||
| @@ -1587,10 +1572,10 @@ private: | |||
| ObjCBlock<AUImplementorValueFromStringCallback> valueFromStringProvider; | |||
| #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| bool usingManagedParameter; | |||
| Array<AUParameterAddress> paramAddresses; | |||
| HashMap<int64, int> paramMap; | |||
| #endif | |||
| LegacyAudioParametersWrapper juceParameters; | |||
| // to avoid recursion on parameter changes, we need to add an | |||
| // editor observer to do the parameter changes | |||
| @@ -1620,6 +1605,11 @@ private: | |||
| String contextName; | |||
| ThreadLocalValue<bool> inParameterChangedCallback; | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| static constexpr bool forceLegacyParamIDs = true; | |||
| #else | |||
| static constexpr bool forceLegacyParamIDs = false; | |||
| #endif | |||
| }; | |||
| const double JuceAudioUnitv3::kDefaultSampleRate = 44100.0; | |||
| @@ -79,6 +79,7 @@ | |||
| #include "../utility/juce_FakeMouseMoveGenerator.h" | |||
| #include "../utility/juce_WindowsHooks.h" | |||
| #include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp" | |||
| #include "../../juce_audio_processors/format_types/juce_VSTCommon.h" | |||
| #ifdef _MSC_VER | |||
| @@ -281,6 +282,8 @@ public: | |||
| processor->setPlayHead (this); | |||
| processor->addListener (this); | |||
| juceParameters.update (*processor, false); | |||
| memset (&vstEffect, 0, sizeof (vstEffect)); | |||
| vstEffect.interfaceIdentifier = juceVstInterfaceIdentifier; | |||
| vstEffect.dispatchFunction = dispatcherCB; | |||
| @@ -288,7 +291,7 @@ public: | |||
| vstEffect.setParameterValueFunction = setParameterCB; | |||
| vstEffect.getParameterValueFunction = getParameterCB; | |||
| vstEffect.numPrograms = jmax (1, af->getNumPrograms()); | |||
| vstEffect.numParameters = af->getNumParameters(); | |||
| vstEffect.numParameters = juceParameters.getNumParameters(); | |||
| vstEffect.numInputChannels = maxNumInChannels; | |||
| vstEffect.numOutputChannels = maxNumOutChannels; | |||
| vstEffect.latency = processor->getLatencySamples(); | |||
| @@ -706,11 +709,10 @@ public: | |||
| //============================================================================== | |||
| float getParameter (int32 index) const | |||
| { | |||
| if (processor == nullptr) | |||
| return 0.0f; | |||
| if (auto* param = juceParameters.getParamForIndex (index)) | |||
| return param->getValue(); | |||
| jassert (isPositiveAndBelow (index, processor->getNumParameters())); | |||
| return processor->getParameter (index); | |||
| return 0.0f; | |||
| } | |||
| static float getParameterCB (VstEffectInterface* vstInterface, int32 index) | |||
| @@ -720,20 +722,12 @@ public: | |||
| void setParameter (int32 index, float value) | |||
| { | |||
| if (processor != nullptr) | |||
| if (auto* param = juceParameters.getParamForIndex (index)) | |||
| { | |||
| if (auto* param = processor->getParameters()[index]) | |||
| { | |||
| param->setValue (value); | |||
| param->setValue (value); | |||
| inParameterChangedCallback = true; | |||
| param->sendValueChangedMessageToListeners (value); | |||
| } | |||
| else | |||
| { | |||
| jassert (isPositiveAndBelow (index, processor->getNumParameters())); | |||
| processor->setParameter (index, value); | |||
| } | |||
| inParameterChangedCallback = true; | |||
| param->sendValueChangedMessageToListeners (value); | |||
| } | |||
| } | |||
| @@ -1472,6 +1466,8 @@ private: | |||
| VSTMidiEventList outgoingEvents; | |||
| float editorScaleFactor = 1.0f; | |||
| LegacyAudioParametersWrapper juceParameters; | |||
| bool isProcessing = false, isBypassed = false, hasShutdown = false; | |||
| bool firstProcessCallback = true, shouldDeleteEditor = false; | |||
| @@ -1664,11 +1660,10 @@ private: | |||
| pointer_sized_int handleGetParameterLabel (VstOpCodeArguments args) | |||
| { | |||
| if (processor != nullptr) | |||
| if (auto* param = juceParameters.getParamForIndex (args.index)) | |||
| { | |||
| jassert (isPositiveAndBelow (args.index, processor->getNumParameters())); | |||
| // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. | |||
| processor->getParameterLabel (args.index).copyToUTF8 ((char*) args.ptr, 24 + 1); | |||
| param->getLabel().copyToUTF8 ((char*) args.ptr, 24 + 1); | |||
| } | |||
| return 0; | |||
| @@ -1676,11 +1671,10 @@ private: | |||
| pointer_sized_int handleGetParameterText (VstOpCodeArguments args) | |||
| { | |||
| if (processor != nullptr) | |||
| if (auto* param = juceParameters.getParamForIndex (args.index)) | |||
| { | |||
| jassert (isPositiveAndBelow (args.index, processor->getNumParameters())); | |||
| // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. | |||
| processor->getParameterText (args.index, 24).copyToUTF8 ((char*) args.ptr, 24 + 1); | |||
| param->getCurrentValueAsText().copyToUTF8 ((char*) args.ptr, 24 + 1); | |||
| } | |||
| return 0; | |||
| @@ -1688,11 +1682,10 @@ private: | |||
| pointer_sized_int handleGetParameterName (VstOpCodeArguments args) | |||
| { | |||
| if (processor != nullptr) | |||
| if (auto* param = juceParameters.getParamForIndex (args.index)) | |||
| { | |||
| jassert (isPositiveAndBelow (args.index, processor->getNumParameters())); | |||
| // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. | |||
| processor->getParameterName (args.index, 32).copyToUTF8 ((char*) args.ptr, 32 + 1); | |||
| param->getName (32).copyToUTF8 ((char*) args.ptr, 32 + 1); | |||
| } | |||
| return 0; | |||
| @@ -1823,20 +1816,20 @@ private: | |||
| pointer_sized_int handleIsParameterAutomatable (VstOpCodeArguments args) | |||
| { | |||
| if (processor == nullptr) | |||
| return 0; | |||
| if (auto* param = juceParameters.getParamForIndex (args.index)) | |||
| { | |||
| const bool isMeter = (((param->getCategory() & 0xffff0000) >> 16) == 2); | |||
| return (param->isAutomatable() && (! isMeter) ? 1 : 0); | |||
| } | |||
| const bool isMeter = (((processor->getParameterCategory (args.index) & 0xffff0000) >> 16) == 2); | |||
| return (processor->isParameterAutomatable (args.index) && (! isMeter) ? 1 : 0); | |||
| return 0; | |||
| } | |||
| pointer_sized_int handleParameterValueForText (VstOpCodeArguments args) | |||
| { | |||
| if (processor != nullptr) | |||
| if (auto* param = juceParameters.getParamForIndex (args.index)) | |||
| { | |||
| jassert (isPositiveAndBelow (args.index, processor->getNumParameters())); | |||
| if (auto* param = processor->getParameters()[args.index]) | |||
| if (! LegacyAudioParameter::isLegacy (param)) | |||
| { | |||
| auto value = param->getValueForText (String::fromUTF8 ((char*) args.ptr)); | |||
| param->setValue (value); | |||
| @@ -2120,11 +2113,14 @@ private: | |||
| { | |||
| if (processor != nullptr && dest != nullptr) | |||
| { | |||
| if (auto* param = processor->getParameters()[(int) paramIndex]) | |||
| if (auto* param = juceParameters.getParamForIndex ((int) paramIndex)) | |||
| { | |||
| String text (param->getText (value, 1024)); | |||
| memcpy (dest, text.toRawUTF8(), ((size_t) text.length()) + 1); | |||
| return 0xbeef; | |||
| if (! LegacyAudioParameter::isLegacy (param)) | |||
| { | |||
| String text (param->getText (value, 1024)); | |||
| memcpy (dest, text.toRawUTF8(), ((size_t) text.length()) + 1); | |||
| return 0xbeef; | |||
| } | |||
| } | |||
| } | |||
| @@ -42,6 +42,7 @@ | |||
| #include "../utility/juce_IncludeModuleHeaders.h" | |||
| #include "../utility/juce_WindowsHooks.h" | |||
| #include "../utility/juce_FakeMouseMoveGenerator.h" | |||
| #include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp" | |||
| #include "../../juce_audio_processors/format_types/juce_VST3Common.h" | |||
| #ifndef JUCE_VST3_CAN_REPLACE_VST2 | |||
| @@ -89,7 +90,12 @@ using namespace Steinberg; | |||
| class JuceAudioProcessor : public FUnknown | |||
| { | |||
| public: | |||
| JuceAudioProcessor (AudioProcessor* source) noexcept : audioProcessor (source) {} | |||
| JuceAudioProcessor (AudioProcessor* source) noexcept | |||
| : audioProcessor (source) | |||
| { | |||
| setupParameters(); | |||
| } | |||
| virtual ~JuceAudioProcessor() {} | |||
| AudioProcessor* get() const noexcept { return audioProcessor; } | |||
| @@ -97,15 +103,85 @@ public: | |||
| JUCE_DECLARE_VST3_COM_QUERY_METHODS | |||
| JUCE_DECLARE_VST3_COM_REF_METHODS | |||
| static const FUID iid; | |||
| //============================================================================== | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept { return static_cast<Vst::ParamID> (paramIndex); } | |||
| #else | |||
| inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept | |||
| { | |||
| return isUsingManagedParameters() ? vstParamIDs.getReference (paramIndex) | |||
| : static_cast<Vst::ParamID> (paramIndex); | |||
| } | |||
| #endif | |||
| AudioProcessorParameter* getParamForVSTParamID (Vst::ParamID paramID) const noexcept | |||
| { | |||
| return paramMap[static_cast<int32> (paramID)]; | |||
| } | |||
| int getNumParameters() const noexcept { return vstParamIDs.size(); } | |||
| bool isUsingManagedParameters() const noexcept { return juceParameters.isUsingManagedParameters(); } | |||
| //============================================================================== | |||
| static const FUID iid; | |||
| Array<Vst::ParamID> vstParamIDs; | |||
| Vst::ParamID bypassParamID = 0; | |||
| bool isBypassed = false; | |||
| private: | |||
| enum InternalParameters | |||
| { | |||
| paramBypass = 0x62797073 // 'byps' | |||
| }; | |||
| //============================================================================== | |||
| void setupParameters() | |||
| { | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| const bool forceLegacyParamIDs = true; | |||
| #else | |||
| const bool forceLegacyParamIDs = false; | |||
| #endif | |||
| juceParameters.update (*audioProcessor, forceLegacyParamIDs); | |||
| auto numParameters = juceParameters.getNumParameters(); | |||
| int i = 0; | |||
| for (auto* juceParam : juceParameters.params) | |||
| { | |||
| Vst::ParamID vstParamID = forceLegacyParamIDs ? static_cast<Vst::ParamID> (i++) | |||
| : generateVSTParamIDForParam (juceParam); | |||
| vstParamIDs.add (vstParamID); | |||
| paramMap.set (static_cast<int32> (vstParamID), juceParam); | |||
| } | |||
| bypassParamID = static_cast<Vst::ParamID> (isUsingManagedParameters() ? paramBypass : numParameters); | |||
| } | |||
| Vst::ParamID generateVSTParamIDForParam (AudioProcessorParameter* param) | |||
| { | |||
| auto juceParamID = LegacyAudioParameter::getParamID (param, false); | |||
| auto paramHash = static_cast<Vst::ParamID> (juceParamID.hashCode()); | |||
| #if JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| // studio one doesn't like negative parameters | |||
| paramHash &= ~(1 << (sizeof (Vst::ParamID) * 8 - 1)); | |||
| #endif | |||
| return isUsingManagedParameters() ? paramHash | |||
| : static_cast<Vst::ParamID> (juceParamID.getIntValue()); | |||
| } | |||
| //============================================================================== | |||
| Atomic<int> refCount; | |||
| ScopedPointer<AudioProcessor> audioProcessor; | |||
| ScopedJuceInitialiser_GUI libraryInitialiser; | |||
| //============================================================================== | |||
| LegacyAudioParametersWrapper juceParameters; | |||
| HashMap<int32, AudioProcessorParameter*> paramMap; | |||
| JuceAudioProcessor() = delete; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAudioProcessor) | |||
| }; | |||
| @@ -197,39 +273,38 @@ public: | |||
| enum InternalParameters | |||
| { | |||
| paramPreset = 0x70727374, // 'prst' | |||
| paramBypass = 0x62797073, // 'byps' | |||
| paramMidiControllerOffset = 0x6d636d00 // 'mdm*' | |||
| }; | |||
| struct Param : public Vst::Parameter | |||
| { | |||
| Param (AudioProcessor& p, int index, Vst::ParamID paramID) : owner (p), paramIndex (index) | |||
| Param (JuceVST3EditController& editController, AudioProcessorParameter& p, | |||
| Vst::ParamID vstParamID, bool forceLegacyParamIDs) | |||
| : owner (editController), param (p) | |||
| { | |||
| info.id = paramID; | |||
| info.id = vstParamID; | |||
| toString128 (info.title, p.getParameterName (index)); | |||
| toString128 (info.shortTitle, p.getParameterName (index, 8)); | |||
| toString128 (info.units, p.getParameterLabel (index)); | |||
| toString128 (info.title, param.getName (128)); | |||
| toString128 (info.shortTitle, param.getName (8)); | |||
| toString128 (info.units, param.getLabel()); | |||
| info.stepCount = (Steinberg::int32) 0; | |||
| #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| if (p.isParameterDiscrete (index)) | |||
| #endif | |||
| if (! forceLegacyParamIDs && param.isDiscrete()) | |||
| { | |||
| const int numSteps = p.getParameterNumSteps (index); | |||
| const int numSteps = param.getNumSteps(); | |||
| info.stepCount = (Steinberg::int32) (numSteps > 0 && numSteps < 0x7fffffff ? numSteps - 1 : 0); | |||
| } | |||
| info.defaultNormalizedValue = p.getParameterDefaultValue (index); | |||
| info.defaultNormalizedValue = param.getDefaultValue(); | |||
| jassert (info.defaultNormalizedValue >= 0 && info.defaultNormalizedValue <= 1.0f); | |||
| info.unitId = Vst::kRootUnitId; | |||
| // Is this a meter? | |||
| if (((p.getParameterCategory (index) & 0xffff0000) >> 16) == 2) | |||
| if (((param.getCategory() & 0xffff0000) >> 16) == 2) | |||
| info.flags = Vst::ParameterInfo::kIsReadOnly; | |||
| else | |||
| info.flags = p.isParameterAutomatable (index) ? Vst::ParameterInfo::kCanAutomate : 0; | |||
| info.flags = param.isAutomatable() ? Vst::ParameterInfo::kCanAutomate : 0; | |||
| valueNormalized = info.defaultNormalizedValue; | |||
| } | |||
| @@ -251,17 +326,10 @@ public: | |||
| { | |||
| auto value = static_cast<float> (v); | |||
| if (auto* param = owner.getParameters()[paramIndex]) | |||
| { | |||
| param->setValue (value); | |||
| param.setValue (value); | |||
| inParameterChangedCallback = true; | |||
| param->sendValueChangedMessageToListeners (value); | |||
| } | |||
| else | |||
| { | |||
| owner.setParameter (paramIndex, value); | |||
| } | |||
| inParameterChangedCallback = true; | |||
| param.sendValueChangedMessageToListeners (value); | |||
| } | |||
| changed(); | |||
| @@ -273,18 +341,14 @@ public: | |||
| void toString (Vst::ParamValue value, Vst::String128 result) const override | |||
| { | |||
| if (auto* p = owner.getParameters()[paramIndex]) | |||
| toString128 (result, p->getText ((float) value, 128)); | |||
| else | |||
| // remain backward-compatible with old JUCE code | |||
| toString128 (result, owner.getParameterText (paramIndex, 128)); | |||
| toString128 (result, param.getText ((float) value, 128)); | |||
| } | |||
| bool fromString (const Vst::TChar* text, Vst::ParamValue& outValueNormalized) const override | |||
| { | |||
| if (auto* p = owner.getParameters()[paramIndex]) | |||
| if (! LegacyAudioParameter::isLegacy (¶m)) | |||
| { | |||
| outValueNormalized = p->getValueForText (getStringFromVstTChars (text)); | |||
| outValueNormalized = param.getValueForText (getStringFromVstTChars (text)); | |||
| return true; | |||
| } | |||
| @@ -300,8 +364,8 @@ public: | |||
| Vst::ParamValue toNormalized (Vst::ParamValue v) const override { return v; } | |||
| private: | |||
| AudioProcessor& owner; | |||
| int paramIndex; | |||
| JuceVST3EditController& owner; | |||
| AudioProcessorParameter& param; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Param) | |||
| }; | |||
| @@ -502,12 +566,8 @@ public: | |||
| // Cubase and Nuendo need to inform the host of the current parameter values | |||
| if (auto* pluginInstance = getPluginInstance()) | |||
| { | |||
| auto numParameters = pluginInstance->getNumParameters(); | |||
| for (int i = 0; i < numParameters; ++i) | |||
| setParamNormalized (getVSTParamIDForIndex (i), (double) pluginInstance->getParameter (i)); | |||
| setParamNormalized (bypassParamID, audioProcessor->isBypassed ? 1.0f : 0.0f); | |||
| for (auto vstParamId : audioProcessor->vstParamIDs) | |||
| setParamNormalized (vstParamId, audioProcessor->getParamForVSTParamID (vstParamId)->getValue()); | |||
| auto numPrograms = pluginInstance->getNumPrograms(); | |||
| @@ -601,10 +661,7 @@ public: | |||
| } | |||
| //============================================================================== | |||
| void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) override { beginEdit (getVSTParamIDForIndex (index)); } | |||
| void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) override { endEdit (getVSTParamIDForIndex (index)); } | |||
| void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) override | |||
| void paramChanged (Vst::ParamID vstParamId, float newValue) | |||
| { | |||
| if (inParameterChangedCallback.get()) | |||
| { | |||
| @@ -613,8 +670,17 @@ public: | |||
| } | |||
| // NB: Cubase has problems if performEdit is called without setParamNormalized | |||
| EditController::setParamNormalized (getVSTParamIDForIndex (index), (double) newValue); | |||
| performEdit (getVSTParamIDForIndex (index), (double) newValue); | |||
| EditController::setParamNormalized (vstParamId, (double) newValue); | |||
| performEdit (vstParamId, (double) newValue); | |||
| } | |||
| //============================================================================== | |||
| void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) override { beginEdit (audioProcessor->getVSTParamIDForIndex (index)); } | |||
| void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) override { endEdit (audioProcessor->getVSTParamIDForIndex (index)); } | |||
| void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) override | |||
| { | |||
| paramChanged (audioProcessor->getVSTParamIDForIndex (index), newValue); | |||
| } | |||
| void audioProcessorChanged (AudioProcessor*) override | |||
| @@ -641,6 +707,7 @@ public: | |||
| private: | |||
| friend class JuceVST3Component; | |||
| friend struct Param; | |||
| //============================================================================== | |||
| ComSmartPtr<JuceAudioProcessor> audioProcessor; | |||
| @@ -657,53 +724,41 @@ private: | |||
| Vst::ParamID midiControllerToParameter[numMIDIChannels][Vst::kCountCtrlNumber]; | |||
| //============================================================================== | |||
| #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| bool usingManagedParameter = false; | |||
| Array<Vst::ParamID> vstParamIDs; | |||
| #endif | |||
| Vst::ParamID bypassParamID; | |||
| Atomic<int> vst3IsPlaying { 0 }; | |||
| //============================================================================== | |||
| void setupParameters() | |||
| { | |||
| if (auto* pluginInstance = getPluginInstance()) | |||
| { | |||
| pluginInstance->addListener (this); | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| const bool usingManagedParameter = false; | |||
| #endif | |||
| if (parameters.getParameterCount() <= 0) | |||
| { | |||
| auto numParameters = pluginInstance->getNumParameters(); | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| const bool forceLegacyParamIDs = true; | |||
| #else | |||
| const bool forceLegacyParamIDs = false; | |||
| #endif | |||
| #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| usingManagedParameter = (pluginInstance->getParameters().size() == numParameters); | |||
| #endif | |||
| auto n = audioProcessor->getNumParameters(); | |||
| for (int i = 0; i < numParameters; ++i) | |||
| for (int i = 0; i < n; ++i) | |||
| { | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| const Vst::ParamID vstParamID = static_cast<Vst::ParamID> (i); | |||
| #else | |||
| const Vst::ParamID vstParamID = generateVSTParamIDForIndex (pluginInstance, i); | |||
| vstParamIDs.add (vstParamID); | |||
| #endif | |||
| auto vstParamID = audioProcessor->getVSTParamIDForIndex (i); | |||
| auto* juceParam = audioProcessor->getParamForVSTParamID (vstParamID); | |||
| parameters.addParameter (new Param (*pluginInstance, i, vstParamID)); | |||
| parameters.addParameter (new Param (*this, *juceParam, vstParamID, forceLegacyParamIDs)); | |||
| } | |||
| bypassParamID = static_cast<Vst::ParamID> (usingManagedParameter ? paramBypass : numParameters); | |||
| parameters.addParameter (new BypassParam (bypassParamID)); | |||
| parameters.addParameter (new BypassParam (audioProcessor->bypassParamID)); | |||
| if (pluginInstance->getNumPrograms() > 1) | |||
| parameters.addParameter (new ProgramChangeParameter (*pluginInstance)); | |||
| } | |||
| #if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | |||
| parameterToMidiControllerOffset = static_cast<Vst::ParamID> (usingManagedParameter ? paramMidiControllerOffset | |||
| : parameters.getParameterCount()); | |||
| parameterToMidiControllerOffset = static_cast<Vst::ParamID> (audioProcessor->isUsingManagedParameters() ? paramMidiControllerOffset | |||
| : parameters.getParameterCount()); | |||
| initialiseMidiControllerMappings(); | |||
| #endif | |||
| @@ -742,41 +797,6 @@ private: | |||
| } | |||
| } | |||
| //============================================================================== | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept { return static_cast<Vst::ParamID> (paramIndex); } | |||
| #else | |||
| static Vst::ParamID generateVSTParamIDForIndex (AudioProcessor* const pluginFilter, int paramIndex) | |||
| { | |||
| jassert (pluginFilter != nullptr); | |||
| const int n = pluginFilter->getNumParameters(); | |||
| const bool managedParameter = (pluginFilter->getParameters().size() == n); | |||
| if (isPositiveAndBelow (paramIndex, n)) | |||
| { | |||
| auto juceParamID = pluginFilter->getParameterID (paramIndex); | |||
| auto paramHash = static_cast<Vst::ParamID> (juceParamID.hashCode()); | |||
| #if JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| // studio one doesn't like negative parameters | |||
| paramHash &= ~(1 << (sizeof (Vst::ParamID) * 8 - 1)); | |||
| #endif | |||
| return managedParameter ? paramHash | |||
| : static_cast<Vst::ParamID> (juceParamID.getIntValue()); | |||
| } | |||
| return static_cast<Vst::ParamID> (-1); | |||
| } | |||
| inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept | |||
| { | |||
| return usingManagedParameter ? vstParamIDs.getReference (paramIndex) | |||
| : static_cast<Vst::ParamID> (paramIndex); | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| class JuceVST3Editor : public Vst::EditorView, | |||
| public Steinberg::IPlugViewContentScaleSupport, | |||
| @@ -1172,17 +1192,14 @@ public: | |||
| processSetup.sampleRate = 44100.0; | |||
| processSetup.symbolicSampleSize = Vst::kSample32; | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| vstBypassParameterId = static_cast<Vst::ParamID> (pluginInstance->getNumParameters()); | |||
| #else | |||
| cacheParameterIDs(); | |||
| #endif | |||
| pluginInstance->setPlayHead (this); | |||
| } | |||
| ~JuceVST3Component() | |||
| { | |||
| if (juceVST3EditController != nullptr) | |||
| juceVST3EditController->vst3IsPlaying = 0; | |||
| if (pluginInstance != nullptr) | |||
| if (pluginInstance->getPlayHead() == this) | |||
| pluginInstance->setPlayHead (nullptr); | |||
| @@ -1246,6 +1263,9 @@ public: | |||
| tresult PLUGIN_API disconnect (IConnectionPoint*) override | |||
| { | |||
| if (juceVST3EditController != nullptr) | |||
| juceVST3EditController->vst3IsPlaying = 0; | |||
| juceVST3EditController = nullptr; | |||
| return kResultTrue; | |||
| } | |||
| @@ -2020,7 +2040,7 @@ public: | |||
| { | |||
| auto vstParamID = paramQueue->getParameterId(); | |||
| if (vstParamID == vstBypassParameterId) | |||
| if (vstParamID == comPluginInstance->bypassParamID) | |||
| setBypassed (static_cast<float> (value) != 0.0f); | |||
| #if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | |||
| else if (juceVST3EditController->isMidiControllerParamID (vstParamID)) | |||
| @@ -2037,20 +2057,15 @@ public: | |||
| } | |||
| else | |||
| { | |||
| auto index = getJuceIndexForVSTParamID (vstParamID); | |||
| auto floatValue = static_cast<float> (value); | |||
| if (auto* param = pluginInstance->getParameters()[index]) | |||
| if (auto* param = comPluginInstance->getParamForVSTParamID (vstParamID)) | |||
| { | |||
| param->setValue (floatValue); | |||
| inParameterChangedCallback = true; | |||
| param->sendValueChangedMessageToListeners (floatValue); | |||
| } | |||
| else if (isPositiveAndBelow (index, pluginInstance->getNumParameters())) | |||
| { | |||
| pluginInstance->setParameter (index, floatValue); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -2088,12 +2103,16 @@ public: | |||
| if (data.processContext != nullptr) | |||
| { | |||
| processContext = *data.processContext; | |||
| pluginInstance->vst3IsPlaying = processContext.state & Vst::ProcessContext::kPlaying; | |||
| if (juceVST3EditController != nullptr) | |||
| juceVST3EditController->vst3IsPlaying = processContext.state & Vst::ProcessContext::kPlaying; | |||
| } | |||
| else | |||
| { | |||
| zerostruct (processContext); | |||
| pluginInstance->vst3IsPlaying = 0; | |||
| if (juceVST3EditController != nullptr) | |||
| juceVST3EditController->vst3IsPlaying = 0; | |||
| } | |||
| midiBuffer.clear(); | |||
| @@ -2164,14 +2183,6 @@ private: | |||
| #endif | |||
| ScopedJuceInitialiser_GUI libraryInitialiser; | |||
| #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| bool usingManagedParameter; | |||
| Array<Vst::ParamID> vstParamIDs; | |||
| HashMap<int32, int> paramMap; | |||
| #endif | |||
| Vst::ParamID vstBypassParameterId; | |||
| static const char* kJucePrivateDataIdentifier; | |||
| //============================================================================== | |||
| @@ -2387,44 +2398,6 @@ private: | |||
| p.prepareToPlay (sampleRate, bufferSize); | |||
| } | |||
| //============================================================================== | |||
| #if JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept { return static_cast<Vst::ParamID> (paramIndex); } | |||
| inline int getJuceIndexForVSTParamID (Vst::ParamID paramID) const noexcept { return static_cast<int> (paramID); } | |||
| #else | |||
| void cacheParameterIDs() | |||
| { | |||
| const int numParameters = pluginInstance->getNumParameters(); | |||
| usingManagedParameter = (pluginInstance->getParameters().size() == numParameters); | |||
| vstBypassParameterId = static_cast<Vst::ParamID> (usingManagedParameter ? JuceVST3EditController::paramBypass : numParameters); | |||
| for (int i = 0; i < numParameters; ++i) | |||
| { | |||
| auto paramID = JuceVST3EditController::generateVSTParamIDForIndex (pluginInstance, i); | |||
| // Consider yourself very unlucky if you hit this assertion. The hash code of your | |||
| // parameter ids are not unique. | |||
| jassert (! vstParamIDs.contains (paramID)); | |||
| vstParamIDs.add (paramID); | |||
| paramMap.set (static_cast<int32> (paramID), i); | |||
| } | |||
| } | |||
| inline Vst::ParamID getVSTParamIDForIndex (int paramIndex) const noexcept | |||
| { | |||
| return usingManagedParameter ? vstParamIDs.getReference (paramIndex) | |||
| : static_cast<Vst::ParamID> (paramIndex); | |||
| } | |||
| inline int getJuceIndexForVSTParamID (Vst::ParamID paramID) const noexcept | |||
| { | |||
| return usingManagedParameter ? paramMap[static_cast<int32> (paramID)] | |||
| : static_cast<int> (paramID); | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVST3Component) | |||
| }; | |||
| @@ -0,0 +1,185 @@ | |||
| /* | |||
| ============================================================================== | |||
| 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 | |||
| { | |||
| #if JUCE_GCC | |||
| #pragma GCC diagnostic push | |||
| #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||
| #elif JUCE_CLANG | |||
| #pragma clang diagnostic push | |||
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||
| #elif JUCE_MSVC | |||
| #pragma warning (push, 0) | |||
| #pragma warning (disable: 4996) | |||
| #endif | |||
| class LegacyAudioParameter : public AudioProcessorParameter | |||
| { | |||
| public: | |||
| LegacyAudioParameter (AudioProcessor& audioProcessorToUse, int audioParameterIndex) | |||
| : audioProcessor (audioProcessorToUse), idx (audioParameterIndex) | |||
| { | |||
| jassert (idx < audioProcessor.getNumParameters()); | |||
| } | |||
| //============================================================================== | |||
| float getValue() const override { return audioProcessor.getParameter (idx); } | |||
| void setValue (float newValue) override { audioProcessor.setParameter (idx, newValue); } | |||
| float getDefaultValue() const override { return audioProcessor.getParameterDefaultValue (idx); } | |||
| String getName (int maxLen) const override { return audioProcessor.getParameterName (idx, maxLen); } | |||
| String getLabel() const override { return audioProcessor.getParameterLabel (idx); } | |||
| int getNumSteps() const override { return audioProcessor.getParameterNumSteps (idx); } | |||
| bool isDiscrete() const override { return audioProcessor.isParameterDiscrete (idx); } | |||
| bool isBoolean() const override { return false; } | |||
| bool isOrientationInverted() const override { return audioProcessor.isParameterOrientationInverted (idx); } | |||
| bool isAutomatable() const override { return audioProcessor.isParameterAutomatable (idx); } | |||
| bool isMetaParameter() const override { return audioProcessor.isMetaParameter (idx); } | |||
| Category getCategory() const override { return audioProcessor.getParameterCategory (idx); } | |||
| String getCurrentValueAsText() const override { return audioProcessor.getParameterText (idx); } | |||
| String getParamID() const { return audioProcessor.getParameterID (idx); } | |||
| //============================================================================== | |||
| float getValueForText (const String&) const override | |||
| { | |||
| // legacy parameters do not support this method | |||
| jassertfalse; | |||
| return 0.0f; | |||
| } | |||
| String getText (float, int) const override | |||
| { | |||
| // legacy parameters do not support this method | |||
| jassertfalse; | |||
| return {}; | |||
| } | |||
| //============================================================================== | |||
| static bool isLegacy (AudioProcessorParameter* param) noexcept | |||
| { | |||
| return (dynamic_cast<LegacyAudioParameter*> (param) != nullptr); | |||
| } | |||
| static int getParamIndex (AudioProcessor& processor, AudioProcessorParameter* param) noexcept | |||
| { | |||
| if (auto* legacy = dynamic_cast<LegacyAudioParameter*> (param)) | |||
| { | |||
| return legacy->idx; | |||
| } | |||
| else | |||
| { | |||
| auto n = processor.getNumParameters(); | |||
| jassert (n == processor.getParameters().size()); | |||
| for (int i = 0; i < n; ++i) | |||
| { | |||
| if (processor.getParameters()[i] == param) | |||
| return i; | |||
| } | |||
| } | |||
| return -1; | |||
| } | |||
| static String getParamID (AudioProcessorParameter* param, bool forceLegacyParamIDs) noexcept | |||
| { | |||
| if (auto* legacy = dynamic_cast<LegacyAudioParameter*> (param)) | |||
| { | |||
| return legacy->getParamID(); | |||
| } | |||
| else if (auto* paramWithID = dynamic_cast<AudioProcessorParameterWithID*> (param)) | |||
| { | |||
| if (! forceLegacyParamIDs) | |||
| return paramWithID->paramID; | |||
| } | |||
| return String (param->getParameterIndex()); | |||
| } | |||
| private: | |||
| AudioProcessor& audioProcessor; | |||
| int idx; | |||
| }; | |||
| //============================================================================== | |||
| class LegacyAudioParametersWrapper | |||
| { | |||
| public: | |||
| void update (AudioProcessor& audioProcessor, bool forceLegacyParamIDs) | |||
| { | |||
| clear(); | |||
| legacyParamIDs = forceLegacyParamIDs; | |||
| auto numParameters = audioProcessor.getNumParameters(); | |||
| usingManagedParameters = (audioProcessor.getParameters().size() == numParameters) && (! legacyParamIDs); | |||
| for (int i = 0; i < numParameters; ++i) | |||
| { | |||
| AudioProcessorParameter* param = usingManagedParameters ? audioProcessor.getParameters()[i] | |||
| : (legacy.add (new LegacyAudioParameter (audioProcessor, i))); | |||
| params.add (param); | |||
| } | |||
| } | |||
| void clear() | |||
| { | |||
| legacy.clear(); | |||
| params.clear(); | |||
| } | |||
| AudioProcessorParameter* getParamForIndex (int index) const | |||
| { | |||
| if (isPositiveAndBelow (index, params.size())) | |||
| return params[index]; | |||
| return nullptr; | |||
| } | |||
| String getParamID (AudioProcessor& processor, int idx) const noexcept | |||
| { | |||
| return usingManagedParameters ? processor.getParameterID (idx) : String (idx); | |||
| } | |||
| bool isUsingManagedParameters() const noexcept { return usingManagedParameters; } | |||
| int getNumParameters() const noexcept { return params.size(); } | |||
| Array<AudioProcessorParameter*> params; | |||
| private: | |||
| OwnedArray<LegacyAudioParameter> legacy; | |||
| bool legacyParamIDs = false, usingManagedParameters = false; | |||
| }; | |||
| #if JUCE_GCC | |||
| #pragma GCC diagnostic pop | |||
| #elif JUCE_CLANG | |||
| #pragma clang diagnostic pop | |||
| #elif JUCE_MSVC | |||
| #pragma warning (pop) | |||
| #endif | |||
| } // namespace juce | |||
| @@ -154,6 +154,7 @@ struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewCompone | |||
| #include "format/juce_AudioPluginFormat.cpp" | |||
| #include "format/juce_AudioPluginFormatManager.cpp" | |||
| #include "format_types/juce_LegacyAudioParameter.cpp" | |||
| #include "processors/juce_AudioProcessor.cpp" | |||
| #include "processors/juce_AudioPluginInstance.cpp" | |||
| #include "processors/juce_AudioProcessorEditor.cpp" | |||
| @@ -27,6 +27,14 @@ | |||
| namespace juce | |||
| { | |||
| #if JUCE_MSVC | |||
| #pragma warning (push, 0) | |||
| // MSVC does not like it if you override a deprecated method even if you | |||
| // keep the deprecation attribute. Other compilers are more forgiving. | |||
| #pragma warning (disable: 4996) | |||
| #endif | |||
| //============================================================================== | |||
| /** | |||
| Base class for an active instance of a plugin. | |||
| @@ -123,4 +131,8 @@ private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance) | |||
| }; | |||
| #if JUCE_MSVC | |||
| #pragma warning (pop) | |||
| #endif | |||
| } // namespace juce | |||
| @@ -414,6 +414,18 @@ void AudioProcessor::setLatencySamples (const int newLatency) | |||
| } | |||
| } | |||
| //============================================================================== | |||
| #if JUCE_GCC | |||
| #pragma GCC diagnostic push | |||
| #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||
| #elif JUCE_CLANG | |||
| #pragma clang diagnostic push | |||
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |||
| #elif JUCE_MSVC | |||
| #pragma warning (push, 0) | |||
| #pragma warning (disable: 4996) | |||
| #endif | |||
| void AudioProcessor::setParameterNotifyingHost (int parameterIndex, float newValue) | |||
| { | |||
| if (auto* param = getParameters()[parameterIndex]) | |||
| @@ -427,12 +439,6 @@ void AudioProcessor::setParameterNotifyingHost (int parameterIndex, float newVal | |||
| } | |||
| } | |||
| AudioProcessorListener* AudioProcessor::getListenerLocked (int index) const noexcept | |||
| { | |||
| const ScopedLock sl (listenerLock); | |||
| return listeners[index]; | |||
| } | |||
| void AudioProcessor::sendParamChangeMessageToListeners (int parameterIndex, float newValue) | |||
| { | |||
| if (auto* param = getParameters()[parameterIndex]) | |||
| @@ -511,6 +517,49 @@ void AudioProcessor::endParameterChangeGesture (int parameterIndex) | |||
| } | |||
| } | |||
| String AudioProcessor::getParameterName (int index, int maximumStringLength) | |||
| { | |||
| if (auto* p = managedParameters[index]) | |||
| return p->getName (maximumStringLength); | |||
| return getParameterName (index).substring (0, maximumStringLength); | |||
| } | |||
| const String AudioProcessor::getParameterText (int index) | |||
| { | |||
| #if JUCE_DEBUG | |||
| // if you hit this, then you're probably using the old parameter control methods, | |||
| // but have forgotten to implement either of the getParameterText() methods. | |||
| jassert (! textRecursionCheck); | |||
| ScopedValueSetter<bool> sv (textRecursionCheck, true, false); | |||
| #endif | |||
| return getParameterText (index, 1024); | |||
| } | |||
| String AudioProcessor::getParameterText (int index, int maximumStringLength) | |||
| { | |||
| if (auto* p = managedParameters[index]) | |||
| return p->getText (p->getValue(), maximumStringLength); | |||
| return getParameterText (index).substring (0, maximumStringLength); | |||
| } | |||
| #if JUCE_GCC | |||
| #pragma GCC diagnostic pop | |||
| #elif JUCE_CLANG | |||
| #pragma clang diagnostic pop | |||
| #elif JUCE_MSVC | |||
| #pragma warning (pop) | |||
| #endif | |||
| //============================================================================== | |||
| AudioProcessorListener* AudioProcessor::getListenerLocked (int index) const noexcept | |||
| { | |||
| const ScopedLock sl (listenerLock); | |||
| return listeners[index]; | |||
| } | |||
| void AudioProcessor::updateHostDisplay() | |||
| { | |||
| for (int i = listeners.size(); --i >= 0;) | |||
| @@ -567,34 +616,6 @@ String AudioProcessor::getParameterID (int index) | |||
| return String (index); | |||
| } | |||
| String AudioProcessor::getParameterName (int index, int maximumStringLength) | |||
| { | |||
| if (auto* p = managedParameters[index]) | |||
| return p->getName (maximumStringLength); | |||
| return getParameterName (index).substring (0, maximumStringLength); | |||
| } | |||
| const String AudioProcessor::getParameterText (int index) | |||
| { | |||
| #if JUCE_DEBUG | |||
| // if you hit this, then you're probably using the old parameter control methods, | |||
| // but have forgotten to implement either of the getParameterText() methods. | |||
| jassert (! textRecursionCheck); | |||
| ScopedValueSetter<bool> sv (textRecursionCheck, true, false); | |||
| #endif | |||
| return getParameterText (index, 1024); | |||
| } | |||
| String AudioProcessor::getParameterText (int index, int maximumStringLength) | |||
| { | |||
| if (auto* p = managedParameters[index]) | |||
| return p->getText (p->getValue(), maximumStringLength); | |||
| return getParameterText (index).substring (0, maximumStringLength); | |||
| } | |||
| int AudioProcessor::getParameterNumSteps (int index) | |||
| { | |||
| if (auto* p = managedParameters[index]) | |||
| @@ -1368,9 +1389,6 @@ AudioProcessorParameter::~AudioProcessorParameter() | |||
| void AudioProcessorParameter::setValueNotifyingHost (float newValue) | |||
| { | |||
| // This method can't be used until the parameter has been attached to a processor! | |||
| jassert (processor != nullptr && parameterIndex >= 0); | |||
| setValue (newValue); | |||
| sendValueChangedMessageToListeners (newValue); | |||
| } | |||
| @@ -1394,11 +1412,14 @@ void AudioProcessorParameter::beginChangeGesture() | |||
| if (auto* l = listeners[i]) | |||
| l->parameterGestureChanged (getParameterIndex(), true); | |||
| // audioProcessorParameterChangeGestureBegin callbacks will shortly be deprecated and | |||
| // this code will be removed. | |||
| for (int i = processor->listeners.size(); --i >= 0;) | |||
| if (auto* l = processor->listeners[i]) | |||
| l->audioProcessorParameterChangeGestureBegin (processor, getParameterIndex()); | |||
| if (processor != nullptr && parameterIndex >= 0) | |||
| { | |||
| // audioProcessorParameterChangeGestureBegin callbacks will shortly be deprecated and | |||
| // this code will be removed. | |||
| for (int i = processor->listeners.size(); --i >= 0;) | |||
| if (auto* l = processor->listeners[i]) | |||
| l->audioProcessorParameterChangeGestureBegin (processor, getParameterIndex()); | |||
| } | |||
| } | |||
| void AudioProcessorParameter::endChangeGesture() | |||
| @@ -1420,11 +1441,14 @@ void AudioProcessorParameter::endChangeGesture() | |||
| if (auto* l = listeners[i]) | |||
| l->parameterGestureChanged (getParameterIndex(), false); | |||
| // audioProcessorParameterChangeGestureEnd callbacks will shortly be deprecated and | |||
| // this code will be removed. | |||
| for (int i = processor->listeners.size(); --i >= 0;) | |||
| if (auto* l = processor->listeners[i]) | |||
| l->audioProcessorParameterChangeGestureEnd (processor, getParameterIndex()); | |||
| if (processor != nullptr && parameterIndex >= 0) | |||
| { | |||
| // audioProcessorParameterChangeGestureEnd callbacks will shortly be deprecated and | |||
| // this code will be removed. | |||
| for (int i = processor->listeners.size(); --i >= 0;) | |||
| if (auto* l = processor->listeners[i]) | |||
| l->audioProcessorParameterChangeGestureEnd (processor, getParameterIndex()); | |||
| } | |||
| } | |||
| void AudioProcessorParameter::sendValueChangedMessageToListeners (float newValue) | |||
| @@ -1435,11 +1459,14 @@ void AudioProcessorParameter::sendValueChangedMessageToListeners (float newValue | |||
| if (auto* l = listeners [i]) | |||
| l->parameterValueChanged (getParameterIndex(), newValue); | |||
| // audioProcessorParameterChanged callbacks will shortly be deprecated and | |||
| // this code will be removed. | |||
| for (int i = processor->listeners.size(); --i >= 0;) | |||
| if (auto* l = processor->listeners[i]) | |||
| l->audioProcessorParameterChanged (processor, getParameterIndex(), newValue); | |||
| if (processor != nullptr && parameterIndex >= 0) | |||
| { | |||
| // audioProcessorParameterChanged callbacks will shortly be deprecated and | |||
| // this code will be removed. | |||
| for (int i = processor->listeners.size(); --i >= 0;) | |||
| if (auto* l = processor->listeners[i]) | |||
| l->audioProcessorParameterChanged (processor, getParameterIndex(), newValue); | |||
| } | |||
| } | |||
| bool AudioProcessorParameter::isOrientationInverted() const { return false; } | |||
| @@ -962,14 +962,14 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use the | |||
| AudioProcessorParameter class instead to manage your parameters. | |||
| */ | |||
| virtual int getNumParameters(); | |||
| JUCE_DEPRECATED (virtual int getNumParameters()); | |||
| /** Returns the name of a particular parameter. | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use the | |||
| AudioProcessorParameter class instead to manage your parameters. | |||
| */ | |||
| virtual const String getParameterName (int parameterIndex); | |||
| JUCE_DEPRECATED (virtual const String getParameterName (int parameterIndex)); | |||
| /** Returns the ID of a particular parameter. | |||
| @@ -980,7 +980,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use the | |||
| AudioProcessorParameterWithID class instead to manage your parameters. | |||
| */ | |||
| virtual String getParameterID (int index); | |||
| JUCE_DEPRECATED (virtual String getParameterID (int index)); | |||
| /** Called by the host to find out the value of one of the processor's parameters. | |||
| @@ -993,7 +993,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use the | |||
| AudioProcessorParameter class instead to manage your parameters. | |||
| */ | |||
| virtual float getParameter (int parameterIndex); | |||
| JUCE_DEPRECATED (virtual float getParameter (int parameterIndex)); | |||
| /** Returns the name of a parameter as a text string with a preferred maximum length. | |||
| If you want to provide customised short versions of your parameter names that | |||
| @@ -1005,13 +1005,13 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::getName() instead. | |||
| */ | |||
| virtual String getParameterName (int parameterIndex, int maximumStringLength); | |||
| JUCE_DEPRECATED (virtual String getParameterName (int parameterIndex, int maximumStringLength)); | |||
| /** Returns the value of a parameter as a text string. | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::getText() instead. | |||
| */ | |||
| virtual const String getParameterText (int parameterIndex); | |||
| JUCE_DEPRECATED (virtual const String getParameterText (int parameterIndex)); | |||
| /** Returns the value of a parameter as a text string with a preferred maximum length. | |||
| If you want to provide customised short versions of your parameter values that | |||
| @@ -1023,7 +1023,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::getText() instead. | |||
| */ | |||
| virtual String getParameterText (int parameterIndex, int maximumStringLength); | |||
| JUCE_DEPRECATED (virtual String getParameterText (int parameterIndex, int maximumStringLength)); | |||
| /** Returns the number of discrete steps that this parameter can represent. | |||
| @@ -1043,7 +1043,7 @@ public: | |||
| @see isParameterDiscrete | |||
| */ | |||
| virtual int getParameterNumSteps (int parameterIndex); | |||
| JUCE_DEPRECATED (virtual int getParameterNumSteps (int parameterIndex)); | |||
| /** Returns the default number of steps for a parameter. | |||
| @@ -1067,7 +1067,7 @@ public: | |||
| @see getParameterNumSteps | |||
| */ | |||
| virtual bool isParameterDiscrete (int parameterIndex) const; | |||
| JUCE_DEPRECATED (virtual bool isParameterDiscrete (int parameterIndex) const); | |||
| /** Returns the default value for the parameter. | |||
| By default, this just returns 0. | |||
| @@ -1076,7 +1076,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::getDefaultValue() instead. | |||
| */ | |||
| virtual float getParameterDefaultValue (int parameterIndex); | |||
| JUCE_DEPRECATED (virtual float getParameterDefaultValue (int parameterIndex)); | |||
| /** Some plugin types may be able to return a label string for a | |||
| parameter's units. | |||
| @@ -1084,7 +1084,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::getLabel() instead. | |||
| */ | |||
| virtual String getParameterLabel (int index) const; | |||
| JUCE_DEPRECATED (virtual String getParameterLabel (int index) const); | |||
| /** This can be overridden to tell the host that particular parameters operate in the | |||
| reverse direction. (Not all plugin formats or hosts will actually use this information). | |||
| @@ -1092,7 +1092,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::isOrientationInverted() instead. | |||
| */ | |||
| virtual bool isParameterOrientationInverted (int index) const; | |||
| JUCE_DEPRECATED (virtual bool isParameterOrientationInverted (int index) const); | |||
| /** The host will call this method to change the value of one of the processor's parameters. | |||
| @@ -1110,7 +1110,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::setValue() instead. | |||
| */ | |||
| virtual void setParameter (int parameterIndex, float newValue); | |||
| JUCE_DEPRECATED (virtual void setParameter (int parameterIndex, float newValue)); | |||
| /** Your processor can call this when it needs to change one of its parameters. | |||
| @@ -1133,7 +1133,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::isAutomatable() instead. | |||
| */ | |||
| virtual bool isParameterAutomatable (int parameterIndex) const; | |||
| JUCE_DEPRECATED (virtual bool isParameterAutomatable (int parameterIndex) const); | |||
| /** Should return true if this parameter is a "meta" parameter. | |||
| A meta-parameter is a parameter that changes other params. It is used | |||
| @@ -1143,7 +1143,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::isMetaParameter() instead. | |||
| */ | |||
| virtual bool isMetaParameter (int parameterIndex) const; | |||
| JUCE_DEPRECATED (virtual bool isMetaParameter (int parameterIndex) const); | |||
| /** Should return the parameter's category. | |||
| By default, this returns the "generic" category. | |||
| @@ -1151,7 +1151,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::getCategory() instead. | |||
| */ | |||
| virtual AudioProcessorParameter::Category getParameterCategory (int parameterIndex) const; | |||
| JUCE_DEPRECATED (virtual AudioProcessorParameter::Category getParameterCategory (int parameterIndex) const); | |||
| /** Sends a signal to the host to tell it that the user is about to start changing this | |||
| parameter. | |||
| @@ -1164,7 +1164,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::beginChangeGesture() instead. | |||
| */ | |||
| void beginParameterChangeGesture (int parameterIndex); | |||
| JUCE_DEPRECATED (void beginParameterChangeGesture (int parameterIndex)); | |||
| /** Tells the host that the user has finished changing this parameter. | |||
| @@ -1176,7 +1176,7 @@ public: | |||
| NOTE! This method will eventually be deprecated! It's recommended that you use | |||
| AudioProcessorParameter::endChangeGesture() instead. | |||
| */ | |||
| void endParameterChangeGesture (int parameterIndex); | |||
| JUCE_DEPRECATED (void endParameterChangeGesture (int parameterIndex)); | |||
| /** The processor can call this when something (apart from a parameter value) has changed. | |||
| @@ -1626,8 +1626,6 @@ private: | |||
| friend class AudioUnitPluginInstance; | |||
| friend class LADSPAPluginInstance; | |||
| Atomic<int> vst3IsPlaying { 0 }; | |||
| // This method is no longer used - you can delete it from your AudioProcessor classes. | |||
| JUCE_DEPRECATED_WITH_BODY (virtual bool silenceInProducesSilenceOut() const, { return false; }) | |||
| @@ -27,166 +27,6 @@ | |||
| namespace juce | |||
| { | |||
| class ProcessorParameterPropertyComp : public PropertyComponent, | |||
| private AudioProcessorListener, | |||
| private Timer | |||
| { | |||
| public: | |||
| ProcessorParameterPropertyComp (const String& name, AudioProcessor& p, int paramIndex) | |||
| : PropertyComponent (name), | |||
| owner (p), | |||
| index (paramIndex), | |||
| slider (p, paramIndex) | |||
| { | |||
| startTimer (100); | |||
| addAndMakeVisible (slider); | |||
| owner.addListener (this); | |||
| } | |||
| ~ProcessorParameterPropertyComp() | |||
| { | |||
| owner.removeListener (this); | |||
| } | |||
| void refresh() override | |||
| { | |||
| paramHasChanged = false; | |||
| if (slider.getThumbBeingDragged() < 0) | |||
| slider.setValue (owner.getParameter (index), dontSendNotification); | |||
| slider.updateText(); | |||
| } | |||
| void audioProcessorChanged (AudioProcessor*) override {} | |||
| void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float) override | |||
| { | |||
| if (parameterIndex == index) | |||
| paramHasChanged = true; | |||
| } | |||
| void timerCallback() override | |||
| { | |||
| if (paramHasChanged) | |||
| { | |||
| refresh(); | |||
| startTimerHz (50); | |||
| } | |||
| else | |||
| { | |||
| startTimer (jmin (1000 / 4, getTimerInterval() + 10)); | |||
| } | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| class ParamSlider : public Slider | |||
| { | |||
| public: | |||
| ParamSlider (AudioProcessor& p, int paramIndex) : owner (p), index (paramIndex) | |||
| { | |||
| auto steps = owner.getParameterNumSteps (index); | |||
| auto category = p.getParameterCategory (index); | |||
| bool isLevelMeter = (((category & 0xffff0000) >> 16) == 2); | |||
| if (steps > 1 && steps < 0x7fffffff) | |||
| setRange (0.0, 1.0, 1.0 / (steps - 1.0)); | |||
| else | |||
| setRange (0.0, 1.0); | |||
| setEnabled (! isLevelMeter); | |||
| setSliderStyle (Slider::LinearBar); | |||
| setTextBoxIsEditable (false); | |||
| setScrollWheelEnabled (true); | |||
| } | |||
| void valueChanged() override | |||
| { | |||
| auto newVal = static_cast<float> (getValue()); | |||
| if (owner.getParameter (index) != newVal) | |||
| { | |||
| owner.setParameterNotifyingHost (index, newVal); | |||
| updateText(); | |||
| } | |||
| } | |||
| void startedDragging() override | |||
| { | |||
| owner.beginParameterChangeGesture (index); | |||
| } | |||
| void stoppedDragging() override | |||
| { | |||
| owner.endParameterChangeGesture (index); | |||
| } | |||
| String getTextFromValue (double /*value*/) override | |||
| { | |||
| return (owner.getParameterText (index) + " " + owner.getParameterLabel (index)).trimEnd(); | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| AudioProcessor& owner; | |||
| const int index; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParamSlider) | |||
| }; | |||
| AudioProcessor& owner; | |||
| const int index; | |||
| bool volatile paramHasChanged = false; | |||
| ParamSlider slider; | |||
| 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 | |||
| { | |||
| @@ -615,7 +455,7 @@ private: | |||
| class ParametersPanel : public Component | |||
| { | |||
| public: | |||
| ParametersPanel (const OwnedArray<AudioProcessorParameter>& parameters) | |||
| ParametersPanel (const Array<AudioProcessorParameter*>& parameters) | |||
| { | |||
| for (auto* param : parameters) | |||
| if (param->isAutomatable()) | |||
| @@ -647,25 +487,40 @@ private: | |||
| }; | |||
| //============================================================================== | |||
| GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const p) | |||
| : AudioProcessorEditor (p) | |||
| struct GenericAudioProcessorEditor::Pimpl | |||
| { | |||
| jassert (p != nullptr); | |||
| setOpaque (true); | |||
| Pimpl (GenericAudioProcessorEditor& parent) | |||
| : owner (parent) | |||
| { | |||
| auto* p = parent.getAudioProcessor(); | |||
| jassert (p != nullptr); | |||
| auto& parameters = p->getParameters(); | |||
| juceParameters.update (*p, false); | |||
| if (parameters.size() == p->getNumParameters()) | |||
| view.setViewedComponent (new ParametersPanel (parameters)); | |||
| else | |||
| view.setViewedComponent (new LegacyParametersPanel (p)); | |||
| owner.setOpaque (true); | |||
| addAndMakeVisible (view); | |||
| view.setViewedComponent (new ParametersPanel (juceParameters.params)); | |||
| owner.addAndMakeVisible (view); | |||
| view.setScrollBarsShown (true, false); | |||
| setSize (view.getViewedComponent()->getWidth() + view.getVerticalScrollBar().getWidth(), | |||
| jmin (view.getViewedComponent()->getHeight(), 400)); | |||
| } | |||
| view.setScrollBarsShown (true, false); | |||
| owner.setSize (view.getViewedComponent()->getWidth() + view.getVerticalScrollBar().getWidth(), | |||
| jmin (view.getViewedComponent()->getHeight(), 400)); | |||
| } | |||
| //============================================================================== | |||
| GenericAudioProcessorEditor& owner; | |||
| LegacyAudioParametersWrapper juceParameters; | |||
| Viewport view; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
| }; | |||
| //============================================================================== | |||
| GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const p) | |||
| : AudioProcessorEditor (p), pimpl (new Pimpl (*this)) | |||
| {} | |||
| GenericAudioProcessorEditor::~GenericAudioProcessorEditor() {} | |||
| @@ -676,7 +531,7 @@ void GenericAudioProcessorEditor::paint (Graphics& g) | |||
| void GenericAudioProcessorEditor::resized() | |||
| { | |||
| view.setBounds (getLocalBounds()); | |||
| pimpl->view.setBounds (getLocalBounds()); | |||
| } | |||
| } // namespace juce | |||
| @@ -52,7 +52,8 @@ public: | |||
| private: | |||
| //============================================================================== | |||
| Viewport view; | |||
| struct Pimpl; | |||
| ScopedPointer<Pimpl> pimpl; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericAudioProcessorEditor) | |||
| }; | |||