| @@ -4,6 +4,25 @@ JUCE breaking changes | |||||
| Develop | Develop | ||||
| ======= | ======= | ||||
| Change | |||||
| ------ | |||||
| AudioProcessor::getTailLengthSeconds can now return infinity for VST/VST3/AU/AUv3 | |||||
| Possible Issues | |||||
| --------------- | |||||
| If you are using the result of getTailLengthSeconds to allocate a buffer in your host, | |||||
| then your host will now likely crash when loading a plug-in with an infinite tail time. | |||||
| Workaround | |||||
| ---------- | |||||
| Re-write your code to not use the result of getTailLengthSeconds directly to allocate | |||||
| a buffer. | |||||
| Rationale | |||||
| --------- | |||||
| Before this change there was no way for a JUCE plug-in to report an infinite tail time. | |||||
| Version 5.3.2 | Version 5.3.2 | ||||
| ============= | ============= | ||||
| @@ -317,12 +317,15 @@ public: | |||||
| if (processor->supportsDoublePrecisionProcessing()) | if (processor->supportsDoublePrecisionProcessing()) | ||||
| vstEffect.flags |= vstEffectFlagInplaceDoubleAudio; | vstEffect.flags |= vstEffectFlagInplaceDoubleAudio; | ||||
| vstEffect.flags |= vstEffectFlagDataInChunks; | |||||
| #if JucePlugin_IsSynth | #if JucePlugin_IsSynth | ||||
| vstEffect.flags |= vstEffectFlagIsSynth; | vstEffect.flags |= vstEffectFlagIsSynth; | ||||
| #else | |||||
| if (processor->getTailLengthSeconds() == 0.0) | |||||
| vstEffect.flags |= vstEffectFlagSilenceInProducesSilenceOut; | |||||
| #endif | #endif | ||||
| vstEffect.flags |= vstEffectFlagDataInChunks; | |||||
| activePlugins.add (this); | activePlugins.add (this); | ||||
| } | } | ||||
| @@ -588,7 +591,7 @@ public: | |||||
| if (getHostType().isAbletonLive() | if (getHostType().isAbletonLive() | ||||
| && hostCallback != nullptr | && hostCallback != nullptr | ||||
| && processor->getTailLengthSeconds() == std::numeric_limits<double>::max()) | |||||
| && processor->getTailLengthSeconds() == std::numeric_limits<double>::infinity()) | |||||
| { | { | ||||
| AbletonLiveHostSpecific hostCmd; | AbletonLiveHostSpecific hostCmd; | ||||
| @@ -2046,7 +2049,18 @@ private: | |||||
| pointer_sized_int handleGetTailSize (VstOpCodeArguments) | pointer_sized_int handleGetTailSize (VstOpCodeArguments) | ||||
| { | { | ||||
| if (processor != nullptr) | if (processor != nullptr) | ||||
| return (pointer_sized_int) (processor->getTailLengthSeconds() * sampleRate); | |||||
| { | |||||
| int32 result; | |||||
| auto tailSeconds = processor->getTailLengthSeconds(); | |||||
| if (tailSeconds == std::numeric_limits<double>::infinity()) | |||||
| result = std::numeric_limits<int32>::max(); | |||||
| else | |||||
| result = static_cast<int32> (tailSeconds * sampleRate); | |||||
| return result; // Vst2 expects an int32 upcasted to a intptr_t here | |||||
| } | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -2033,6 +2033,9 @@ public: | |||||
| if (tailLengthSeconds <= 0.0 || processSetup.sampleRate <= 0.0) | if (tailLengthSeconds <= 0.0 || processSetup.sampleRate <= 0.0) | ||||
| return Vst::kNoTail; | return Vst::kNoTail; | ||||
| if (tailLengthSeconds == std::numeric_limits<double>::infinity()) | |||||
| return Vst::kInfiniteTail; | |||||
| return (Steinberg::uint32) roundToIntAccurate (tailLengthSeconds * processSetup.sampleRate); | return (Steinberg::uint32) roundToIntAccurate (tailLengthSeconds * processSetup.sampleRate); | ||||
| } | } | ||||
| @@ -2296,7 +2296,14 @@ struct VST3PluginInstance : public AudioPluginInstance | |||||
| auto sampleRate = getSampleRate(); | auto sampleRate = getSampleRate(); | ||||
| if (sampleRate > 0.0) | if (sampleRate > 0.0) | ||||
| { | |||||
| auto tailSamples = processor->getTailSamples(); | |||||
| if (tailSamples == Vst::kInfiniteTail) | |||||
| return std::numeric_limits<double>::infinity(); | |||||
| return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / sampleRate; | return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / sampleRate; | ||||
| } | |||||
| } | } | ||||
| return 0.0; | return 0.0; | ||||
| @@ -83,11 +83,12 @@ typedef pointer_sized_int (VSTINTERFACECALL* VstHostCallback) (VstEffectInterfac | |||||
| enum VstEffectInterfaceFlags | enum VstEffectInterfaceFlags | ||||
| { | { | ||||
| vstEffectFlagHasEditor = 1, | |||||
| vstEffectFlagInplaceAudio = 16, | |||||
| vstEffectFlagDataInChunks = 32, | |||||
| vstEffectFlagIsSynth = 256, | |||||
| vstEffectFlagInplaceDoubleAudio = 4096 | |||||
| vstEffectFlagHasEditor = 1, | |||||
| vstEffectFlagInplaceAudio = 16, | |||||
| vstEffectFlagDataInChunks = 32, | |||||
| vstEffectFlagIsSynth = 256, | |||||
| vstEffectFlagSilenceInProducesSilenceOut = 512, | |||||
| vstEffectFlagInplaceDoubleAudio = 4096 | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -1283,12 +1283,23 @@ struct VSTPluginInstance : public AudioPluginInstance, | |||||
| if (vstEffect == nullptr) | if (vstEffect == nullptr) | ||||
| return 0.0; | return 0.0; | ||||
| if ((vstEffect->flags & vstEffectFlagSilenceInProducesSilenceOut) != 0) | |||||
| return 0.0; | |||||
| auto tailSize = dispatch (plugInOpcodeGetTailSize, 0, 0, 0, 0); | |||||
| auto sampleRate = getSampleRate(); | auto sampleRate = getSampleRate(); | ||||
| if (sampleRate <= 0) | |||||
| return 0.0; | |||||
| // remain backward compatible with old JUCE plug-ins: anything larger | |||||
| // than INT32_MAX is an invalid tail time but old JUCE 64-bit plug-ins | |||||
| // would return INT64_MAX for infinite tail time. So treat anything | |||||
| // equal or greater than INT32_MAX as infinite tail time. | |||||
| if (tailSize >= std::numeric_limits<int32>::max()) | |||||
| return std::numeric_limits<double>::infinity(); | |||||
| if (tailSize >= 0 && sampleRate > 0) | |||||
| return static_cast<double> (tailSize) / sampleRate; | |||||
| return dispatch (plugInOpcodeGetTailSize, 0, 0, 0, 0) / sampleRate; | |||||
| return 0.0; | |||||
| } | } | ||||
| bool acceptsMidi() const override { return wantsMidiMessages; } | bool acceptsMidi() const override { return wantsMidiMessages; } | ||||