|
- /*!
- @file AudioUnitSDK/AUBase.h
- @copyright © 2000-2021 Apple Inc. All rights reserved.
- */
-
- #ifndef AudioUnitSDK_AUBase_h
- #define AudioUnitSDK_AUBase_h
-
- // module
- #include <AudioUnitSDK/AUBuffer.h>
- #include <AudioUnitSDK/AUInputElement.h>
- #include <AudioUnitSDK/AUMIDIUtility.h>
- #include <AudioUnitSDK/AUOutputElement.h>
- #include <AudioUnitSDK/AUPlugInDispatch.h>
- #include <AudioUnitSDK/AUScopeElement.h>
- #include <AudioUnitSDK/AUUtility.h>
-
- // OS
- #include <TargetConditionals.h>
-
- // std
- #include <algorithm>
- #include <array>
- #include <memory>
- #include <mutex>
- #include <thread>
- #include <vector>
-
- // ________________________________________________________________________
-
- namespace ausdk {
-
- /*!
- @class AUBase
- @brief Abstract base class for an Audio Unit implementation.
- */
- class AUBase : public ComponentBase {
- public:
- constexpr static double kAUDefaultSampleRate = 44100.0;
- #if !TARGET_OS_WIN32
- constexpr static UInt32 kAUDefaultMaxFramesPerSlice = 1156;
- // this allows enough default frames for a 512 dest 44K and SRC from 96K
- // add a padding of 4 frames for any vector rounding
- #else
- constexpr static UInt32 kAUDefaultMaxFramesPerSlice = 2048;
- #endif
-
- AUBase(AudioComponentInstance inInstance, UInt32 numInputElements, UInt32 numOutputElements,
- UInt32 numGroupElements = 0);
- ~AUBase() override;
-
- AUBase(const AUBase&) = delete;
- AUBase(AUBase&&) = delete;
- AUBase& operator=(const AUBase&) = delete;
- AUBase& operator=(AUBase&&) = delete;
-
- /// Called immediately after construction, when virtual methods work. Or, a subclass may call
- /// this in order to have access to elements in its constructor.
- void CreateElements();
-
- virtual void CreateExtendedElements() {}
-
- #pragma mark -
- #pragma mark AU dispatch
- // ________________________________________________________________________
- // Virtual methods (mostly) directly corresponding to the entry points. Many of these
- // have useful implementations here and will not need overriding.
-
- /// Implements the entry point and ensures that Initialize is called exactly once from an
- /// uninitialized state.
- OSStatus DoInitialize();
-
- // Overrides to this method can assume that they will only be called exactly once
- // when transitioning from an uninitialized state.
- virtual OSStatus Initialize();
-
- [[nodiscard]] bool IsInitialized() const noexcept { return mInitialized; }
- [[nodiscard]] bool HasBegunInitializing() const noexcept { return mHasBegunInitializing; }
-
- /// Implements the entry point and ensures that Cleanup is called exactly once from an
- /// initialized state.
- void DoCleanup();
-
- // Overrides to this method can assume that they will only be called exactly once
- // when transitioning from an initialized state to an uninitialized state.
- virtual void Cleanup();
-
- virtual OSStatus Reset(AudioUnitScope inScope, AudioUnitElement inElement);
-
- // Note about GetPropertyInfo, GetProperty, SetProperty:
- // Certain properties are trapped out in these dispatch functions and handled with different
- // virtual methods. (To discourage hacks and keep vtable size down, these are non-virtual)
-
- OSStatus DispatchGetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable);
- OSStatus DispatchGetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, void* outData);
- OSStatus DispatchSetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
- OSStatus DispatchRemovePropertyValue(
- AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement);
-
- virtual OSStatus GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable);
- virtual OSStatus GetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, void* outData);
- virtual OSStatus SetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
- virtual OSStatus RemovePropertyValue(
- AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement);
-
- virtual OSStatus AddPropertyListener(
- AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcRefCon);
- virtual OSStatus RemovePropertyListener(AudioUnitPropertyID inID,
- AudioUnitPropertyListenerProc inProc, void* inProcRefCon, bool refConSpecified);
-
- virtual OSStatus SetRenderNotification(AURenderCallback inProc, void* inRefCon);
- virtual OSStatus RemoveRenderNotification(AURenderCallback inProc, void* inRefCon);
-
- virtual OSStatus GetParameter(AudioUnitParameterID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, AudioUnitParameterValue& outValue);
- virtual OSStatus SetParameter(AudioUnitParameterID inID, AudioUnitScope inScope,
- AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 inBufferOffsetInFrames);
-
- [[nodiscard]] virtual bool CanScheduleParameters() const = 0;
- virtual OSStatus ScheduleParameter(
- const AudioUnitParameterEvent* inParameterEvent, UInt32 inNumEvents);
-
- OSStatus DoRender(AudioUnitRenderActionFlags& ioActionFlags, const AudioTimeStamp& inTimeStamp,
- UInt32 inBusNumber, UInt32 inFramesToProcess, AudioBufferList& ioData);
- OSStatus DoProcess(AudioUnitRenderActionFlags& ioActionFlags, const AudioTimeStamp& inTimeStamp,
- UInt32 inFramesToProcess, AudioBufferList& ioData);
- OSStatus DoProcessMultiple(AudioUnitRenderActionFlags& ioActionFlags,
- const AudioTimeStamp& inTimeStamp, UInt32 inFramesToProcess,
- UInt32 inNumberInputBufferLists, const AudioBufferList** inInputBufferLists,
- UInt32 inNumberOutputBufferLists, AudioBufferList** ioOutputBufferLists);
-
- virtual OSStatus ProcessBufferLists(AudioUnitRenderActionFlags& /*ioActionFlags*/,
- const AudioBufferList& /*inBuffer*/, AudioBufferList& /*outBuffer*/,
- UInt32 /*inFramesToProcess*/)
- {
- return kAudio_UnimplementedError;
- }
-
- virtual OSStatus ProcessMultipleBufferLists(AudioUnitRenderActionFlags& /*ioActionFlags*/,
- UInt32 /*inFramesToProcess*/, UInt32 /*inNumberInputBufferLists*/,
- const AudioBufferList** /*inInputBufferLists*/, UInt32 /*inNumberOutputBufferLists*/,
- AudioBufferList** /*ioOutputBufferLists*/)
- {
- return kAudio_UnimplementedError;
- }
-
- virtual OSStatus ComplexRender(AudioUnitRenderActionFlags& /*ioActionFlags*/,
- const AudioTimeStamp& /*inTimeStamp*/, UInt32 /*inOutputBusNumber*/,
- UInt32 /*inNumberOfPackets*/, UInt32* /*outNumberOfPackets*/,
- AudioStreamPacketDescription* /*outPacketDescriptions*/, AudioBufferList& /*ioData*/,
- void* /*outMetadata*/, UInt32* /*outMetadataByteSize*/)
- {
- return kAudio_UnimplementedError;
- }
-
- // Override this method if your AU processes multiple output busses completely independently --
- // you'll want to just call Render without the NeedsToRender check.
- // Otherwise, override Render().
- //
- // N.B. Implementations of this method can assume that the output's buffer list has already been
- // prepared and access it with GetOutput(inBusNumber)->GetBufferList() instead of
- // GetOutput(inBusNumber)->PrepareBuffer(nFrames) -- if PrepareBuffer is called, a
- // copy may occur after rendering.
- virtual OSStatus RenderBus(AudioUnitRenderActionFlags& ioActionFlags,
- const AudioTimeStamp& inTimeStamp, UInt32 /*inBusNumber*/, UInt32 inNumberFrames)
- {
- if (NeedsToRender(inTimeStamp)) {
- return Render(ioActionFlags, inTimeStamp, inNumberFrames);
- }
- return noErr; // was presumably already rendered via another bus
- }
-
- // N.B. For a unit with only one output bus, it can assume in its implementation of this
- // method that the output's buffer list has already been prepared and access it with
- // GetOutput(0)->GetBufferList() instead of GetOutput(0)->PrepareBuffer(nFrames)
- // -- if PrepareBuffer is called, a copy may occur after rendering.
- virtual OSStatus Render(AudioUnitRenderActionFlags& /*ioActionFlags*/,
- const AudioTimeStamp& /*inTimeStamp*/, UInt32 /*inNumberFrames*/)
- {
- return noErr;
- }
-
-
- #pragma mark -
- #pragma mark Property Dispatch
-
- // ________________________________________________________________________
- // These are called from DispatchGetProperty/DispatchGetPropertyInfo/DispatchSetProperty
-
- virtual bool BusCountWritable(AudioUnitScope /*inScope*/) { return false; }
- virtual OSStatus SetBusCount(AudioUnitScope inScope, UInt32 inCount);
- virtual OSStatus SetConnection(const AudioUnitConnection& inConnection);
- virtual OSStatus SetInputCallback(
- UInt32 inPropertyID, AudioUnitElement inElement, AURenderCallback inProc, void* inRefCon);
-
- virtual OSStatus GetParameterList(
- AudioUnitScope inScope, AudioUnitParameterID* outParameterList, UInt32& outNumParameters);
- // outParameterList may be a null pointer
- virtual OSStatus GetParameterInfo(AudioUnitScope inScope, AudioUnitParameterID inParameterID,
- AudioUnitParameterInfo& outParameterInfo);
-
- virtual OSStatus GetParameterHistoryInfo(AudioUnitScope inScope,
- AudioUnitParameterID inParameterID, Float32& outUpdatesPerSecond,
- Float32& outHistoryDurationInSeconds);
- virtual OSStatus SaveState(CFPropertyListRef* outData);
- virtual void SaveExtendedScopes(CFMutableDataRef /*outData*/) {}
- virtual OSStatus RestoreState(CFPropertyListRef plist);
- virtual OSStatus GetParameterValueStrings(
- AudioUnitScope inScope, AudioUnitParameterID inParameterID, CFArrayRef* outStrings);
- virtual OSStatus CopyClumpName(AudioUnitScope inScope, UInt32 inClumpID,
- UInt32 inDesiredNameLength, CFStringRef* outClumpName);
- virtual OSStatus GetPresets(CFArrayRef* outData) const;
-
- /// Set the default preset for the unit. The number of the preset must be >= 0 and the name
- /// should be valid, or the preset will be rejected.
- bool SetAFactoryPresetAsCurrent(const AUPreset& inPreset);
-
- // Called when the host sets a new, valid preset.
- // If this is a valid preset, then the subclass sets its state to that preset
- // and returns noErr.
- // If not a valid preset, return an error, and the pre-existing preset is restored.
- virtual OSStatus NewFactoryPresetSet(const AUPreset& inNewFactoryPreset);
- virtual OSStatus NewCustomPresetSet(const AUPreset& inNewCustomPreset);
- virtual CFURLRef CopyIconLocation();
-
- // default is no latency, and unimplemented tail time
- virtual Float64 GetLatency() { return 0.0; }
- virtual Float64 GetTailTime() { return 0.0; }
- virtual bool SupportsTail() { return false; }
-
- // Stream formats: scope will always be input or output
- bool IsStreamFormatWritable(AudioUnitScope scope, AudioUnitElement element);
-
- virtual bool StreamFormatWritable(AudioUnitScope scope, AudioUnitElement element) = 0;
-
- // pass in a pointer to get the struct, and num channel infos
- // you can pass in NULL to just get the number
- // a return value of 0 (the default in AUBase) means the property is not supported...
- virtual UInt32 SupportedNumChannels(const AUChannelInfo** outInfo);
-
- /// Will only be called after StreamFormatWritable has succeeded. Default implementation
- /// requires non-interleaved native-endian 32-bit float, any sample rate, any number of
- /// channels; override when other formats are supported. A subclass's override can choose to
- /// always return true and trap invalid formats in ChangeStreamFormat.
- virtual bool ValidFormat(AudioUnitScope inScope, AudioUnitElement inElement,
- const AudioStreamBasicDescription& inNewFormat);
-
- virtual AudioStreamBasicDescription GetStreamFormat(
- AudioUnitScope inScope, AudioUnitElement inElement);
-
- // Will only be called after StreamFormatWritable
- // and ValidFormat have succeeded.
- virtual OSStatus ChangeStreamFormat(AudioUnitScope inScope, AudioUnitElement inElement,
- const AudioStreamBasicDescription& inPrevFormat,
- const AudioStreamBasicDescription& inNewFormat);
-
- // ________________________________________________________________________
- // Methods useful for subclasses
- AUScope& GetScope(AudioUnitScope inScope)
- {
- if (inScope >= kNumScopes) {
- AUScope* const scope = GetScopeExtended(inScope);
-
- ThrowQuietIf(scope == nullptr, kAudioUnitErr_InvalidScope);
- return *scope;
- }
- return mScopes[inScope]; // NOLINT
- }
-
- virtual AUScope* GetScopeExtended(AudioUnitScope /*inScope*/) { return nullptr; }
-
- AUScope& GlobalScope() { return mScopes[kAudioUnitScope_Global]; }
- AUScope& Inputs() { return mScopes[kAudioUnitScope_Input]; }
- AUScope& Outputs() { return mScopes[kAudioUnitScope_Output]; }
- AUScope& Groups() { return mScopes[kAudioUnitScope_Group]; }
- AUElement* Globals() { return mScopes[kAudioUnitScope_Global].GetElement(0); }
-
- void SetNumberOfElements(AudioUnitScope inScope, UInt32 numElements);
- virtual std::unique_ptr<AUElement> CreateElement(
- AudioUnitScope scope, AudioUnitElement element);
-
- AUElement* GetElement(AudioUnitScope inScope, AudioUnitElement inElement)
- {
- return GetScope(inScope).GetElement(inElement);
- }
-
- AUSDK_DEPRECATED("Use IOElement()")
- AUIOElement* GetIOElement(AudioUnitScope inScope, AudioUnitElement inElement)
- {
- return &IOElement(inScope, inElement);
- }
-
- AUIOElement& IOElement(AudioUnitScope inScope, AudioUnitElement inElement)
- {
- return *GetScope(inScope).GetIOElement(inElement);
- }
-
- AUSDK_DEPRECATED("Use Element()")
- AUElement* SafeGetElement(AudioUnitScope inScope, AudioUnitElement inElement)
- {
- return &Element(inScope, inElement);
- }
-
- AUElement& Element(AudioUnitScope inScope, AudioUnitElement inElement)
- {
- return *GetScope(inScope).SafeGetElement(inElement);
- }
-
- AUSDK_DEPRECATED("Use Input()")
- AUInputElement* GetInput(AudioUnitElement inElement) { return &Input(inElement); }
- AUInputElement& Input(AudioUnitElement inElement)
- {
- return static_cast<AUInputElement&>(*Inputs().SafeGetElement(inElement)); // NOLINT downcast
- }
-
- AUSDK_DEPRECATED("Use Output()")
- AUOutputElement* GetOutput(AudioUnitElement inElement) { return &Output(inElement); }
- AUOutputElement& Output(AudioUnitElement inElement)
- {
- return static_cast<AUOutputElement&>( // NOLINT downcast
- *Outputs().SafeGetElement(inElement));
- }
-
- AUSDK_DEPRECATED("Use Group()")
- AUElement* GetGroup(AudioUnitElement inElement) { return &Group(inElement); }
- AUElement& Group(AudioUnitElement inElement) { return *Groups().SafeGetElement(inElement); }
-
- OSStatus PullInput(UInt32 inBusNumber, AudioUnitRenderActionFlags& ioActionFlags,
- const AudioTimeStamp& inTimeStamp, UInt32 inNumberFrames)
- {
- AUInputElement& input = Input(inBusNumber); // throws if error
- return input.PullInput(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames);
- }
-
- [[nodiscard]] UInt32 GetMaxFramesPerSlice() const noexcept { return mMaxFramesPerSlice; }
-
- [[nodiscard]] bool UsesFixedBlockSize() const noexcept { return mUsesFixedBlockSize; }
-
- void SetUsesFixedBlockSize(bool inUsesFixedBlockSize) noexcept
- {
- mUsesFixedBlockSize = inUsesFixedBlockSize;
- }
-
- [[nodiscard]] virtual bool InRenderThread() const
- {
- return std::this_thread::get_id() == mRenderThreadID;
- }
-
- /// Says whether an input is connected or has a callback.
- bool HasInput(AudioUnitElement inElement)
- {
- auto* const in =
- static_cast<AUInputElement*>(Inputs().GetElement(inElement)); // NOLINT downcast
- return in != nullptr && in->IsActive();
- }
-
- virtual void PropertyChanged(
- AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement);
-
- // These calls can be used to call a Host's Callbacks. The method returns -1 if the host
- // hasn't supplied the callback. Any other result is returned by the host.
- // As in the API contract, for a parameter's value, you specify a pointer
- // to that data type. Specify NULL for a parameter that you are not interested
- // as this can save work in the host.
- OSStatus CallHostBeatAndTempo(Float64* outCurrentBeat, Float64* outCurrentTempo) const
- {
- return (mHostCallbackInfo.beatAndTempoProc != nullptr
- ? (*mHostCallbackInfo.beatAndTempoProc)(
- mHostCallbackInfo.hostUserData, outCurrentBeat, outCurrentTempo)
- : -1);
- }
-
- OSStatus CallHostMusicalTimeLocation(UInt32* outDeltaSampleOffsetToNextBeat,
- Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator,
- Float64* outCurrentMeasureDownBeat) const
- {
- return (mHostCallbackInfo.musicalTimeLocationProc != nullptr
- ? (*mHostCallbackInfo.musicalTimeLocationProc)(mHostCallbackInfo.hostUserData,
- outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator,
- outTimeSig_Denominator, outCurrentMeasureDownBeat)
- : -1);
- }
-
- OSStatus CallHostTransportState(Boolean* outIsPlaying, Boolean* outTransportStateChanged,
- Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, Float64* outCycleStartBeat,
- Float64* outCycleEndBeat) const
- {
- return (mHostCallbackInfo.transportStateProc != nullptr
- ? (*mHostCallbackInfo.transportStateProc)(mHostCallbackInfo.hostUserData,
- outIsPlaying, outTransportStateChanged, outCurrentSampleInTimeLine,
- outIsCycling, outCycleStartBeat, outCycleEndBeat)
- : -1);
- }
-
- [[nodiscard]] const char* GetLoggingString() const noexcept;
-
- AUMutex* GetMutex() noexcept { return mAUMutex; }
- // The caller of SetMutex is responsible for the managing the lifetime of the
- // mutex object and, if deleted before the AUBase instance, is responsible
- // for calling SetMutex(nullptr)
- void SetMutex(AUMutex* mutex) noexcept { mAUMutex = mutex; }
-
- #pragma mark -
- #pragma mark AU Output Base Dispatch
- // ________________________________________________________________________
- // ________________________________________________________________________
- // ________________________________________________________________________
- // output unit methods
- virtual OSStatus Start() { return kAudio_UnimplementedError; }
-
- virtual OSStatus Stop() { return kAudio_UnimplementedError; }
-
- #pragma mark -
- #pragma mark AU Music Base Dispatch
- // ________________________________________________________________________
- // ________________________________________________________________________
- // ________________________________________________________________________
- // music device/music effect methods
-
- virtual OSStatus MIDIEvent(
- UInt32 /*inStatus*/, UInt32 /*inData1*/, UInt32 /*inData2*/, UInt32 /*inOffsetSampleFrame*/)
- {
- return kAudio_UnimplementedError;
- }
-
- virtual OSStatus SysEx(const UInt8* /*inData*/, UInt32 /*inLength*/)
- {
- return kAudio_UnimplementedError;
- }
-
- #if AUSDK_MIDI2_AVAILABLE
- virtual OSStatus MIDIEventList(
- UInt32 /*inOffsetSampleFrame*/, const MIDIEventList* /*eventList*/)
- {
- return kAudio_UnimplementedError;
- }
- #endif
-
- virtual OSStatus StartNote(MusicDeviceInstrumentID /*inInstrument*/,
- MusicDeviceGroupID /*inGroupID*/, NoteInstanceID* /*outNoteInstanceID*/,
- UInt32 /*inOffsetSampleFrame*/, const MusicDeviceNoteParams& /*inParams*/)
- {
- return kAudio_UnimplementedError;
- }
-
- virtual OSStatus StopNote(MusicDeviceGroupID /*inGroupID*/, NoteInstanceID /*inNoteInstanceID*/,
- UInt32 /*inOffsetSampleFrame*/)
- {
- return kAudio_UnimplementedError;
- }
-
- /// Obsolete
- static OSStatus PrepareInstrument(MusicDeviceInstrumentID /*inInstrument*/)
- {
- return kAudio_UnimplementedError;
- }
-
- /// Obsolete
- static OSStatus ReleaseInstrument(MusicDeviceInstrumentID /*inInstrument*/)
- {
- return kAudio_UnimplementedError;
- }
-
- // ________________________________________________________________________
- // ________________________________________________________________________
- // ________________________________________________________________________
-
- protected:
- #pragma mark -
- #pragma mark Implementation methods
- void PostConstructorInternal() final;
- void PreDestructorInternal() final;
-
- /// needs to be called when mMaxFramesPerSlice changes
- virtual void ReallocateBuffers();
-
- virtual void DeallocateIOBuffers();
-
- static void FillInParameterName(
- AudioUnitParameterInfo& ioInfo, CFStringRef inName, bool inShouldRelease)
- {
- ioInfo.cfNameString = inName;
- ioInfo.flags |= kAudioUnitParameterFlag_HasCFNameString;
- if (inShouldRelease) {
- ioInfo.flags |= kAudioUnitParameterFlag_CFNameRelease;
- }
- CFStringGetCString(inName, &ioInfo.name[0], offsetof(AudioUnitParameterInfo, clumpID),
- kCFStringEncodingUTF8);
- }
-
- static void HasClump(AudioUnitParameterInfo& ioInfo, UInt32 inClumpID) noexcept
- {
- ioInfo.clumpID = inClumpID;
- ioInfo.flags |= kAudioUnitParameterFlag_HasClump;
- }
-
- virtual void SetMaxFramesPerSlice(UInt32 nFrames);
-
- [[nodiscard]] virtual OSStatus CanSetMaxFrames() const;
-
- [[nodiscard]] bool WantsRenderThreadID() const noexcept { return mWantsRenderThreadID; }
-
- void SetWantsRenderThreadID(bool inFlag);
-
- OSStatus SetRenderError(OSStatus inErr)
- {
- if (inErr != noErr && mLastRenderError == 0) {
- mLastRenderError = inErr;
- PropertyChanged(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0);
- }
- return inErr;
- }
-
- struct PropertyListener {
- AudioUnitPropertyID propertyID{ 0 };
- AudioUnitPropertyListenerProc listenerProc{ nullptr };
- void* listenerRefCon{ nullptr };
- };
- using PropertyListeners = std::vector<PropertyListener>;
-
- [[nodiscard]] const PropertyListeners& GetPropertyListeners() const noexcept
- {
- return mPropertyListeners;
- }
-
- HostCallbackInfo& GetHostCallbackInfo() noexcept { return mHostCallbackInfo; }
-
- private:
- // shared between Render and RenderSlice, inlined to minimize function call overhead
- OSStatus DoRenderBus(AudioUnitRenderActionFlags& ioActionFlags,
- const AudioTimeStamp& inTimeStamp, UInt32 inBusNumber, AUOutputElement& theOutput,
- UInt32 inNumberFrames, AudioBufferList& ioData)
- {
- if (ioData.mBuffers[0].mData == nullptr ||
- (theOutput.WillAllocateBuffer() && Outputs().GetNumberOfElements() > 1)) {
- // will render into cache buffer
- theOutput.PrepareBuffer(inNumberFrames);
- } else {
- // will render into caller's buffer
- theOutput.SetBufferList(ioData);
- }
- const OSStatus result = RenderBus(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames);
- if (result == noErr) {
- if (ioData.mBuffers[0].mData == nullptr) {
- theOutput.CopyBufferListTo(ioData);
- } else {
- theOutput.CopyBufferContentsTo(ioData);
- theOutput.InvalidateBufferList();
- }
- }
- return result;
- }
-
- bool HasIcon();
-
- [[nodiscard]] std::string CreateLoggingString() const;
-
- protected:
- //. Returns size. outLayoutPtr may be null if querying only for size.
- virtual UInt32 GetAudioChannelLayout(AudioUnitScope scope, AudioUnitElement element,
- AudioChannelLayout* outLayoutPtr, bool& outWritable);
-
- /// Layout is non-null.
- virtual OSStatus SetAudioChannelLayout(
- AudioUnitScope scope, AudioUnitElement element, const AudioChannelLayout* inLayout);
-
- virtual OSStatus RemoveAudioChannelLayout(AudioUnitScope scope, AudioUnitElement element);
-
- virtual std::vector<AudioChannelLayoutTag> GetChannelLayoutTags(
- AudioUnitScope scope, AudioUnitElement element);
-
- bool NeedsToRender(const AudioTimeStamp& inTimeStamp)
- {
- const bool needsToRender = (inTimeStamp.mSampleTime != mCurrentRenderTime.mSampleTime);
- if (needsToRender) { // only copy this if we need to render
- mCurrentRenderTime = inTimeStamp;
- }
- return needsToRender;
- }
-
- // Scheduled parameter implementation:
-
- using ParameterEventList = std::vector<AudioUnitParameterEvent>;
-
- // Usually, you won't override this method. You only need to call this if your DSP code
- // is prepared to handle scheduled immediate and ramped parameter changes.
- // Before calling this method, it is assumed you have already called PullInput() on the input
- // busses for which the DSP code depends. ProcessForScheduledParams() will call (potentially
- // repeatedly) virtual method ProcessScheduledSlice() to perform the actual DSP for a given
- // sub-division of the buffer. The job of ProcessForScheduledParams() is to sub-divide the
- // buffer into smaller pieces according to the scheduled times found in the ParameterEventList
- // (usually coming directly from a previous call to ScheduleParameter() ), setting the
- // appropriate immediate or ramped parameter values for the corresponding scopes and elements,
- // then calling ProcessScheduledSlice() to do the actual DSP for each of these divisions.
- virtual OSStatus ProcessForScheduledParams(
- ParameterEventList& inParamList, UInt32 inFramesToProcess, void* inUserData);
-
- // This method is called (potentially repeatedly) by ProcessForScheduledParams()
- // in order to perform the actual DSP required for this portion of the entire buffer
- // being processed. The entire buffer can be divided up into smaller "slices"
- // according to the timestamps on the scheduled parameters...
- //
- // sub-classes wishing to handle scheduled parameter changes should override this method
- // in order to do the appropriate DSP. AUEffectBase already overrides this for standard
- // effect AudioUnits.
- virtual OSStatus ProcessScheduledSlice(void* /*inUserData*/, UInt32 /*inStartFrameInBuffer*/,
- UInt32 /*inSliceFramesToProcess*/, UInt32 /*inTotalBufferFrames*/)
- {
- // default implementation does nothing.
- return noErr;
- }
-
- [[nodiscard]] const AudioTimeStamp& CurrentRenderTime() const noexcept
- {
- return mCurrentRenderTime;
- }
- void ResetRenderTime();
-
- // ________________________________________________________________________
- // Private data members to discourage hacking in subclasses
- private:
- struct RenderCallback {
- RenderCallback() = default;
-
- RenderCallback(AURenderCallback proc, void* ref)
- : mRenderNotify(proc), mRenderNotifyRefCon(ref)
- {
- }
-
- AURenderCallback mRenderNotify = nullptr;
- void* mRenderNotifyRefCon = nullptr;
-
- bool operator==(const RenderCallback& other) const
- {
- return this->mRenderNotify == other.mRenderNotify &&
- this->mRenderNotifyRefCon == other.mRenderNotifyRefCon;
- }
- };
-
- class RenderCallbackList {
- public:
- void add(const RenderCallback& rc)
- {
- const std::lock_guard guard{ mLock };
- const auto iter = std::find(mImpl.begin(), mImpl.end(), rc);
- if (iter != mImpl.end()) {
- return;
- }
- mImpl.emplace_back(rc);
- }
-
- void remove(const RenderCallback& rc)
- {
- const std::lock_guard guard{ mLock };
- const auto iter = std::find(mImpl.begin(), mImpl.end(), rc);
- if (iter != mImpl.end()) {
- mImpl.erase(iter);
- }
- }
-
- template <typename F>
- void foreach (F&& func)
- {
- const std::lock_guard guard{ mLock };
- for (const auto& cb : mImpl) {
- func(cb);
- }
- }
-
- private:
- AUMutex mLock;
- std::vector<RenderCallback> mImpl;
- };
-
- protected:
- static constexpr AudioUnitScope kNumScopes = 4;
-
- ParameterEventList& GetParamEventList() noexcept { return mParamEventList; }
- void SetBuffersAllocated(bool b) noexcept { mBuffersAllocated = b; }
-
- [[nodiscard]] CFStringRef GetContextName() const { return *mContextName; }
- void SetContextName(CFStringRef str) { mContextName = str; }
-
- [[nodiscard]] CFStringRef GetNickName() const { return *mNickName; }
-
- private:
- bool mElementsCreated{ false };
- bool mInitialized{ false };
- bool mHasBegunInitializing{ false };
- const UInt32 mInitNumInputEls;
- const UInt32 mInitNumOutputEls;
- const UInt32 mInitNumGroupEls;
- std::array<AUScope, kNumScopes> mScopes;
- RenderCallbackList mRenderCallbacks;
- bool mRenderCallbacksTouched{ false };
- std::thread::id mRenderThreadID{};
- bool mWantsRenderThreadID{ false };
- AudioTimeStamp mCurrentRenderTime{};
- UInt32 mMaxFramesPerSlice{ 0 };
- OSStatus mLastRenderError{ noErr };
- #ifndef AUSDK_NO_LOGGING
- const double mHostTimeFrequency{
- HostTime::Frequency()
- }; // cache because there is calculation cost
- #endif
- AUPreset mCurrentPreset{ -1, nullptr };
- bool mUsesFixedBlockSize{ false };
-
- ParameterEventList mParamEventList;
- PropertyListeners mPropertyListeners;
- bool mBuffersAllocated{ false };
- const std::string mLogString;
- Owned<CFStringRef> mNickName;
-
- /*! @var mAUMutex
- If non-null, guards all non-realtime entry points into the AudioUnit. Most AudioUnits
- do not need to use this. It's useful for the case of an AU which must synchronize
- an external source of callbacks against entry from the host.
- */
- AUMutex* mAUMutex{ nullptr };
- HostCallbackInfo mHostCallbackInfo{};
- Owned<CFStringRef> mContextName;
- };
-
- } // namespace ausdk
-
- #endif // AudioUnitSDK_AUBase_h
|