diff --git a/distrho/DistrhoInfo.hpp b/distrho/DistrhoInfo.hpp index 050cbd3c..28ec90f3 100644 --- a/distrho/DistrhoInfo.hpp +++ b/distrho/DistrhoInfo.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2024 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -667,6 +667,29 @@ START_NAMESPACE_DISTRHO */ #define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" +/** + The AudioUnit type for a plugin.@n + This is a 4-character symbol, automatically set by DPF based on other plugin macros. + See https://developer.apple.com/documentation/audiotoolbox/1584142-audio_unit_types for more information. + */ +#define DISTRHO_PLUGIN_AU_TYPE aufx + +/** + The AudioUnit subtype for a plugin.@n + This is a 4-character symbol which identifies a plugin.@n + It must be unique within a manufacturer's plugins, but different manufacturers can use the same subtype. + @note This macro is required when building AU plugins + */ +#define DISTRHO_PLUGIN_AU_SUBTYPE test + +/** + The AudioUnit manufacturer for a plugin.@n + This is a 4-character symbol with at least one non-lower case character.@n + Plugins from the same brand/maker should use the same symbol. + @note This macro is required when building AU plugins + */ +#define DISTRHO_PLUGIN_AU_MANUFACTURER Dstr + /** Custom LV2 category for the plugin.@n This is a single string, and can be one of the following values: diff --git a/distrho/src/DistrhoPluginAU.cpp b/distrho/src/DistrhoPluginAU.cpp index 895e6c85..7da3eed7 100644 --- a/distrho/src/DistrhoPluginAU.cpp +++ b/distrho/src/DistrhoPluginAU.cpp @@ -22,9 +22,16 @@ #include "../DistrhoPluginUtils.hpp" #include +#include #include -#define TRACE d_stderr("////////--------------------------------------------------------------- %s %d", __PRETTY_FUNCTION__, __LINE__); +#ifndef DISTRHO_PLUGIN_AU_SUBTYPE +# error DISTRHO_PLUGIN_AU_SUBTYPE undefined! +#endif + +#ifndef DISTRHO_PLUGIN_AU_MANUFACTURER +# error DISTRHO_PLUGIN_AU_MANUFACTURER undefined! +#endif START_NAMESPACE_DISTRHO @@ -154,6 +161,27 @@ static const char* AudioUnitSelector2Str(const SInt16 selector) noexcept return "[unknown]"; } +static constexpr FourCharCode getFourCharCodeFromString(const char str[4]) +{ + return (str[0] << 24) + (str[1] << 16) + (str[2] << 8) + str[3]; +} + +// -------------------------------------------------------------------------------------------------------------------- + +#define MACRO_STR2(s) #s +#define MACRO_STR(s) MACRO_STR2(s) + +static constexpr const char kTypeStr[] = MACRO_STR(DISTRHO_PLUGIN_AU_TYPE); +static constexpr const char kSubTypeStr[] = MACRO_STR(DISTRHO_PLUGIN_AU_SUBTYPE); +static constexpr const char kManufacturerStr[] = MACRO_STR(DISTRHO_PLUGIN_AU_MANUFACTURER); + +#undef MACRO_STR +#undef MACRO_STR2 + +static constexpr const FourCharCode kType = getFourCharCodeFromString(kTypeStr); +static constexpr const FourCharCode kSubType = getFourCharCodeFromString(kSubTypeStr); +static constexpr const FourCharCode kManufacturer = getFourCharCodeFromString(kManufacturerStr); + // -------------------------------------------------------------------------------------------------------------------- struct PropertyListener { @@ -179,47 +207,60 @@ static constexpr const updateStateValueFunc updateStateValueCallback = nullptr; class PluginAU { public: - PluginAU(const AudioUnit component, const AudioComponentDescription* const description) + PluginAU(const AudioUnit component) : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, updateStateValueCallback), fComponent(component), - fComponentDescription(description), - fParameterCount(fPlugin.getParameterCount()), - fCachedParameterValues(nullptr), fLastRenderError(noErr), - fPropertyListeners() + fPropertyListeners(), + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + fSampleRateForInput(d_nextSampleRate), + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + fSampleRateForOutput(d_nextSampleRate), + #endif + fParameterCount(fPlugin.getParameterCount()), + fLastParameterValues(nullptr), + fBypassParameterIndex(UINT32_MAX) { if (fParameterCount != 0) { - fCachedParameterValues = new float[fParameterCount]; - std::memset(fCachedParameterValues, 0, sizeof(float) * fParameterCount); + fLastParameterValues = new float[fParameterCount]; + std::memset(fLastParameterValues, 0, sizeof(float) * fParameterCount); for (uint32_t i=0; icomponentType; + value = kType; if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) { CFDictionarySetValue(dict, CFSTR(kAUPresetTypeKey), num); CFRelease(num); } - value = fComponentDescription->componentSubType; + value = kSubType; if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) { CFDictionarySetValue(dict, CFSTR(kAUPresetSubtypeKey), num); CFRelease(num); } - value = fComponentDescription->componentManufacturer; + value = kManufacturer; if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) { CFDictionarySetValue(dict, CFSTR(kAUPresetManufacturerKey), num); @@ -365,6 +468,7 @@ public: if (const CFMutableDataRef data = CFDataCreateMutable(nullptr, 0)) { // TODO save plugin state here + d_stdout("WIP GetProperty(%d:%s, %d:%s, %d, ...)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); CFDictionarySetValue(dict, CFSTR(kAUPresetDataKey), data); CFRelease(data); @@ -376,9 +480,27 @@ public: } return noErr; + #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 case kAudioUnitProperty_SampleRate: - *static_cast(outData) = fPlugin.getSampleRate(); + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + if (inScope == kAudioUnitScope_Input) + { + *static_cast(outData) = fSampleRateForInput; + } + else + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + if (inScope == kAudioUnitScope_Output) + { + *static_cast(outData) = fSampleRateForOutput; + } + else + #endif + { + return kAudioUnitErr_InvalidScope; + } return noErr; + #endif case kAudioUnitProperty_ParameterList: { @@ -446,6 +568,7 @@ public: } return noErr; + #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 case kAudioUnitProperty_StreamFormat: { AudioStreamBasicDescription* const desc = static_cast(outData); @@ -481,6 +604,7 @@ public: desc->mFramesPerPacket = 1; } return noErr; + #endif case kAudioUnitProperty_ElementCount: switch (inScope) @@ -519,7 +643,12 @@ public: fLastRenderError = noErr; return noErr; + case kAudioUnitProperty_BypassEffect: + *static_cast(outData) = fPlugin.getParameterValue(fBypassParameterIndex) > 0.5f ? 1 : 0; + return noErr; + case kAudioUnitProperty_SetRenderCallback: + d_stdout("WIP GetProperty(%d:%s, %d:%s, %d, ...)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); // TODO break; @@ -544,7 +673,18 @@ public: encoding:NSUTF8StringEncoding]; info->mCocoaAUViewBundleLocation = static_cast([[NSURL fileURLWithPath: bundlePathString] retain]); - info->mCocoaAUViewClass[0] = CFSTR("DPF_UI_ViewFactory"); + + #define MACRO_STR3(a, b, c) a "_" b "_" c + #define MACRO_STR2(a, b, c) MACRO_STR3(#a, #b, #c) + #define MACRO_STR(a, b, c) MACRO_STR2(a, b, c) + + info->mCocoaAUViewClass[0] = CFSTR("CocoaAUView_" MACRO_STR(DISTRHO_PLUGIN_AU_TYPE, + DISTRHO_PLUGIN_AU_SUBTYPE, + DISTRHO_PLUGIN_AU_MANUFACTURER)); + + #undef MACRO_STR + #undef MACRO_STR2 + #undef MACRO_STR3 [bundlePathString release]; } @@ -552,38 +692,92 @@ public: #endif } + d_stdout("TODO GetProperty(%d:%s, %d:%s, %d, ...)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); return kAudioUnitErr_InvalidProperty; } - OSStatus auSetProperty(const AudioUnitPropertyID prop, + OSStatus auSetProperty(const AudioUnitPropertyID inProp, const AudioUnitScope inScope, const AudioUnitElement inElement, const void* const inData, const UInt32 inDataSize) { - switch (prop) + switch (inProp) { case kAudioUnitProperty_ClassInfo: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFPropertyListRef), inDataSize, kAudioUnitErr_InvalidPropertyValue); + d_stdout("WIP SetProperty(%d:%s, %d:%s, %d, %p, %u)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); // TODO return noErr; case kAudioUnitProperty_MakeConnection: + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global || inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AudioUnitConnection), inDataSize, kAudioUnitErr_InvalidPropertyValue); + d_stdout("WIP SetProperty(%d:%s, %d:%s, %d, %p, %u)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); // TODO return noErr; + #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 case kAudioUnitProperty_SampleRate: + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope); + #elif DISTRHO_PLUGIN_NUM_INPUTS != 0 + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); + #else + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope); + #endif DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(Float64), inDataSize, kAudioUnitErr_InvalidPropertyValue); + { + const Float64 sampleRate = *static_cast(inData); - if (fPlugin.setSampleRate(*static_cast(inData), true)) - notifyListerners(kAudioUnitProperty_SampleRate, inScope, inElement); + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + if (inScope == kAudioUnitScope_Input) + { + if (d_isNotEqual(fSampleRateForInput, sampleRate)) + { + fSampleRateForInput = sampleRate; - return noErr; + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + if (d_isEqual(fSampleRateForOutput, sampleRate)) + #endif + { + fPlugin.setSampleRate(sampleRate, true); + } + notifyListeners(inProp, inScope, inElement); + } + return noErr; + } + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + if (inScope == kAudioUnitScope_Output) + { + if (d_isNotEqual(fSampleRateForOutput, sampleRate)) + { + fSampleRateForOutput = sampleRate; + + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + if (d_isEqual(fSampleRateForInput, sampleRate)) + #endif + { + fPlugin.setSampleRate(sampleRate, true); + } + + notifyListeners(inProp, inScope, inElement); + } + return noErr; + } + #endif + } + return kAudioUnitErr_InvalidScope; + #endif + + #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 case kAudioUnitProperty_StreamFormat: #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope); @@ -621,10 +815,48 @@ public: : DISTRHO_PLUGIN_NUM_OUTPUTS)) return kAudioUnitErr_FormatNotSupported; - if (fPlugin.setSampleRate(desc->mSampleRate, true)) - notifyListerners(kAudioUnitProperty_StreamFormat, inScope, inElement); + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + if (inScope == kAudioUnitScope_Input) + { + if (d_isNotEqual(fSampleRateForInput, desc->mSampleRate)) + { + fSampleRateForInput = desc->mSampleRate; + + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + if (d_isEqual(fSampleRateForOutput, desc->mSampleRate)) + #endif + { + fPlugin.setSampleRate(desc->mSampleRate, true); + } + + notifyListeners(inProp, inScope, inElement); + } + return noErr; + } + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + if (inScope == kAudioUnitScope_Output) + { + if (d_isNotEqual(fSampleRateForOutput, desc->mSampleRate)) + { + fSampleRateForOutput = desc->mSampleRate; + + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + if (d_isEqual(fSampleRateForInput, desc->mSampleRate)) + #endif + { + fPlugin.setSampleRate(desc->mSampleRate, true); + } + + notifyListeners(inProp, inScope, inElement); + } + return noErr; + } + #endif } - return noErr; + return kAudioUnitErr_InvalidScope; + #endif case kAudioUnitProperty_MaximumFramesPerSlice: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); @@ -632,13 +864,33 @@ public: DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(UInt32), inDataSize, kAudioUnitErr_InvalidPropertyValue); if (fPlugin.setBufferSize(*static_cast(inData), true)) - notifyListerners(kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0); + notifyListeners(inProp, inScope, inElement); return noErr; + case kAudioUnitProperty_BypassEffect: + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(UInt32), inDataSize, kAudioUnitErr_InvalidPropertyValue); + DISTRHO_SAFE_ASSERT_RETURN(fBypassParameterIndex != UINT32_MAX, kAudioUnitErr_InvalidPropertyValue); + { + const bool bypass = *static_cast(inData) != 0; + + if ((fLastParameterValues[fBypassParameterIndex] > 0.5f) != bypass) + { + const float value = bypass ? 1.f : 0.f; + fLastParameterValues[fBypassParameterIndex] = value; + fPlugin.setParameterValue(fBypassParameterIndex, value); + notifyListeners(inProp, inScope, inElement); + } + } + return noErr; + case kAudioUnitProperty_SetRenderCallback: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AURenderCallbackStruct), inDataSize, kAudioUnitErr_InvalidPropertyValue); + d_stdout("WIP SetProperty(%d:%s, %d:%s, %d, %p, %u)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); // TODO return noErr; @@ -646,10 +898,53 @@ public: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AUPreset), inDataSize, kAudioUnitErr_InvalidPropertyValue); + d_stdout("WIP SetProperty(%d:%s, %d:%s, %d, %p, %u)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); // TODO return noErr; + + case 19001: + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(float), inDataSize, kAudioUnitErr_InvalidPropertyValue); + { + const float value = *static_cast(inData); + DISTRHO_SAFE_ASSERT_RETURN(std::isfinite(value), kAudioUnitErr_InvalidParameterValue); + + fLastParameterValues[inElement] = value; + fPlugin.setParameterValue(inElement, value); + + AudioUnitEvent event; + std::memset(&event, 0, sizeof(event)); + + event.mEventType = kAudioUnitEvent_ParameterValueChange; + event.mArgument.mParameter.mAudioUnit = fComponent; + event.mArgument.mParameter.mParameterID = inElement; + event.mArgument.mParameter.mScope = kAudioUnitScope_Global; + AUEventListenerNotify(NULL, NULL, &event); + } + return noErr; + + case 19002: + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(bool), inDataSize, kAudioUnitErr_InvalidPropertyValue); + { + const bool started = *static_cast(inData); + + AudioUnitEvent event; + std::memset(&event, 0, sizeof(event)); + + event.mEventType = started ? kAudioUnitEvent_BeginParameterChangeGesture + : kAudioUnitEvent_EndParameterChangeGesture; + event.mArgument.mParameter.mAudioUnit = fComponent; + event.mArgument.mParameter.mParameterID = inElement; + event.mArgument.mParameter.mScope = kAudioUnitScope_Global; + AUEventListenerNotify(NULL, NULL, &event); + } + return noErr; } + d_stdout("TODO SetProperty(%d:%s, %d:%s, %d, %p, %u)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); return kAudioUnitErr_InvalidProperty; } @@ -704,12 +999,14 @@ public: OSStatus auAddRenderNotify(const AURenderCallback proc, void* const userData) { + d_stdout("WIP AddRenderNotify(%p, %p)", proc, userData); // TODO return noErr; } OSStatus auRemoveRenderNotify(const AURenderCallback proc, void* const userData) { + d_stdout("WIP RemoveRenderNotify(%p, %p)", proc, userData); // TODO return noErr; } @@ -737,14 +1034,39 @@ public: DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(param < fParameterCount, param, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement); + DISTRHO_SAFE_ASSERT_RETURN(std::isfinite(value), kAudioUnitErr_InvalidParameterValue); + + if (d_isNotEqual(fLastParameterValues[param], value)) + { + fLastParameterValues[param] = value; + fPlugin.setParameterValue(param, value); + // TODO flag param only, notify listeners later on bg thread (sem_post etc) + notifyListeners(19003, kAudioUnitScope_Global, param); + } - fPlugin.setParameterValue(param, value); return noErr; } OSStatus auScheduleParameters(const AudioUnitParameterEvent* const events, const UInt32 numEvents) { - // TODO + for (UInt32 i=0; i(DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS), ioData.mNumberBuffers, kAudio_ParamError); + if (inFramesToProcess > fPlugin.getBufferSize()) { + setLastRenderError(kAudioUnitErr_TooManyFramesToProcess); return kAudioUnitErr_TooManyFramesToProcess; } + for (uint i=0; i(ioData.mBuffers[i].mData); + } + #else + constexpr const float** inputs = nullptr; + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; + + for (uint16_t i=0; i(ioData.mBuffers[i].mData); + } + #else + constexpr float** outputs = nullptr; + #endif + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + fPlugin.run(inputs, outputs, inFramesToProcess, nullptr, 0); + #else + fPlugin.run(inputs, outputs, inFramesToProcess); + #endif return noErr; } @@ -768,6 +1132,7 @@ public: DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement); // TODO + d_stdout("WIP Reset(%d:%s, %d)", scope, AudioUnitScope2Str(scope), elem); return noErr; } @@ -787,10 +1152,10 @@ protected: { value = fPlugin.getParameterValue(i); - if (d_isEqual(fCachedParameterValues[i], value)) + if (d_isEqual(fLastParameterValues[i], value)) continue; - fCachedParameterValues[i] = value; + fLastParameterValues[i] = value; if (AUElement* const elem = GlobalScope().GetElement(0)) elem->SetParameter(i, value); @@ -806,16 +1171,27 @@ protected: private: PluginExporter fPlugin; + // AU component const AudioUnit fComponent; - const AudioComponentDescription* const fComponentDescription; - const uint32_t fParameterCount; - float* fCachedParameterValues; + + // AUv2 related fields OSStatus fLastRenderError; PropertyListeners fPropertyListeners; + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + Float64 fSampleRateForInput; + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + Float64 fSampleRateForOutput; + #endif + + // Caching + const uint32_t fParameterCount; + float* fLastParameterValues; + uint32_t fBypassParameterIndex; // ---------------------------------------------------------------------------------------------------------------- - void notifyListerners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem) + void notifyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem) { for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it) { @@ -832,7 +1208,7 @@ private: return; fLastRenderError = err; - notifyListerners(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0); + notifyListeners(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0); } // ---------------------------------------------------------------------------------------------------------------- @@ -846,7 +1222,7 @@ private: static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent) { - return static_cast(ptr)->writeMidi(midiEvent); + return static_cast(ptr)->writeMidi(midiEvent); } #endif @@ -858,7 +1234,7 @@ private: static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) { - return static_cast(ptr)->requestParameterValueChange(index, value); + return static_cast(ptr)->requestParameterValueChange(index, value); } #endif @@ -870,7 +1246,7 @@ private: static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value) { - return static_cast(ptr)->updateState(key, value); + return static_cast(ptr)->updateState(key, value); } #endif @@ -881,19 +1257,17 @@ private: struct AudioComponentPlugInInstance { AudioComponentPlugInInterface acpi; - const AudioComponentDescription* const description; PluginAU* plugin; - AudioComponentPlugInInstance(const AudioComponentDescription* const desc) noexcept + AudioComponentPlugInInstance() noexcept : acpi(), - description(desc), plugin(nullptr) { std::memset(&acpi, 0, sizeof(acpi)); acpi.Open = Open; acpi.Close = Close; acpi.Lookup = Lookup; - acpi.reserved = NULL; + acpi.reserved = nullptr; } ~AudioComponentPlugInInstance() @@ -903,8 +1277,7 @@ struct AudioComponentPlugInInstance { static OSStatus Open(void* const self, const AudioUnit component) { - AudioComponentPlugInInstance* const selfx = static_cast(self); - selfx->plugin = new PluginAU(component, selfx->description); + static_cast(self)->plugin = new PluginAU(component); return noErr; } @@ -916,7 +1289,7 @@ struct AudioComponentPlugInInstance { static AudioComponentMethod Lookup(const SInt16 selector) { - d_stdout("AudioComponentPlugInInstance::Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector)); + d_debug("AudioComponentPlugInInstance::Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector)); switch (selector) { @@ -962,34 +1335,35 @@ struct AudioComponentPlugInInstance { */ } + d_stdout("TODO Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector)); return nullptr; } static OSStatus Initialize(AudioComponentPlugInInstance* const self) { - d_stdout("AudioComponentPlugInInstance::Initialize(%p)", self); + d_debug("AudioComponentPlugInInstance::Initialize(%p)", self); return self->plugin->auInitialize(); } static OSStatus Uninitialize(AudioComponentPlugInInstance* const self) { - d_stdout("AudioComponentPlugInInstance::Uninitialize(%p)", self); + d_debug("AudioComponentPlugInInstance::Uninitialize(%p)", self); return self->plugin->auUninitialize(); } static OSStatus GetPropertyInfo(AudioComponentPlugInInstance* const self, - const AudioUnitPropertyID prop, + const AudioUnitPropertyID inProp, const AudioUnitScope inScope, const AudioUnitElement inElement, UInt32* const outDataSize, Boolean* const outWritable) { - d_stdout("AudioComponentPlugInInstance::GetPropertyInfo(%p, %2d:%s, %d:%s, %d, ...)", - self, prop, AudioUnitPropertyID2Str(prop), inScope, AudioUnitScope2Str(inScope), inElement); + d_debug("AudioComponentPlugInInstance::GetPropertyInfo(%p, %2d:%s, %d:%s, %d, ...)", + self, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); UInt32 dataSize = 0; Boolean writable = false; - const OSStatus res = self->plugin->auGetPropertyInfo(prop, inScope, inElement, dataSize, writable); + const OSStatus res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, dataSize, writable); if (outDataSize != nullptr) *outDataSize = dataSize; @@ -1001,14 +1375,14 @@ struct AudioComponentPlugInInstance { } static OSStatus GetProperty(AudioComponentPlugInInstance* const self, - const AudioUnitPropertyID prop, + const AudioUnitPropertyID inProp, const AudioUnitScope inScope, const AudioUnitElement inElement, void* const outData, UInt32* const ioDataSize) { - d_stdout("AudioComponentPlugInInstance::GetProperty (%p, %2d:%s, %d:%s, %d, ...)", - self, prop, AudioUnitPropertyID2Str(prop), inScope, AudioUnitScope2Str(inScope), inElement); + d_debug("AudioComponentPlugInInstance::GetProperty (%p, %2d:%s, %d:%s, %d, ...)", + self, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); DISTRHO_SAFE_ASSERT_RETURN(ioDataSize != nullptr, kAudio_ParamError); Boolean writable; @@ -1017,7 +1391,7 @@ struct AudioComponentPlugInInstance { if (outData == nullptr) { - res = self->plugin->auGetPropertyInfo(prop, inScope, inElement, outDataSize, writable); + res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, outDataSize, writable); *ioDataSize = outDataSize; return res; } @@ -1026,7 +1400,7 @@ struct AudioComponentPlugInInstance { if (inDataSize == 0) return kAudio_ParamError; - res = self->plugin->auGetPropertyInfo(prop, inScope, inElement, outDataSize, writable); + res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, outDataSize, writable); if (res != noErr) return res; @@ -1044,7 +1418,7 @@ struct AudioComponentPlugInInstance { outBuffer = outData; } - res = self->plugin->auGetProperty(prop, inScope, inElement, outBuffer); + res = self->plugin->auGetProperty(inProp, inScope, inElement, outBuffer); if (res != noErr) { @@ -1066,15 +1440,15 @@ struct AudioComponentPlugInInstance { } static OSStatus SetProperty(AudioComponentPlugInInstance* const self, - const AudioUnitPropertyID prop, + const AudioUnitPropertyID inProp, const AudioUnitScope inScope, const AudioUnitElement inElement, const void* const inData, const UInt32 inDataSize) { - d_stdout("AudioComponentPlugInInstance::SetProperty(%p, %d:%s, %d:%s, %d, %p, %u)", - self, prop, AudioUnitPropertyID2Str(prop), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); - return self->plugin->auSetProperty(prop, inScope, inElement, inData, inDataSize); + d_debug("AudioComponentPlugInInstance::SetProperty(%p, %d:%s, %d:%s, %d, %p, %u)", + self, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); + return self->plugin->auSetProperty(inProp, inScope, inElement, inData, inDataSize); } static OSStatus AddPropertyListener(AudioComponentPlugInInstance* const self, @@ -1082,8 +1456,8 @@ struct AudioComponentPlugInInstance { const AudioUnitPropertyListenerProc proc, void* const userData) { - d_stdout("AudioComponentPlugInInstance::AddPropertyListener(%p, %d:%s, %p, %p)", - self, prop, AudioUnitPropertyID2Str(prop), proc, userData); + d_debug("AudioComponentPlugInInstance::AddPropertyListener(%p, %d:%s, %p, %p)", + self, prop, AudioUnitPropertyID2Str(prop), proc, userData); return self->plugin->auAddPropertyListener(prop, proc, userData); } @@ -1091,8 +1465,8 @@ struct AudioComponentPlugInInstance { const AudioUnitPropertyID prop, const AudioUnitPropertyListenerProc proc) { - d_stdout("AudioComponentPlugInInstance::RemovePropertyListener(%p, %d:%s, %p)", - self, prop, AudioUnitPropertyID2Str(prop), proc); + d_debug("AudioComponentPlugInInstance::RemovePropertyListener(%p, %d:%s, %p)", + self, prop, AudioUnitPropertyID2Str(prop), proc); return self->plugin->auRemovePropertyListener(prop, proc); } @@ -1101,8 +1475,8 @@ struct AudioComponentPlugInInstance { const AudioUnitPropertyListenerProc proc, void* const userData) { - d_stdout("AudioComponentPlugInInstance::RemovePropertyListenerWithUserData(%p, %d:%s, %p, %p)", - self, prop, AudioUnitPropertyID2Str(prop), proc, userData); + d_debug("AudioComponentPlugInInstance::RemovePropertyListenerWithUserData(%p, %d:%s, %p, %p)", + self, prop, AudioUnitPropertyID2Str(prop), proc, userData); return self->plugin->auRemovePropertyListenerWithUserData(prop, proc, userData); } @@ -1110,8 +1484,7 @@ struct AudioComponentPlugInInstance { const AURenderCallback proc, void* const userData) { - d_stdout("AudioComponentPlugInInstance::AddRenderNotify(%p, %p, %p)", - self, proc, userData); + d_debug("AudioComponentPlugInInstance::AddRenderNotify(%p, %p, %p)", self, proc, userData); return self->plugin->auAddRenderNotify(proc, userData); } @@ -1119,8 +1492,7 @@ struct AudioComponentPlugInInstance { const AURenderCallback proc, void* const userData) { - d_stdout("AudioComponentPlugInInstance::RemoveRenderNotify(%p, %p, %p)", - self, proc, userData); + d_debug("AudioComponentPlugInInstance::RemoveRenderNotify(%p, %p, %p)", self, proc, userData); return self->plugin->auRemoveRenderNotify(proc, userData); } @@ -1130,8 +1502,8 @@ struct AudioComponentPlugInInstance { const AudioUnitElement elem, AudioUnitParameterValue* const value) { - d_stdout("AudioComponentPlugInInstance::GetParameter(%p, %d, %d:%s, %d, %p)", - self, param, scope, AudioUnitScope2Str(scope), elem, value); + d_debug("AudioComponentPlugInInstance::GetParameter(%p, %d, %d:%s, %d, %p)", + self, param, scope, AudioUnitScope2Str(scope), elem, value); return self->plugin->auGetParameter(param, scope, elem, value); } @@ -1142,8 +1514,8 @@ struct AudioComponentPlugInInstance { const AudioUnitParameterValue value, const UInt32 bufferOffset) { - d_stdout("AudioComponentPlugInInstance::SetParameter(%p, %d %d:%s, %d, %f, %u)", - self, param, scope, AudioUnitScope2Str(scope), elem, value, bufferOffset); + d_debug("AudioComponentPlugInInstance::SetParameter(%p, %d %d:%s, %d, %f, %u)", + self, param, scope, AudioUnitScope2Str(scope), elem, value, bufferOffset); return self->plugin->auSetParameter(param, scope, elem, value, bufferOffset); } @@ -1151,8 +1523,7 @@ struct AudioComponentPlugInInstance { const AudioUnitParameterEvent* const events, const UInt32 numEvents) { - d_stdout("AudioComponentPlugInInstance::ScheduleParameters(%p, %p, %u)", - self, events, numEvents); + d_debug("AudioComponentPlugInInstance::ScheduleParameters(%p, %p, %u)", self, events, numEvents); return self->plugin->auScheduleParameters(events, numEvents); } @@ -1160,8 +1531,7 @@ struct AudioComponentPlugInInstance { const AudioUnitScope scope, const AudioUnitElement elem) { - d_stdout("AudioComponentPlugInInstance::Reset(%p, %d:%s, %d)", - self, scope, AudioUnitScope2Str(scope), elem); + d_debug("AudioComponentPlugInInstance::Reset(%p, %d:%s, %d)", self, scope, AudioUnitScope2Str(scope), elem); return self->plugin->auReset(scope, elem); } @@ -1172,8 +1542,6 @@ struct AudioComponentPlugInInstance { const UInt32 inNumberFrames, AudioBufferList* const ioData) { - d_stdout("AudioComponentPlugInInstance::Render(%p, %p, %p, %u, %u, %p)", - self, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData); DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError); DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError); @@ -1196,16 +1564,19 @@ struct AudioComponentPlugInInstance { END_NAMESPACE_DISTRHO DISTRHO_PLUGIN_EXPORT -void* PluginAUFactory(const AudioComponentDescription* const description) +void* PluginAUFactory(const AudioComponentDescription* const desc) { USE_NAMESPACE_DISTRHO - TRACE + + DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentType == kType, desc->componentType, kType, nullptr); + DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentSubType == kSubType, desc->componentSubType, kSubType, nullptr); + DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentManufacturer == kManufacturer, desc->componentManufacturer, kManufacturer, nullptr); if (d_nextBufferSize == 0) d_nextBufferSize = 1156; if (d_isZero(d_nextSampleRate)) - d_nextSampleRate = 48000.0; + d_nextSampleRate = 44100.0; if (d_nextBundlePath == nullptr) { @@ -1230,7 +1601,7 @@ void* PluginAUFactory(const AudioComponentDescription* const description) d_nextCanRequestParameterValueChanges = true; - return new AudioComponentPlugInInstance(description); + return new AudioComponentPlugInInstance(); } // -------------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h index 4828a6b0..a7dc4eef 100644 --- a/distrho/src/DistrhoPluginChecks.h +++ b/distrho/src/DistrhoPluginChecks.h @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2023 Filipe Coelho + * Copyright (C) 2012-2024 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -23,7 +23,7 @@ #include "DistrhoPluginInfo.h" -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Check if all required macros are defined #ifndef DISTRHO_PLUGIN_NAME @@ -42,7 +42,7 @@ # error DISTRHO_PLUGIN_URI undefined! #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Define optional macros if not done yet #ifndef DISTRHO_PLUGIN_HAS_UI @@ -110,7 +110,26 @@ # define DISTRHO_UI_USE_NANOVG 0 #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- +// Define DISTRHO_PLUGIN_AU_TYPE if needed + +#ifndef DISTRHO_PLUGIN_AU_TYPE +# if DISTRHO_PLUGIN_IS_SYNTH +# define DISTRHO_PLUGIN_AU_TYPE aumu /* kAudioUnitType_MusicDevice */ +# elif (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) && DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 +# define DISTRHO_PLUGIN_AU_TYPE aumf /* kAudioUnitType_MusicEffect */ +# elif (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) && DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 +# define DISTRHO_PLUGIN_AU_TYPE aumu /* kAudioUnitType_MusicDevice */ +# elif (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) && DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 +# define DISTRHO_PLUGIN_AU_TYPE aumi /* kAudioUnitType_MIDIProcessor */ +# elif DISTRHO_PLUGIN_NUM_INPUTS == 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 +# define DISTRHO_PLUGIN_AU_TYPE augn /* kAudioUnitType_Generator */ +# else +# define DISTRHO_PLUGIN_AU_TYPE aufx /* kAudioUnitType_Effect */ +# endif +#endif + +// -------------------------------------------------------------------------------------------------------------------- // Define DISTRHO_PLUGIN_HAS_EMBED_UI if needed #ifndef DISTRHO_PLUGIN_HAS_EMBED_UI @@ -121,21 +140,21 @@ # endif #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Define DISTRHO_UI_URI if needed #ifndef DISTRHO_UI_URI # define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#DPF_UI" #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Test if synth has audio outputs #if DISTRHO_PLUGIN_IS_SYNTH && DISTRHO_PLUGIN_NUM_OUTPUTS == 0 # error Synths need audio output to work! #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Enable MIDI input if synth, test if midi-input disabled when synth #ifndef DISTRHO_PLUGIN_WANT_MIDI_INPUT @@ -144,7 +163,7 @@ # error Synths need MIDI input to work! #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Enable state if plugin wants state files (deprecated) #ifdef DISTRHO_PLUGIN_WANT_STATEFILES @@ -156,7 +175,7 @@ # endif #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Enable full state if plugin exports presets #if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && defined(DISTRHO_PLUGIN_WANT_FULL_STATE_WAS_NOT_SET) @@ -165,7 +184,7 @@ # define DISTRHO_PLUGIN_WANT_FULL_STATE 1 #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Disable file browser if using external UI #if DISTRHO_UI_FILE_BROWSER && DISTRHO_PLUGIN_HAS_EXTERNAL_UI @@ -174,7 +193,7 @@ # define DISTRHO_UI_FILE_BROWSER 0 #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Disable UI if DGL or external UI is not available #if (defined(DGL_CAIRO) && ! defined(HAVE_CAIRO)) || (defined(DGL_OPENGL) && ! defined(HAVE_OPENGL)) @@ -187,7 +206,7 @@ # define DISTRHO_PLUGIN_HAS_UI 0 #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Make sure both default width and height are provided #if defined(DISTRHO_UI_DEFAULT_WIDTH) && !defined(DISTRHO_UI_DEFAULT_HEIGHT) @@ -198,7 +217,7 @@ # error DISTRHO_UI_DEFAULT_HEIGHT is defined but DISTRHO_UI_DEFAULT_WIDTH is not #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Other UI defaults #ifndef DISTRHO_UI_USE_CAIRO @@ -209,13 +228,13 @@ # define DISTRHO_UI_USE_CUSTOM 0 #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // Prevent users from messing about with DPF internals #ifdef DISTRHO_UI_IS_STANDALONE # error DISTRHO_UI_IS_STANDALONE must not be defined #endif -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- #endif // DISTRHO_PLUGIN_CHECKS_H_INCLUDED diff --git a/distrho/src/DistrhoPluginExport.cpp b/distrho/src/DistrhoPluginExport.cpp index ccc15c63..a5793330 100644 --- a/distrho/src/DistrhoPluginExport.cpp +++ b/distrho/src/DistrhoPluginExport.cpp @@ -17,6 +17,14 @@ #include "DistrhoPluginInternal.hpp" #include "../DistrhoPluginUtils.hpp" +#ifndef DISTRHO_PLUGIN_AU_SUBTYPE +# error DISTRHO_PLUGIN_AU_SUBTYPE undefined! +#endif + +#ifndef DISTRHO_PLUGIN_AU_MANUFACTURER +# error DISTRHO_PLUGIN_AU_MANUFACTURER undefined! +#endif + #include #include @@ -37,6 +45,13 @@ void generate_au_plist(const PluginExporter& plugin, const uint32_t minorVersion = (version & 0x00FF00) >> 8; const uint32_t microVersion = (version & 0x0000FF) >> 0; + #define MACRO_STR2(s) #s + #define MACRO_STR(s) MACRO_STR2(s) + + static_assert(sizeof(MACRO_STR(DISTRHO_PLUGIN_AU_TYPE)) == 5, "The macro DISTRHO_PLUGIN_AU_TYPE has incorrect length"); + static_assert(sizeof(MACRO_STR(DISTRHO_PLUGIN_AU_SUBTYPE)) == 5, "The macro DISTRHO_PLUGIN_AU_SUBTYPE has incorrect length"); + static_assert(sizeof(MACRO_STR(DISTRHO_PLUGIN_AU_MANUFACTURER)) == 5, "The macro DISTRHO_PLUGIN_AU_MANUFACTURER has incorrect length"); + outputFile << "\n"; outputFile << "\n"; outputFile << "\n"; @@ -72,24 +87,12 @@ void generate_au_plist(const PluginExporter& plugin, outputFile << " " << plugin.getDescription() << "\n"; outputFile << " factoryFunction\n"; outputFile << " PluginAUFactory\n"; - outputFile << " manufacturer\n"; - outputFile << " Dstr\n"; outputFile << " type\n"; - #if DISTRHO_PLUGIN_IS_SYNTH - outputFile << " aumu\n"; // kAudioUnitType_MusicDevice - #elif (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) && DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 - outputFile << " aumf\n"; // kAudioUnitType_MusicEffect - #elif (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) && DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 - outputFile << " aumu\n"; // kAudioUnitType_MusicDevice - #elif (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) && DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 - outputFile << " aumi\n"; // kAudioUnitType_MIDIProcessor - #elif DISTRHO_PLUGIN_NUM_INPUTS == 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 - outputFile << " augn\n"; // kAudioUnitType_Generator - #else - outputFile << " aufx\n"; // kAudioUnitType_Effect - #endif + outputFile << " " MACRO_STR(DISTRHO_PLUGIN_AU_TYPE) "\n"; outputFile << " subtype\n"; - outputFile << " Cair\n"; + outputFile << " " MACRO_STR(DISTRHO_PLUGIN_AU_SUBTYPE) "\n"; + outputFile << " manufacturer\n"; + outputFile << " " MACRO_STR(DISTRHO_PLUGIN_AU_MANUFACTURER) "\n"; outputFile << " version\n"; outputFile << " " << version << "\n"; outputFile << " resourceUsage\n"; @@ -104,6 +107,9 @@ void generate_au_plist(const PluginExporter& plugin, outputFile << " \n"; outputFile << "\n"; + #undef MACRO_STR + #undef MACRO_STR2 + outputFile.close(); std::cout << " done!" << std::endl; } diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 1d71377a..5c0625a5 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -1017,14 +1017,14 @@ public: return true; } - bool setSampleRate(const double sampleRate, const bool doCallback = false) + void setSampleRate(const double sampleRate, const bool doCallback = false) { - DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, false); - DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, false); + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); DISTRHO_SAFE_ASSERT(sampleRate > 0.0); if (d_isEqual(fData->sampleRate, sampleRate)) - return false; + return; fData->sampleRate = sampleRate; @@ -1034,8 +1034,6 @@ public: fPlugin->sampleRateChanged(sampleRate); if (fIsActive) fPlugin->activate(); } - - return true; } private: diff --git a/distrho/src/DistrhoUIAU.mm b/distrho/src/DistrhoUIAU.mm index 5159a88b..bbf6ecd1 100644 --- a/distrho/src/DistrhoUIAU.mm +++ b/distrho/src/DistrhoUIAU.mm @@ -19,12 +19,20 @@ #define Point AudioUnitPoint #define Size AudioUnitSize -// #include +#include #include #undef Point #undef Size +#ifndef DISTRHO_PLUGIN_AU_SUBTYPE +# error DISTRHO_PLUGIN_AU_SUBTYPE undefined! +#endif + +#ifndef DISTRHO_PLUGIN_AU_MANUFACTURER +# error DISTRHO_PLUGIN_AU_MANUFACTURER undefined! +#endif + START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- @@ -47,11 +55,11 @@ extern const char* d_nextBundlePath; class DPF_UI_AU { public: - DPF_UI_AU(const AudioUnit inAU, + DPF_UI_AU(const AudioUnit component, const intptr_t winId, const double sampleRate, void* const instancePointer) - : fAU(inAU), + : fComponent(component), fTimerRef(nullptr), fUI(this, winId, sampleRate, editParameterCallback, @@ -72,10 +80,14 @@ public: DISTRHO_SAFE_ASSERT_RETURN(fTimerRef != nullptr,); CFRunLoopAddTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); + + AudioUnitAddPropertyListener(fComponent, 19003, auPropertyChangedCallback, this); } ~DPF_UI_AU() { + AudioUnitRemovePropertyListenerWithUserData(fComponent, 19003, auPropertyChangedCallback, this); + if (fTimerRef != nullptr) { CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); @@ -89,7 +101,7 @@ public: } private: - const AudioUnit fAU; + const AudioUnit fComponent; CFRunLoopTimerRef fTimerRef; UIExporter fUI; @@ -107,11 +119,44 @@ private: static_cast(info)->idleCallback(); } + // ---------------------------------------------------------------------------------------------------------------- + // AU callbacks + + void auPropertyChanged(const AudioUnitPropertyID prop, const AudioUnitElement elem) + { + switch (prop) + { + case 19003: + { + AudioUnitParameterValue value; + if (AudioUnitGetParameter(fComponent, elem, kAudioUnitScope_Global, 0, &value) == noErr) + fUI.parameterChanged(elem, value); + } + break; + } + } + + static void auPropertyChangedCallback(void* const userData, + const AudioUnit component, + const AudioUnitPropertyID prop, + const AudioUnitScope scope, + const AudioUnitElement elem) + { + DPF_UI_AU* const self = static_cast(userData); + + DISTRHO_SAFE_ASSERT_RETURN(self != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(self->fComponent == component,); + DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope,); + + self->auPropertyChanged(prop, elem); + } + // ---------------------------------------------------------------------------------------------------------------- // DPF callbacks - void editParameter(uint32_t, bool) const + void editParameter(const uint32_t rindex, const bool started) const { + AudioUnitSetProperty(fComponent, 19002, kAudioUnitScope_Global, rindex, &started, sizeof(bool)); } static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started) @@ -119,8 +164,9 @@ private: static_cast(ptr)->editParameter(rindex, started); } - void setParameterValue(uint32_t, float) + void setParameterValue(const uint32_t rindex, const float value) { + AudioUnitSetProperty(fComponent, 19001, kAudioUnitScope_Global, rindex, &value, sizeof(float)); } static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value) @@ -166,13 +212,18 @@ END_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- -@interface DPF_UI_ViewFactory : NSObject +#define MACRO_NAME2(a, b, c, d, e, f) a ## b ## c ## d ## e ## f +#define MACRO_NAME(a, b, c, d, e, f) MACRO_NAME2(a, b, c, d, e, f) + +#define COCOA_VIEW_CLASS_NAME MACRO_NAME(CocoaAUView_, DISTRHO_PLUGIN_AU_TYPE, _, DISTRHO_PLUGIN_AU_SUBTYPE, _, DISTRHO_PLUGIN_AU_MANUFACTURER) + +@interface COCOA_VIEW_CLASS_NAME : NSObject { DPF_UI_AU* ui; } @end -@implementation DPF_UI_ViewFactory +@implementation COCOA_VIEW_CLASS_NAME - (NSString*) description { @@ -184,17 +235,20 @@ END_NAMESPACE_DISTRHO return 0; } -- (NSView*) uiViewForAudioUnit:(AudioUnit)inAU withSize:(NSSize)inPreferredSize +- (NSView*) uiViewForAudioUnit:(AudioUnit)component withSize:(NSSize)inPreferredSize { const double sampleRate = d_nextSampleRate; const intptr_t winId = 0; void* const instancePointer = nullptr; - ui = new DPF_UI_AU(inAU, winId, sampleRate, instancePointer); + ui = new DPF_UI_AU(component, winId, sampleRate, instancePointer); return ui->getNativeView(); } @end +#undef MACRO_NAME +#undef MACRO_NAME2 + // -------------------------------------------------------------------------------------------------------------------- diff --git a/examples/CairoUI/DistrhoPluginInfo.h b/examples/CairoUI/DistrhoPluginInfo.h index 1966426c..78880caa 100644 --- a/examples/CairoUI/DistrhoPluginInfo.h +++ b/examples/CairoUI/DistrhoPluginInfo.h @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2024 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -39,6 +39,22 @@ */ #define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/CairoUI" +/** + The AudioUnit subtype for a plugin. + This is a 4-character symbol which identifies a plugin. + It must be unique within a manufacturer's plugins, but different manufacturers can use the same subtype. + @note This macro is required when building AU plugins + */ +#define DISTRHO_PLUGIN_AU_SUBTYPE cair + +/** + The AudioUnit manufacturer for a plugin. + This is a 4-character symbol with at least one non-lower case character. + Plugins from the same brand/maker should use the same symbol. + @note This macro is required when building AU plugins + */ +#define DISTRHO_PLUGIN_AU_MANUFACTURER Dstr + /** The plugin id when exporting in CLAP format, in reverse URI form. @note This macro is required when building CLAP plugins diff --git a/examples/Info/DistrhoPluginInfo.h b/examples/Info/DistrhoPluginInfo.h index a041f817..80f80f21 100644 --- a/examples/Info/DistrhoPluginInfo.h +++ b/examples/Info/DistrhoPluginInfo.h @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2024 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -22,6 +22,9 @@ #define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/Info" #define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.examples.info" +#define DISTRHO_PLUGIN_AU_SUBTYPE info +#define DISTRHO_PLUGIN_AU_MANUFACTURER Dstr + #define DISTRHO_PLUGIN_HAS_UI 1 #define DISTRHO_PLUGIN_IS_RT_SAFE 1 #define DISTRHO_PLUGIN_NUM_INPUTS 2 diff --git a/examples/Parameters/DistrhoPluginInfo.h b/examples/Parameters/DistrhoPluginInfo.h index b9b50d97..22f5497e 100644 --- a/examples/Parameters/DistrhoPluginInfo.h +++ b/examples/Parameters/DistrhoPluginInfo.h @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2024 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -22,6 +22,9 @@ #define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/Parameters" #define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.examples.parameters" +#define DISTRHO_PLUGIN_AU_SUBTYPE parm +#define DISTRHO_PLUGIN_AU_MANUFACTURER Dstr + #define DISTRHO_PLUGIN_HAS_UI 1 #define DISTRHO_PLUGIN_IS_RT_SAFE 1 #define DISTRHO_PLUGIN_NUM_INPUTS 2