| @@ -537,13 +537,15 @@ namespace AAXClasses | |||||
| //============================================================================== | //============================================================================== | ||||
| class JuceAAX_Processor : public AAX_CEffectParameters, | class JuceAAX_Processor : public AAX_CEffectParameters, | ||||
| public juce::AudioPlayHead, | public juce::AudioPlayHead, | ||||
| public AudioProcessorListener | |||||
| public AudioProcessorListener, | |||||
| private AsyncUpdater | |||||
| { | { | ||||
| public: | public: | ||||
| JuceAAX_Processor() | JuceAAX_Processor() | ||||
| : pluginInstance (createPluginFilterOfType (AudioProcessor::wrapperType_AAX)), | : pluginInstance (createPluginFilterOfType (AudioProcessor::wrapperType_AAX)), | ||||
| isPrepared (false), | isPrepared (false), | ||||
| sampleRate (0), lastBufferSize (1024), maxBufferSize (1024) | |||||
| sampleRate (0), lastBufferSize (1024), maxBufferSize (1024), | |||||
| hasSidechain (false), canDisableSidechain (false) | |||||
| { | { | ||||
| pluginInstance->setPlayHead (this); | pluginInstance->setPlayHead (this); | ||||
| pluginInstance->addListener (this); | pluginInstance->addListener (this); | ||||
| @@ -561,9 +563,13 @@ namespace AAXClasses | |||||
| AAX_Result Uninitialize() override | AAX_Result Uninitialize() override | ||||
| { | { | ||||
| cancelPendingUpdate(); | |||||
| if (isPrepared && pluginInstance != nullptr) | if (isPrepared && pluginInstance != nullptr) | ||||
| { | { | ||||
| isPrepared = false; | isPrepared = false; | ||||
| processingSidechainChange.set (0); | |||||
| pluginInstance->releaseResources(); | pluginInstance->releaseResources(); | ||||
| } | } | ||||
| @@ -572,9 +578,13 @@ namespace AAXClasses | |||||
| AAX_Result EffectInit() override | AAX_Result EffectInit() override | ||||
| { | { | ||||
| cancelPendingUpdate(); | |||||
| AAX_Result err; | AAX_Result err; | ||||
| check (Controller()->GetSampleRate (&sampleRate)); | check (Controller()->GetSampleRate (&sampleRate)); | ||||
| processingSidechainChange.set (0); | |||||
| if ((err = preparePlugin()) != AAX_SUCCESS) | if ((err = preparePlugin()) != AAX_SUCCESS) | ||||
| return err; | return err; | ||||
| @@ -947,7 +957,24 @@ namespace AAXClasses | |||||
| const int numOuts = pluginInstance->getTotalNumOutputChannels(); | const int numOuts = pluginInstance->getTotalNumOutputChannels(); | ||||
| const int numMeters = aaxMeters.size(); | const int numMeters = aaxMeters.size(); | ||||
| if (pluginInstance->isSuspended()) | |||||
| const bool processWantsSidechain = (sideChainBufferIdx != -1); | |||||
| bool isSuspended = pluginInstance->isSuspended(); | |||||
| if (processingSidechainChange.get() == 0) | |||||
| { | |||||
| if (hasSidechain && canDisableSidechain | |||||
| && (sidechainDesired.get() != 0) != processWantsSidechain) | |||||
| { | |||||
| isSuspended = true; | |||||
| sidechainDesired.set (processWantsSidechain ? 1 : 0); | |||||
| processingSidechainChange.set (1); | |||||
| triggerAsyncUpdate(); | |||||
| } | |||||
| } | |||||
| else | |||||
| isSuspended = true; | |||||
| if (isSuspended) | |||||
| { | { | ||||
| for (int i = 0; i < numOuts; ++i) | for (int i = 0; i < numOuts; ++i) | ||||
| FloatVectorOperations::clear (outputs[i], bufferSize); | FloatVectorOperations::clear (outputs[i], bufferSize); | ||||
| @@ -1007,38 +1034,39 @@ namespace AAXClasses | |||||
| // In aax, the format of the aux and sidechain buses need to be fully determined | // In aax, the format of the aux and sidechain buses need to be fully determined | ||||
| // by the format on the main buses. This function tried to provide such a mapping. | // by the format on the main buses. This function tried to provide such a mapping. | ||||
| // Returns false if the in/out main layout is not supported | // Returns false if the in/out main layout is not supported | ||||
| static bool fullBusesLayoutFromMainLayout (AudioProcessor& p, | |||||
| static bool fullBusesLayoutFromMainLayout (const AudioProcessor& p, | |||||
| const AudioChannelSet& mainInput, const AudioChannelSet& mainOutput, | const AudioChannelSet& mainInput, const AudioChannelSet& mainOutput, | ||||
| AudioProcessor::BusesLayout& fullLayout) | AudioProcessor::BusesLayout& fullLayout) | ||||
| { | { | ||||
| bool success = p.setBusesLayout (getDefaultLayout (p, true)); | |||||
| AudioProcessor::BusesLayout currentLayout = getDefaultLayout (p, true); | |||||
| bool success = p.checkBusesLayoutSupported (currentLayout); | |||||
| jassert (success); | jassert (success); | ||||
| ignoreUnused (success); | ignoreUnused (success); | ||||
| const int numInputBuses = p.getBusCount (true); | const int numInputBuses = p.getBusCount (true); | ||||
| const int numOutputBuses = p.getBusCount (false); | const int numOutputBuses = p.getBusCount (false); | ||||
| if (AudioProcessor::Bus* bus = p.getBus (true, 0)) | |||||
| if (! bus->setCurrentLayout (mainInput)) | |||||
| if (const AudioProcessor::Bus* bus = p.getBus (true, 0)) | |||||
| if (! bus->isLayoutSupported (mainInput, ¤tLayout)) | |||||
| return false; | return false; | ||||
| if (AudioProcessor::Bus* bus = p.getBus (false, 0)) | |||||
| if (! bus->setCurrentLayout (mainOutput)) | |||||
| if (const AudioProcessor::Bus* bus = p.getBus (false, 0)) | |||||
| if (! bus->isLayoutSupported (mainOutput, ¤tLayout)) | |||||
| return false; | return false; | ||||
| // did this change the input again | // did this change the input again | ||||
| if (numInputBuses > 0 && p.getChannelLayoutOfBus (true, 0) != mainInput) | |||||
| if (numInputBuses > 0 && currentLayout.inputBuses.getReference (0) != mainInput) | |||||
| return false; | return false; | ||||
| #ifdef JucePlugin_PreferredChannelConfigurations | #ifdef JucePlugin_PreferredChannelConfigurations | ||||
| short configs[][2] = {JucePlugin_PreferredChannelConfigurations}; | short configs[][2] = {JucePlugin_PreferredChannelConfigurations}; | ||||
| if (! AudioProcessor::containsLayout (p.getBusesLayout(), configs)) | |||||
| if (! AudioProcessor::containsLayout (currentLayout, configs)) | |||||
| return false; | return false; | ||||
| #endif | #endif | ||||
| bool foundValid = false; | bool foundValid = false; | ||||
| { | { | ||||
| AudioProcessor::BusesLayout onlyMains = p.getBusesLayout(); | |||||
| AudioProcessor::BusesLayout onlyMains = currentLayout; | |||||
| for (int i = 1; i < numInputBuses; ++i) | for (int i = 1; i < numInputBuses; ++i) | ||||
| onlyMains.inputBuses.getReference (i) = AudioChannelSet::disabled(); | onlyMains.inputBuses.getReference (i) = AudioChannelSet::disabled(); | ||||
| @@ -1056,43 +1084,43 @@ namespace AAXClasses | |||||
| if (numInputBuses > 1) | if (numInputBuses > 1) | ||||
| { | { | ||||
| // can the first bus be a sidechain or disabled, if not then we can't use this layout combination | // can the first bus be a sidechain or disabled, if not then we can't use this layout combination | ||||
| if (AudioProcessor::Bus* bus = p.getBus (true, 1)) | |||||
| if (! bus->setCurrentLayout (AudioChannelSet::mono()) && ! bus->setCurrentLayout (AudioChannelSet::disabled())) | |||||
| if (const AudioProcessor::Bus* bus = p.getBus (true, 1)) | |||||
| if (! bus->isLayoutSupported (AudioChannelSet::mono(), ¤tLayout) && ! bus->isLayoutSupported (AudioChannelSet::disabled(), ¤tLayout)) | |||||
| return foundValid; | return foundValid; | ||||
| // can all the other inputs be disabled, if not then we can't use this layout combination | // can all the other inputs be disabled, if not then we can't use this layout combination | ||||
| for (int i = 2; i < numInputBuses; ++i) | for (int i = 2; i < numInputBuses; ++i) | ||||
| if (AudioProcessor::Bus* bus = p.getBus (true, i)) | |||||
| if (! bus->setCurrentLayout (AudioChannelSet::disabled())) | |||||
| if (const AudioProcessor::Bus* bus = p.getBus (true, i)) | |||||
| if (! bus->isLayoutSupported (AudioChannelSet::disabled(), ¤tLayout)) | |||||
| return foundValid; | return foundValid; | ||||
| if (AudioProcessor::Bus* bus = p.getBus (true, 0)) | |||||
| if (! bus->setCurrentLayout (mainInput)) | |||||
| if (const AudioProcessor::Bus* bus = p.getBus (true, 0)) | |||||
| if (! bus->isLayoutSupported (mainInput, ¤tLayout)) | |||||
| return foundValid; | return foundValid; | ||||
| if (AudioProcessor::Bus* bus = p.getBus (false, 0)) | |||||
| if (! bus->setCurrentLayout (mainOutput)) | |||||
| if (const AudioProcessor::Bus* bus = p.getBus (false, 0)) | |||||
| if (! bus->isLayoutSupported (mainOutput, ¤tLayout)) | |||||
| return foundValid; | return foundValid; | ||||
| // recheck if the format is correct | // recheck if the format is correct | ||||
| if ((numInputBuses > 0 && p.getChannelLayoutOfBus (true, 0) != mainInput) | |||||
| || (numOutputBuses > 0 && p.getChannelLayoutOfBus (false, 0) != mainOutput)) | |||||
| if ((numInputBuses > 0 && currentLayout.inputBuses .getReference (0) != mainInput) | |||||
| || (numOutputBuses > 0 && currentLayout.outputBuses.getReference (0) != mainOutput)) | |||||
| return foundValid; | return foundValid; | ||||
| const AudioChannelSet& sidechainBus = p.getChannelLayoutOfBus (true, 1); | |||||
| const AudioChannelSet& sidechainBus = currentLayout.inputBuses.getReference (1); | |||||
| if (sidechainBus != AudioChannelSet::mono() && sidechainBus != AudioChannelSet::disabled()) | if (sidechainBus != AudioChannelSet::mono() && sidechainBus != AudioChannelSet::disabled()) | ||||
| return foundValid; | return foundValid; | ||||
| for (int i = 2; i < numInputBuses; ++i) | for (int i = 2; i < numInputBuses; ++i) | ||||
| if (p.getChannelLayoutOfBus (true, i) != AudioChannelSet::disabled()) | |||||
| if (currentLayout.outputBuses.getReference (i) != AudioChannelSet::disabled()) | |||||
| return foundValid; | return foundValid; | ||||
| } | } | ||||
| const bool hasSidechain = (numInputBuses > 1 && p.getChannelLayoutOfBus (true, 1) == AudioChannelSet::mono()); | |||||
| const bool hasSidechain = (numInputBuses > 1 && currentLayout.inputBuses.getReference (1) == AudioChannelSet::mono()); | |||||
| if (hasSidechain) | if (hasSidechain) | ||||
| { | { | ||||
| AudioProcessor::BusesLayout onlyMainsAndSidechain = p.getBusesLayout(); | |||||
| AudioProcessor::BusesLayout onlyMainsAndSidechain = currentLayout; | |||||
| for (int i = 1; i < numOutputBuses; ++i) | for (int i = 1; i < numOutputBuses; ++i) | ||||
| onlyMainsAndSidechain.outputBuses.getReference (i) = AudioChannelSet::disabled(); | onlyMainsAndSidechain.outputBuses.getReference (i) = AudioChannelSet::disabled(); | ||||
| @@ -1106,7 +1134,7 @@ namespace AAXClasses | |||||
| if (numOutputBuses > 1) | if (numOutputBuses > 1) | ||||
| { | { | ||||
| AudioProcessor::BusesLayout copy = p.getBusesLayout(); | |||||
| AudioProcessor::BusesLayout copy = currentLayout; | |||||
| int maxAuxBuses = jmin (16, numOutputBuses); | int maxAuxBuses = jmin (16, numOutputBuses); | ||||
| for (int i = 1; i < maxAuxBuses; ++i) | for (int i = 1; i < maxAuxBuses; ++i) | ||||
| @@ -1123,38 +1151,38 @@ namespace AAXClasses | |||||
| else | else | ||||
| { | { | ||||
| for (int i = 1; i < maxAuxBuses; ++i) | for (int i = 1; i < maxAuxBuses; ++i) | ||||
| if (p.getChannelLayoutOfBus (false, i).isDisabled()) | |||||
| if (currentLayout.outputBuses.getReference (i).isDisabled()) | |||||
| return foundValid; | return foundValid; | ||||
| for (int i = maxAuxBuses; i < numOutputBuses; ++i) | for (int i = maxAuxBuses; i < numOutputBuses; ++i) | ||||
| if (AudioProcessor::Bus* bus = p.getBus (false, i)) | |||||
| if (! bus->setCurrentLayout (AudioChannelSet::disabled())) | |||||
| if (const AudioProcessor::Bus* bus = p.getBus (false, i)) | |||||
| if (! bus->isLayoutSupported (AudioChannelSet::disabled(), ¤tLayout)) | |||||
| return foundValid; | return foundValid; | ||||
| if (AudioProcessor::Bus* bus = p.getBus (true, 0)) | |||||
| if (! bus->setCurrentLayout (mainInput)) | |||||
| if (const AudioProcessor::Bus* bus = p.getBus (true, 0)) | |||||
| if (! bus->isLayoutSupported (mainInput, ¤tLayout)) | |||||
| return foundValid; | return foundValid; | ||||
| if (AudioProcessor::Bus* bus = p.getBus (false, 0)) | |||||
| if (! bus->setCurrentLayout (mainOutput)) | |||||
| if (const AudioProcessor::Bus* bus = p.getBus (false, 0)) | |||||
| if (! bus->isLayoutSupported (mainOutput, ¤tLayout)) | |||||
| return foundValid; | return foundValid; | ||||
| if ((numInputBuses > 0 && p.getChannelLayoutOfBus (true, 0) != mainInput) | |||||
| || (numOutputBuses > 0 && p.getChannelLayoutOfBus (false, 0) != mainOutput)) | |||||
| if ((numInputBuses > 0 && currentLayout.inputBuses .getReference (0) != mainInput) | |||||
| || (numOutputBuses > 0 && currentLayout.outputBuses.getReference (0) != mainOutput)) | |||||
| return foundValid; | return foundValid; | ||||
| if (numInputBuses > 1 ) | |||||
| if (numInputBuses > 1) | |||||
| { | { | ||||
| const AudioChannelSet& sidechainBus = p.getChannelLayoutOfBus (true, 1); | |||||
| const AudioChannelSet& sidechainBus = currentLayout.inputBuses.getReference (1); | |||||
| if (sidechainBus != AudioChannelSet::mono() && sidechainBus != AudioChannelSet::disabled()) | if (sidechainBus != AudioChannelSet::mono() && sidechainBus != AudioChannelSet::disabled()) | ||||
| return foundValid; | return foundValid; | ||||
| } | } | ||||
| for (int i = maxAuxBuses; i < numOutputBuses; ++i) | for (int i = maxAuxBuses; i < numOutputBuses; ++i) | ||||
| if (! p.getChannelLayoutOfBus (false, i).isDisabled()) | |||||
| if (! currentLayout.outputBuses.getReference (i).isDisabled()) | |||||
| return foundValid; | return foundValid; | ||||
| fullLayout = p.getBusesLayout(); | |||||
| fullLayout = currentLayout; | |||||
| foundValid = true; | foundValid = true; | ||||
| } | } | ||||
| } | } | ||||
| @@ -1383,6 +1411,23 @@ namespace AAXClasses | |||||
| return AAX_ERROR_UNIMPLEMENTED; | return AAX_ERROR_UNIMPLEMENTED; | ||||
| } | } | ||||
| hasSidechain = (newLayout.getNumChannels (true, 1) == 1); | |||||
| if (hasSidechain) | |||||
| { | |||||
| sidechainDesired.set (1); | |||||
| AudioProcessor::BusesLayout disabledSidechainLayout (newLayout); | |||||
| disabledSidechainLayout.inputBuses.getReference (1) = AudioChannelSet::disabled(); | |||||
| canDisableSidechain = audioProcessor.checkBusesLayoutSupported (disabledSidechainLayout); | |||||
| if (canDisableSidechain) | |||||
| { | |||||
| sidechainDesired.set (0); | |||||
| newLayout = disabledSidechainLayout; | |||||
| } | |||||
| } | |||||
| const bool layoutChanged = (oldLayout != newLayout); | const bool layoutChanged = (oldLayout != newLayout); | ||||
| if (layoutChanged) | if (layoutChanged) | ||||
| @@ -1412,10 +1457,8 @@ namespace AAXClasses | |||||
| audioProcessor.setRateAndBufferSizeDetails (sampleRate, lastBufferSize); | audioProcessor.setRateAndBufferSizeDetails (sampleRate, lastBufferSize); | ||||
| audioProcessor.prepareToPlay (sampleRate, lastBufferSize); | audioProcessor.prepareToPlay (sampleRate, lastBufferSize); | ||||
| maxBufferSize = lastBufferSize; | maxBufferSize = lastBufferSize; | ||||
| hasSidechain = audioProcessor.getChannelLayoutOfBus (true, 1) == AudioChannelSet::mono(); | |||||
| if (hasSidechain) | |||||
| sideChainBuffer.calloc (static_cast<size_t> (maxBufferSize)); | |||||
| sideChainBuffer.calloc (static_cast<size_t> (maxBufferSize)); | |||||
| } | } | ||||
| check (Controller()->SetSignalLatency (audioProcessor.getLatencySamples())); | check (Controller()->SetSignalLatency (audioProcessor.getLatencySamples())); | ||||
| @@ -1477,6 +1520,33 @@ namespace AAXClasses | |||||
| meterTapBuffers); | meterTapBuffers); | ||||
| } | } | ||||
| } | } | ||||
| //============================================================================== | |||||
| void handleAsyncUpdate() override | |||||
| { | |||||
| if (processingSidechainChange.get() == 0) | |||||
| return; | |||||
| AudioProcessor& audioProcessor = getPluginInstance(); | |||||
| const bool sidechainActual = (audioProcessor.getChannelCountOfBus (true, 1) > 0); | |||||
| if (hasSidechain && canDisableSidechain && (sidechainDesired.get() != 0) != sidechainActual) | |||||
| { | |||||
| if (isPrepared) | |||||
| { | |||||
| isPrepared = false; | |||||
| audioProcessor.releaseResources(); | |||||
| } | |||||
| if (AudioProcessor::Bus* bus = audioProcessor.getBus (true, 1)) | |||||
| bus->setCurrentLayout (sidechainDesired.get() != 0 ? AudioChannelSet::mono() : AudioChannelSet::disabled()); | |||||
| audioProcessor.prepareToPlay (audioProcessor.getSampleRate(), audioProcessor.getBlockSize()); | |||||
| isPrepared = true; | |||||
| } | |||||
| processingSidechainChange.set (0); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| inline int getParamIndexFromID (AAX_CParamID paramID) const noexcept | inline int getParamIndexFromID (AAX_CParamID paramID) const noexcept | ||||
| @@ -1492,7 +1562,7 @@ namespace AAXClasses | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| static AudioProcessor::BusesLayout getDefaultLayout (AudioProcessor& p, bool enableAll) | |||||
| static AudioProcessor::BusesLayout getDefaultLayout (const AudioProcessor& p, bool enableAll) | |||||
| { | { | ||||
| AudioProcessor::BusesLayout defaultLayout; | AudioProcessor::BusesLayout defaultLayout; | ||||
| @@ -1503,7 +1573,7 @@ namespace AAXClasses | |||||
| Array<AudioChannelSet>& layouts = (isInput ? defaultLayout.inputBuses : defaultLayout.outputBuses); | Array<AudioChannelSet>& layouts = (isInput ? defaultLayout.inputBuses : defaultLayout.outputBuses); | ||||
| for (int i = 0; i < n; ++i) | for (int i = 0; i < n; ++i) | ||||
| if (AudioProcessor::Bus* bus = p.getBus (isInput, i)) | |||||
| if (const AudioProcessor::Bus* bus = p.getBus (isInput, i)) | |||||
| layouts.add (enableAll || bus->isEnabledByDefault() ? bus->getDefaultLayout() : AudioChannelSet()); | layouts.add (enableAll || bus->isEnabledByDefault() ? bus->getDefaultLayout() : AudioChannelSet()); | ||||
| } | } | ||||
| @@ -1535,7 +1605,10 @@ namespace AAXClasses | |||||
| int32_t juceChunkIndex; | int32_t juceChunkIndex; | ||||
| AAX_CSampleRate sampleRate; | AAX_CSampleRate sampleRate; | ||||
| int lastBufferSize, maxBufferSize; | int lastBufferSize, maxBufferSize; | ||||
| bool hasSidechain; | |||||
| bool hasSidechain, canDisableSidechain; | |||||
| Atomic<int> processingSidechainChange, sidechainDesired; | |||||
| HeapBlock<float> sideChainBuffer; | HeapBlock<float> sideChainBuffer; | ||||
| Array<int> inputLayoutMap, outputLayoutMap; | Array<int> inputLayoutMap, outputLayoutMap; | ||||
| @@ -1694,12 +1767,15 @@ namespace AAXClasses | |||||
| check (desc.AddPrivateData (JUCEAlgorithmIDs::pluginInstance, sizeof (PluginInstanceInfo))); | check (desc.AddPrivateData (JUCEAlgorithmIDs::pluginInstance, sizeof (PluginInstanceInfo))); | ||||
| check (desc.AddPrivateData (JUCEAlgorithmIDs::preparedFlag, sizeof (int32_t))); | check (desc.AddPrivateData (JUCEAlgorithmIDs::preparedFlag, sizeof (int32_t))); | ||||
| HeapBlock<AAX_CTypeID> meterIDs (static_cast<size_t> (numMeters)); | |||||
| if (numMeters > 0) | |||||
| { | |||||
| HeapBlock<AAX_CTypeID> meterIDs (static_cast<size_t> (numMeters)); | |||||
| for (int i = 0; i < numMeters; ++i) | |||||
| meterIDs[i] = 'Metr' + static_cast<AAX_CTypeID> (i); | |||||
| for (int i = 0; i < numMeters; ++i) | |||||
| meterIDs[i] = 'Metr' + static_cast<AAX_CTypeID> (i); | |||||
| check (desc.AddMeters (JUCEAlgorithmIDs::meterTapBuffers, meterIDs.getData(), static_cast<uint32_t> (numMeters))); | |||||
| check (desc.AddMeters (JUCEAlgorithmIDs::meterTapBuffers, meterIDs.getData(), static_cast<uint32_t> (numMeters))); | |||||
| } | |||||
| // Create a property map | // Create a property map | ||||
| AAX_IPropertyMap* const properties = desc.NewPropertyMap(); | AAX_IPropertyMap* const properties = desc.NewPropertyMap(); | ||||