build(deps): vendor JUCE 7.0.12

This commit is contained in:
2026-09-08 14:55:21 +02:00
parent 5b99f51bd1
commit d6705ab29f
3591 changed files with 1218267 additions and 0 deletions
@@ -0,0 +1,46 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#ifndef DOXYGEN
struct ModifierKeyProvider
{
virtual ~ModifierKeyProvider() {}
virtual int getWin32Modifiers() const = 0;
};
struct ModifierKeyReceiver
{
virtual ~ModifierKeyReceiver() {}
virtual void setModifierKeyProvider (ModifierKeyProvider*) = 0;
virtual void removeModifierKeyProvider() = 0;
};
#endif
} // namespace juce
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,733 @@
/*!
@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, std::data(ioInfo.name), std::size(ioInfo.name),
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
@@ -0,0 +1,188 @@
/*!
@file AudioUnitSDK/AUBuffer.cpp
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#include <AudioUnitSDK/AUBuffer.h>
#include <AudioUnitSDK/AUUtility.h>
namespace ausdk {
inline void ThrowBadAlloc()
{
AUSDK_LogError("AUBuffer throwing bad_alloc");
throw std::bad_alloc();
}
// x: number to be rounded; y: the power of 2 to which to round
constexpr uint32_t RoundUpToMultipleOfPowerOf2(uint32_t x, uint32_t y) noexcept
{
const auto mask = y - 1;
#if DEBUG
assert((mask & y) == 0u); // verifies that y is a power of 2 NOLINT
#endif
return (x + mask) & ~mask;
}
// a * b + c
static UInt32 SafeMultiplyAddUInt32(UInt32 a, UInt32 b, UInt32 c)
{
if (a == 0 || b == 0) {
return c; // prevent zero divide
}
if (a > (0xFFFFFFFF - c) / b) { // NOLINT magic
ThrowBadAlloc();
}
return a * b + c;
}
AllocatedBuffer* BufferAllocator::Allocate(
UInt32 numberBuffers, UInt32 maxBytesPerBuffer, UInt32 /*reservedFlags*/)
{
constexpr size_t kAlignment = 16;
constexpr size_t kMaxBufferListSize = 65536;
// Check for a reasonable number of buffers (obviate a more complicated check with offsetof).
if (numberBuffers > kMaxBufferListSize / sizeof(AudioBuffer)) {
throw std::out_of_range("AudioBuffers::Allocate: Too many buffers");
}
maxBytesPerBuffer = RoundUpToMultipleOfPowerOf2(maxBytesPerBuffer, kAlignment);
const auto bufferDataSize = SafeMultiplyAddUInt32(numberBuffers, maxBytesPerBuffer, 0);
void* bufferData = nullptr;
if (bufferDataSize > 0) {
bufferData = malloc(bufferDataSize);
// don't use calloc(); it might not actually touch the memory and cause a VM fault later
memset(bufferData, 0, bufferDataSize);
}
const auto implSize = static_cast<uint32_t>(
offsetof(AllocatedBuffer, mAudioBufferList.mBuffers[std::max(UInt32(1), numberBuffers)]));
auto* const implMem = malloc(implSize);
auto* const allocatedBuffer =
new (implMem) AllocatedBuffer{ .mMaximumNumberBuffers = numberBuffers,
.mMaximumBytesPerBuffer = maxBytesPerBuffer,
.mHeaderSize = implSize,
.mBufferDataSize = bufferDataSize,
.mBufferData = bufferData };
allocatedBuffer->mAudioBufferList.mNumberBuffers = numberBuffers;
return allocatedBuffer;
}
void BufferAllocator::Deallocate(AllocatedBuffer* allocatedBuffer)
{
if (allocatedBuffer->mBufferData != nullptr) {
free(allocatedBuffer->mBufferData);
}
allocatedBuffer->~AllocatedBuffer();
free(allocatedBuffer);
}
AudioBufferList& AllocatedBuffer::Prepare(UInt32 channelsPerBuffer, UInt32 bytesPerBuffer)
{
if (mAudioBufferList.mNumberBuffers > mMaximumNumberBuffers) {
throw std::out_of_range("AllocatedBuffer::Prepare(): too many buffers");
}
if (bytesPerBuffer > mMaximumBytesPerBuffer) {
throw std::out_of_range("AllocatedBuffer::Prepare(): insufficient capacity");
}
auto* ptr = static_cast<Byte*>(mBufferData);
auto* const ptrend = ptr + mBufferDataSize;
for (UInt32 bufIdx = 0, nBufs = mAudioBufferList.mNumberBuffers; bufIdx < nBufs; ++bufIdx) {
auto& buf = mAudioBufferList.mBuffers[bufIdx]; // NOLINT
buf.mNumberChannels = channelsPerBuffer;
buf.mDataByteSize = bytesPerBuffer;
buf.mData = ptr;
ptr += mMaximumBytesPerBuffer; // NOLINT ptr math
}
if (ptr > ptrend) {
throw std::out_of_range("AllocatedBuffer::Prepare(): insufficient capacity");
}
return mAudioBufferList;
}
AudioBufferList& AllocatedBuffer::PrepareNull(UInt32 channelsPerBuffer, UInt32 bytesPerBuffer)
{
if (mAudioBufferList.mNumberBuffers > mMaximumNumberBuffers) {
throw std::out_of_range("AllocatedBuffer::PrepareNull(): too many buffers");
}
for (UInt32 bufIdx = 0, nBufs = mAudioBufferList.mNumberBuffers; bufIdx < nBufs; ++bufIdx) {
auto& buf = mAudioBufferList.mBuffers[bufIdx]; // NOLINT
buf.mNumberChannels = channelsPerBuffer;
buf.mDataByteSize = bytesPerBuffer;
buf.mData = nullptr;
}
return mAudioBufferList;
}
AudioBufferList& AUBufferList::PrepareBuffer(
const AudioStreamBasicDescription& format, UInt32 nFrames)
{
ausdk::ThrowExceptionIf(nFrames > mAllocatedFrames, kAudioUnitErr_TooManyFramesToProcess);
UInt32 nStreams = 0;
UInt32 channelsPerStream = 0;
if (ASBD::IsInterleaved(format)) {
nStreams = 1;
channelsPerStream = format.mChannelsPerFrame;
} else {
nStreams = format.mChannelsPerFrame;
channelsPerStream = 1;
}
ausdk::ThrowExceptionIf(nStreams > mAllocatedStreams, kAudioUnitErr_FormatNotSupported);
auto& abl = mBuffers->Prepare(channelsPerStream, nFrames * format.mBytesPerFrame);
mPtrState = EPtrState::ToMyMemory;
return abl;
}
AudioBufferList& AUBufferList::PrepareNullBuffer(
const AudioStreamBasicDescription& format, UInt32 nFrames)
{
UInt32 nStreams = 0;
UInt32 channelsPerStream = 0;
if (ASBD::IsInterleaved(format)) {
nStreams = 1;
channelsPerStream = format.mChannelsPerFrame;
} else {
nStreams = format.mChannelsPerFrame;
channelsPerStream = 1;
}
ausdk::ThrowExceptionIf(nStreams > mAllocatedStreams, kAudioUnitErr_FormatNotSupported);
auto& abl = mBuffers->PrepareNull(channelsPerStream, nFrames * format.mBytesPerFrame);
mPtrState = EPtrState::ToExternalMemory;
return abl;
}
void AUBufferList::Allocate(const AudioStreamBasicDescription& format, UInt32 nFrames)
{
auto& alloc = BufferAllocator::instance();
if (mBuffers != nullptr) {
alloc.Deallocate(mBuffers);
}
const uint32_t nstreams = ASBD::IsInterleaved(format) ? 1 : format.mChannelsPerFrame;
mBuffers = alloc.Allocate(nstreams, nFrames * format.mBytesPerFrame, 0u);
mAllocatedFrames = nFrames;
mAllocatedStreams = nstreams;
mPtrState = EPtrState::Invalid;
}
void AUBufferList::Deallocate()
{
if (mBuffers != nullptr) {
BufferAllocator::instance().Deallocate(mBuffers);
mBuffers = nullptr;
}
mAllocatedFrames = 0;
mAllocatedStreams = 0;
mPtrState = EPtrState::Invalid;
}
} // namespace ausdk
@@ -0,0 +1,166 @@
/*!
@file AudioUnitSDK/AUBuffer.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_AUBuffer_h
#define AudioUnitSDK_AUBuffer_h
#include <AudioUnitSDK/AUUtility.h>
#include <AudioToolbox/AudioUnit.h>
#include <cstddef>
#include <optional>
namespace ausdk {
/// struct created/destroyed by allocator. Do not attempt to manually create/destroy.
struct AllocatedBuffer {
const UInt32 mMaximumNumberBuffers;
const UInt32 mMaximumBytesPerBuffer;
const UInt32 mReservedA[2]; // NOLINT C-style array
const UInt32 mHeaderSize;
const UInt32 mBufferDataSize;
const UInt32 mReservedB[2]; // NOLINT C-style array
void* const mBufferData;
void* const mReservedC;
AudioBufferList mAudioBufferList;
// opaque variable-length data may follow the AudioBufferList
AudioBufferList& Prepare(UInt32 channelsPerBuffer, UInt32 bytesPerBuffer);
AudioBufferList& PrepareNull(UInt32 channelsPerBuffer, UInt32 bytesPerBuffer);
};
/*!
@class BufferAllocator
@brief Class which allocates memory for internal audio buffers.
To customize, create a subclass and install an instance into the global via set_instance().
*/
class BufferAllocator {
public:
/// Obtain the global instance, creating it if necessary.
static BufferAllocator& instance();
/// A client may install a custom global instance via this method. Throws an exception if
/// a default instance has already been created.
static void set_instance(BufferAllocator& instance);
BufferAllocator() = default;
virtual ~BufferAllocator() = default;
// Rule of 5
BufferAllocator(const BufferAllocator&) = delete;
BufferAllocator(BufferAllocator&&) = delete;
BufferAllocator& operator=(const BufferAllocator&) = delete;
BufferAllocator& operator=(BufferAllocator&&) = delete;
// N.B. Must return zeroed memory aligned to at least 16 bytes.
virtual AllocatedBuffer* Allocate(
UInt32 numberBuffers, UInt32 maxBytesPerBuffer, UInt32 reservedFlags);
virtual void Deallocate(AllocatedBuffer* allocatedBuffer);
};
/*!
@class AUBufferList
@brief Manages an `AudioBufferList` backed by allocated memory buffers.
*/
class AUBufferList {
enum class EPtrState { Invalid, ToMyMemory, ToExternalMemory };
public:
AUBufferList() = default;
~AUBufferList() { Deallocate(); }
AUBufferList(const AUBufferList&) = delete;
AUBufferList(AUBufferList&&) = delete;
AUBufferList& operator=(const AUBufferList&) = delete;
AUBufferList& operator=(AUBufferList&&) = delete;
AudioBufferList& PrepareBuffer(const AudioStreamBasicDescription& format, UInt32 nFrames);
AudioBufferList& PrepareNullBuffer(const AudioStreamBasicDescription& format, UInt32 nFrames);
AudioBufferList& SetBufferList(const AudioBufferList& abl)
{
ausdk::ThrowExceptionIf(mAllocatedStreams < abl.mNumberBuffers, -1);
mPtrState = EPtrState::ToExternalMemory;
auto& myabl = mBuffers->mAudioBufferList;
memcpy(&myabl, &abl,
static_cast<size_t>(
reinterpret_cast<const std::byte*>(&abl.mBuffers[abl.mNumberBuffers]) - // NOLINT
reinterpret_cast<const std::byte*>(&abl))); // NOLINT
return myabl;
}
void SetBuffer(UInt32 index, const AudioBuffer& ab)
{
auto& myabl = mBuffers->mAudioBufferList;
ausdk::ThrowExceptionIf(
mPtrState == EPtrState::Invalid || index >= myabl.mNumberBuffers, -1);
mPtrState = EPtrState::ToExternalMemory;
myabl.mBuffers[index] = ab; // NOLINT
}
void InvalidateBufferList() noexcept { mPtrState = EPtrState::Invalid; }
[[nodiscard]] AudioBufferList& GetBufferList() const
{
ausdk::ThrowExceptionIf(mPtrState == EPtrState::Invalid, -1);
return mBuffers->mAudioBufferList;
}
void CopyBufferListTo(AudioBufferList& abl) const
{
ausdk::ThrowExceptionIf(mPtrState == EPtrState::Invalid, -1);
memcpy(&abl, &mBuffers->mAudioBufferList,
static_cast<size_t>(
reinterpret_cast<std::byte*>(&abl.mBuffers[abl.mNumberBuffers]) - // NOLINT
reinterpret_cast<std::byte*>(&abl))); // NOLINT
}
void CopyBufferContentsTo(AudioBufferList& destabl) const
{
ausdk::ThrowExceptionIf(mPtrState == EPtrState::Invalid, -1);
const auto& srcabl = mBuffers->mAudioBufferList;
const AudioBuffer* srcbuf = srcabl.mBuffers; // NOLINT
AudioBuffer* destbuf = destabl.mBuffers; // NOLINT
for (UInt32 i = 0; i < destabl.mNumberBuffers; ++i, ++srcbuf, ++destbuf) { // NOLINT
if (i >=
srcabl.mNumberBuffers) { // duplicate last source to additional outputs [4341137]
--srcbuf; // NOLINT
}
if (destbuf->mData != srcbuf->mData) {
memmove(destbuf->mData, srcbuf->mData, srcbuf->mDataByteSize);
}
destbuf->mDataByteSize = srcbuf->mDataByteSize;
}
}
void Allocate(const AudioStreamBasicDescription& format, UInt32 nFrames);
void Deallocate();
// AudioBufferList utilities
static void ZeroBuffer(AudioBufferList& abl)
{
AudioBuffer* buf = abl.mBuffers; // NOLINT
for (UInt32 i = 0; i < abl.mNumberBuffers; ++i, ++buf) { // NOLINT
memset(buf->mData, 0, buf->mDataByteSize);
}
}
[[nodiscard]] UInt32 GetAllocatedFrames() const noexcept { return mAllocatedFrames; }
private:
EPtrState mPtrState{ EPtrState::Invalid };
AllocatedBuffer* mBuffers = nullptr; // only valid between Allocate and Deallocate
UInt32 mAllocatedStreams{ 0 };
UInt32 mAllocatedFrames{ 0 };
};
} // namespace ausdk
#endif // AudioUnitSDK_AUBuffer_h
@@ -0,0 +1,15 @@
/*!
@file AudioUnitSDK/AUBufferAllocator.cpp
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#include <AudioUnitSDK/AUBuffer.h>
namespace ausdk {
BufferAllocator& BufferAllocator::instance()
{
__attribute__ ((no_destroy)) static BufferAllocator global;
return global;
}
} // namespace ausdk
@@ -0,0 +1,400 @@
/*!
@file AudioUnitSDK/AUEffectBase.cpp
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#include <AudioUnitSDK/AUEffectBase.h>
#include <cstddef>
/*
This class does not deal as well as it should with N-M effects...
The problem areas are (if the channels don't match):
ProcessInPlace if the channels don't match - there will be problems if InputChan !=
OutputChan Bypass - its just passing the buffers through when not processing them
*/
namespace ausdk {
//_____________________________________________________________________________
//
AUEffectBase::AUEffectBase(AudioComponentInstance audioUnit, bool inProcessesInPlace)
: AUBase(audioUnit, 1, 1), // 1 in bus, 1 out bus
mProcessesInPlace(inProcessesInPlace)
#if TARGET_OS_IPHONE
,
mOnlyOneKernel(false)
#endif
{
}
//_____________________________________________________________________________
//
void AUEffectBase::Cleanup()
{
mKernelList.clear();
mMainOutput = nullptr;
mMainInput = nullptr;
}
//_____________________________________________________________________________
//
OSStatus AUEffectBase::Initialize()
{
// get our current numChannels for input and output
const auto auNumInputs = static_cast<SInt16>(Input(0).GetStreamFormat().mChannelsPerFrame);
const auto auNumOutputs = static_cast<SInt16>(Output(0).GetStreamFormat().mChannelsPerFrame);
// does the unit publish specific information about channel configurations?
const AUChannelInfo* auChannelConfigs = nullptr;
const UInt32 numIOconfigs = SupportedNumChannels(&auChannelConfigs);
if ((numIOconfigs > 0) && (auChannelConfigs != nullptr)) {
bool foundMatch = false;
for (UInt32 i = 0; (i < numIOconfigs) && !foundMatch; ++i) {
const SInt16 configNumInputs = auChannelConfigs[i].inChannels; // NOLINT
const SInt16 configNumOutputs = auChannelConfigs[i].outChannels; // NOLINT
if ((configNumInputs < 0) && (configNumOutputs < 0)) {
// unit accepts any number of channels on input and output
if (((configNumInputs == -1) && (configNumOutputs == -2)) ||
((configNumInputs == -2) &&
(configNumOutputs == -1))) { // NOLINT repeated branch below
foundMatch = true;
// unit accepts any number of channels on input and output IFF they are the same
// number on both scopes
} else if (((configNumInputs == -1) && (configNumOutputs == -1)) &&
(auNumInputs == auNumOutputs)) {
foundMatch = true;
// unit has specified a particular number of channels on both scopes
} else {
continue;
}
} else {
// the -1 case on either scope is saying that the unit doesn't care about the
// number of channels on that scope
const bool inputMatch = (auNumInputs == configNumInputs) || (configNumInputs == -1);
const bool outputMatch =
(auNumOutputs == configNumOutputs) || (configNumOutputs == -1);
if (inputMatch && outputMatch) {
foundMatch = true;
}
}
}
if (!foundMatch) {
return kAudioUnitErr_FormatNotSupported;
}
} else {
// there is no specifically published channel info
// so for those kinds of effects, the assumption is that the channels (whatever their
// number) should match on both scopes
if ((auNumOutputs != auNumInputs) || (auNumOutputs == 0)) {
return kAudioUnitErr_FormatNotSupported;
}
}
MaintainKernels();
mMainOutput = &Output(0);
mMainInput = &Input(0);
const AudioStreamBasicDescription format = GetStreamFormat(kAudioUnitScope_Output, 0);
mBytesPerFrame = format.mBytesPerFrame;
return noErr;
}
OSStatus AUEffectBase::Reset(AudioUnitScope inScope, AudioUnitElement inElement)
{
for (auto& kernel : mKernelList) {
if (kernel) {
kernel->Reset();
}
}
return AUBase::Reset(inScope, inElement);
}
OSStatus AUEffectBase::GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable)
{
if (inScope == kAudioUnitScope_Global) {
switch (inID) {
case kAudioUnitProperty_BypassEffect:
case kAudioUnitProperty_InPlaceProcessing:
outWritable = true;
outDataSize = sizeof(UInt32);
return noErr;
default:
break;
}
}
return AUBase::GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable);
}
OSStatus AUEffectBase::GetProperty(
AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData)
{
if (inScope == kAudioUnitScope_Global) {
switch (inID) {
case kAudioUnitProperty_BypassEffect:
*static_cast<UInt32*>(outData) = (IsBypassEffect() ? 1 : 0); // NOLINT
return noErr;
case kAudioUnitProperty_InPlaceProcessing:
*static_cast<UInt32*>(outData) = (mProcessesInPlace ? 1 : 0); // NOLINT
return noErr;
default:
break;
}
}
return AUBase::GetProperty(inID, inScope, inElement, outData);
}
OSStatus AUEffectBase::SetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, const void* inData, UInt32 inDataSize)
{
if (inScope == kAudioUnitScope_Global) {
switch (inID) {
case kAudioUnitProperty_BypassEffect: {
if (inDataSize < sizeof(UInt32)) {
return kAudioUnitErr_InvalidPropertyValue;
}
const bool tempNewSetting = *static_cast<const UInt32*>(inData) != 0;
// we're changing the state of bypass
if (tempNewSetting != IsBypassEffect()) {
if (!tempNewSetting && IsBypassEffect() &&
IsInitialized()) { // turning bypass off and we're initialized
Reset(kAudioUnitScope_Global, 0);
}
SetBypassEffect(tempNewSetting);
}
return noErr;
}
case kAudioUnitProperty_InPlaceProcessing:
mProcessesInPlace = *static_cast<const UInt32*>(inData) != 0;
return noErr;
default:
break;
}
}
return AUBase::SetProperty(inID, inScope, inElement, inData, inDataSize);
}
void AUEffectBase::MaintainKernels()
{
#if TARGET_OS_IPHONE
const UInt32 nKernels = mOnlyOneKernel ? 1 : GetNumberOfChannels();
#else
const UInt32 nKernels = GetNumberOfChannels();
#endif
if (mKernelList.size() < nKernels) {
mKernelList.reserve(nKernels);
for (auto i = static_cast<UInt32>(mKernelList.size()); i < nKernels; ++i) {
mKernelList.push_back(NewKernel());
}
} else {
while (mKernelList.size() > nKernels) {
mKernelList.pop_back();
}
}
for (UInt32 i = 0; i < nKernels; i++) {
if (mKernelList[i]) {
mKernelList[i]->SetChannelNum(i);
}
}
}
bool AUEffectBase::StreamFormatWritable(AudioUnitScope /*scope*/, AudioUnitElement /*element*/)
{
return !IsInitialized();
}
OSStatus AUEffectBase::ChangeStreamFormat(AudioUnitScope inScope, AudioUnitElement inElement,
const AudioStreamBasicDescription& inPrevFormat, const AudioStreamBasicDescription& inNewFormat)
{
const OSStatus result =
AUBase::ChangeStreamFormat(inScope, inElement, inPrevFormat, inNewFormat);
if (result == noErr) {
// for the moment this only dependency we know about
// where a parameter's range may change is with the sample rate
// and effects are only publishing parameters in the global scope!
if (GetParamHasSampleRateDependency() &&
inPrevFormat.mSampleRate != inNewFormat.mSampleRate) {
PropertyChanged(kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0);
}
}
return result;
}
// ____________________________________________________________________________
//
// 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...
//
OSStatus AUEffectBase::ProcessScheduledSlice(void* inUserData, UInt32 /*inStartFrameInBuffer*/,
UInt32 inSliceFramesToProcess, UInt32 /*inTotalBufferFrames*/)
{
const ScheduledProcessParams& sliceParams = *static_cast<ScheduledProcessParams*>(inUserData);
AudioUnitRenderActionFlags& actionFlags = *sliceParams.actionFlags;
AudioBufferList& inputBufferList = *sliceParams.inputBufferList;
AudioBufferList& outputBufferList = *sliceParams.outputBufferList;
UInt32 channelSize = inSliceFramesToProcess * mBytesPerFrame;
// fix the size of the buffer we're operating on before we render this slice of time
for (UInt32 i = 0; i < inputBufferList.mNumberBuffers; i++) {
inputBufferList.mBuffers[i].mDataByteSize = // NOLINT
inputBufferList.mBuffers[i].mNumberChannels * channelSize; // NOLINT
}
for (UInt32 i = 0; i < outputBufferList.mNumberBuffers; i++) {
outputBufferList.mBuffers[i].mDataByteSize = // NOLINT
outputBufferList.mBuffers[i].mNumberChannels * channelSize; // NOLINT
}
// process the buffer
const OSStatus result =
ProcessBufferLists(actionFlags, inputBufferList, outputBufferList, inSliceFramesToProcess);
// we just partially processed the buffers, so increment the data pointers to the next part of
// the buffer to process
for (UInt32 i = 0; i < inputBufferList.mNumberBuffers; i++) {
inputBufferList.mBuffers[i].mData = // NOLINT
static_cast<std::byte*>(inputBufferList.mBuffers[i].mData) + // NOLINT
inputBufferList.mBuffers[i].mNumberChannels * channelSize; // NOLINT
}
for (UInt32 i = 0; i < outputBufferList.mNumberBuffers; i++) {
outputBufferList.mBuffers[i].mData = // NOLINT
static_cast<std::byte*>(outputBufferList.mBuffers[i].mData) + // NOLINT
outputBufferList.mBuffers[i].mNumberChannels * channelSize; // NOLINT
}
return result;
}
// ____________________________________________________________________________
//
OSStatus AUEffectBase::Render(
AudioUnitRenderActionFlags& ioActionFlags, const AudioTimeStamp& inTimeStamp, UInt32 nFrames)
{
if (!HasInput(0)) {
return kAudioUnitErr_NoConnection;
}
OSStatus result = noErr;
result = mMainInput->PullInput(ioActionFlags, inTimeStamp, 0 /* element */, nFrames);
if (result == noErr) {
if (ProcessesInPlace() && mMainOutput->WillAllocateBuffer()) {
mMainOutput->SetBufferList(mMainInput->GetBufferList());
}
if (ShouldBypassEffect()) {
// leave silence bit alone
if (!ProcessesInPlace()) {
mMainInput->CopyBufferContentsTo(mMainOutput->GetBufferList());
}
} else {
auto& paramEventList = GetParamEventList();
if (paramEventList.empty()) {
// this will read/write silence bit
result = ProcessBufferLists(ioActionFlags, mMainInput->GetBufferList(),
mMainOutput->GetBufferList(), nFrames);
} else {
// deal with scheduled parameters...
AudioBufferList& inputBufferList = mMainInput->GetBufferList();
AudioBufferList& outputBufferList = mMainOutput->GetBufferList();
ScheduledProcessParams processParams{ .actionFlags = &ioActionFlags,
.inputBufferList = &inputBufferList,
.outputBufferList = &outputBufferList };
// divide up the buffer into slices according to scheduled params then
// do the DSP for each slice (ProcessScheduledSlice() called for each slice)
result = ProcessForScheduledParams(paramEventList, nFrames, &processParams);
// fixup the buffer pointers to how they were before we started
const UInt32 channelSize = nFrames * mBytesPerFrame;
for (UInt32 i = 0; i < inputBufferList.mNumberBuffers; i++) {
const UInt32 size =
inputBufferList.mBuffers[i].mNumberChannels * channelSize; // NOLINT
inputBufferList.mBuffers[i].mData = // NOLINT
static_cast<std::byte*>(inputBufferList.mBuffers[i].mData) - size; // NOLINT
inputBufferList.mBuffers[i].mDataByteSize = size; // NOLINT
}
for (UInt32 i = 0; i < outputBufferList.mNumberBuffers; i++) {
const UInt32 size =
outputBufferList.mBuffers[i].mNumberChannels * channelSize; // NOLINT
outputBufferList.mBuffers[i].mData = // NOLINT
static_cast<std::byte*>(outputBufferList.mBuffers[i].mData) -
size; // NOLINT
outputBufferList.mBuffers[i].mDataByteSize = size; // NOLINT
}
}
}
if (((ioActionFlags & kAudioUnitRenderAction_OutputIsSilence) != 0u) &&
!ProcessesInPlace()) {
AUBufferList::ZeroBuffer(mMainOutput->GetBufferList());
}
}
return result;
}
OSStatus AUEffectBase::ProcessBufferLists(AudioUnitRenderActionFlags& ioActionFlags,
const AudioBufferList& inBuffer, AudioBufferList& outBuffer, UInt32 inFramesToProcess)
{
if (ShouldBypassEffect()) {
return noErr;
}
bool ioSilence = false;
const bool silentInput = IsInputSilent(ioActionFlags, inFramesToProcess);
ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
for (UInt32 channel = 0; channel < mKernelList.size(); ++channel) {
auto& kernel = mKernelList[channel];
if (!kernel) {
continue;
}
ioSilence = silentInput;
const AudioBuffer* const srcBuffer = &inBuffer.mBuffers[channel]; // NOLINT subscript
AudioBuffer* const destBuffer = &outBuffer.mBuffers[channel]; // NOLINT subscript
kernel->Process(static_cast<const Float32*>(srcBuffer->mData),
static_cast<Float32*>(destBuffer->mData), inFramesToProcess, ioSilence);
if (!ioSilence) {
ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence;
}
}
return noErr;
}
Float64 AUEffectBase::GetSampleRate() { return Output(0).GetStreamFormat().mSampleRate; }
UInt32 AUEffectBase::GetNumberOfChannels() { return Output(0).GetStreamFormat().mChannelsPerFrame; }
} // namespace ausdk
@@ -0,0 +1,194 @@
/*!
@file AudioUnitSDK/AUEffectBase.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_AUEffectBase_h
#define AudioUnitSDK_AUEffectBase_h
#include <AudioUnitSDK/AUBase.h>
#include <AudioUnitSDK/AUSilentTimeout.h>
#include <memory>
namespace ausdk {
class AUKernelBase;
/*!
@class AUEffectBase
@brief Base class for an effect with one input stream, one output stream, and any number of
channels.
*/
class AUEffectBase : public AUBase {
public:
explicit AUEffectBase(AudioComponentInstance audioUnit, bool inProcessesInPlace = true);
AUEffectBase(const AUEffectBase&) = delete;
AUEffectBase(AUEffectBase&&) = delete;
AUEffectBase& operator=(const AUEffectBase&) = delete;
AUEffectBase& operator=(AUEffectBase&&) = delete;
~AUEffectBase() override = default;
OSStatus Initialize() override;
void Cleanup() override;
OSStatus Reset(AudioUnitScope inScope, AudioUnitElement inElement) override;
OSStatus GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable) override;
OSStatus GetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, void* outData) override;
OSStatus SetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, const void* inData, UInt32 inDataSize) override;
bool StreamFormatWritable(AudioUnitScope scope, AudioUnitElement element) override;
OSStatus ChangeStreamFormat(AudioUnitScope inScope, AudioUnitElement inElement,
const AudioStreamBasicDescription& inPrevFormat,
const AudioStreamBasicDescription& inNewFormat) override;
OSStatus Render(AudioUnitRenderActionFlags& ioActionFlags, const AudioTimeStamp& inTimeStamp,
UInt32 nFrames) override;
// our virtual methods
// If your unit processes N to N channels, and there are no interactions between channels,
// it can override NewKernel to create a mono processing object per channel. Otherwise,
// don't override NewKernel, and instead, override ProcessBufferLists.
virtual std::unique_ptr<AUKernelBase> NewKernel() { return {}; }
OSStatus ProcessBufferLists(AudioUnitRenderActionFlags& ioActionFlags,
const AudioBufferList& inBuffer, AudioBufferList& outBuffer,
UInt32 inFramesToProcess) override;
// convenience format accessors (use output 0's format)
Float64 GetSampleRate();
UInt32 GetNumberOfChannels();
// convenience wrappers for accessing parameters in the global scope
using AUBase::SetParameter;
void SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value)
{
Globals()->SetParameter(paramID, value);
}
using AUBase::GetParameter;
AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID)
{
return Globals()->GetParameter(paramID);
}
[[nodiscard]] bool CanScheduleParameters() const override { return true; }
// This is used for the property value - to reflect to the UI if an effect is bypassed
[[nodiscard]] bool IsBypassEffect() const noexcept { return mBypassEffect; }
virtual void SetBypassEffect(bool inFlag) { mBypassEffect = inFlag; }
void SetParamHasSampleRateDependency(bool inFlag) noexcept { mParamSRDep = inFlag; }
[[nodiscard]] bool GetParamHasSampleRateDependency() const noexcept { return mParamSRDep; }
/// Context, passed as `void* userData`, for `ProcessScheduledSlice()`.
struct ScheduledProcessParams {
AudioUnitRenderActionFlags* actionFlags = nullptr;
AudioBufferList* inputBufferList = nullptr;
AudioBufferList* outputBufferList = nullptr;
};
OSStatus ProcessScheduledSlice(void* inUserData, UInt32 inStartFrameInBuffer,
UInt32 inSliceFramesToProcess, UInt32 inTotalBufferFrames) override;
[[nodiscard]] bool ProcessesInPlace() const noexcept { return mProcessesInPlace; }
void SetProcessesInPlace(bool inProcessesInPlace) noexcept
{
mProcessesInPlace = inProcessesInPlace;
}
using KernelList = std::vector<std::unique_ptr<AUKernelBase>>;
protected:
void MaintainKernels();
// This is used in the render call to see if an effect is bypassed
// It can return a different status than IsBypassEffect (though it MUST take that into account)
virtual bool ShouldBypassEffect() { return IsBypassEffect(); }
[[nodiscard]] AUKernelBase* GetKernel(UInt32 index) const
{
return (index < mKernelList.size()) ? mKernelList[index].get() : nullptr;
}
[[nodiscard]] const KernelList& GetKernelList() const noexcept { return mKernelList; }
bool IsInputSilent(AudioUnitRenderActionFlags inActionFlags, UInt32 inFramesToProcess)
{
bool inputSilent = (inActionFlags & kAudioUnitRenderAction_OutputIsSilence) != 0;
// take latency and tail time into account when propagating the silent bit
const auto silentTimeoutFrames =
static_cast<UInt32>(GetSampleRate() * (GetLatency() + GetTailTime()));
mSilentTimeout.Process(inFramesToProcess, silentTimeoutFrames, inputSilent);
return inputSilent;
}
#if TARGET_OS_IPHONE
void SetOnlyOneKernel(bool inUseOnlyOneKernel) noexcept
{
mOnlyOneKernel = inUseOnlyOneKernel;
} // set in ctor of subclass that wants it.
#endif
private:
KernelList mKernelList;
bool mBypassEffect{ false };
bool mParamSRDep{ false };
bool mProcessesInPlace;
AUSilentTimeout mSilentTimeout;
AUOutputElement* mMainOutput{ nullptr };
AUInputElement* mMainInput{ nullptr };
#if TARGET_OS_IPHONE
bool mOnlyOneKernel;
#endif
UInt32 mBytesPerFrame = 0;
};
/*!
@class AUKernelBase
@brief Base class for a signal-processing "kernel", an object that performs DSP on one channel
of an audio stream.
*/
class AUKernelBase {
public:
explicit AUKernelBase(AUEffectBase& inAudioUnit) : mAudioUnit(inAudioUnit) {}
AUSDK_DEPRECATED("Construct with a reference")
explicit AUKernelBase(AUEffectBase* inAudioUnit) : mAudioUnit(*inAudioUnit) {}
AUKernelBase(const AUKernelBase&) = delete;
AUKernelBase(AUKernelBase&&) = delete;
AUKernelBase& operator=(const AUKernelBase&) = delete;
AUKernelBase& operator=(AUKernelBase&&) = delete;
virtual ~AUKernelBase() = default;
virtual void Reset() {}
virtual void Process(const Float32* /*inSourceP*/, Float32* /*inDestP*/,
UInt32 /*inFramesToProcess*/, bool& /*ioSilence*/) = 0;
Float64 GetSampleRate() { return mAudioUnit.GetSampleRate(); }
AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID)
{
return mAudioUnit.GetParameter(paramID);
}
void SetChannelNum(UInt32 inChan) noexcept { mChannelNum = inChan; }
[[nodiscard]] UInt32 GetChannelNum() const noexcept { return mChannelNum; }
protected:
AUEffectBase& mAudioUnit; // NOLINT protected
UInt32 mChannelNum = 0; // NOLINT protected
};
} // namespace ausdk
#endif // AudioUnitSDK_AUEffectBase_h
@@ -0,0 +1,85 @@
/*!
@file AudioUnitSDK/AUInputElement.cpp
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#include <AudioUnitSDK/AUBase.h>
namespace ausdk {
constexpr bool HasGoodBufferPointers(const AudioBufferList& abl, UInt32 nBytes) noexcept
{
const AudioBuffer* buf = abl.mBuffers; // NOLINT
for (UInt32 i = abl.mNumberBuffers; i-- > 0; ++buf) { // NOLINT
if (buf->mData == nullptr || buf->mDataByteSize < nBytes) {
return false;
}
}
return true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// AUInputElement::SetConnection
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AUInputElement::SetConnection(const AudioUnitConnection& conn)
{
if (conn.sourceAudioUnit == nullptr) {
Disconnect();
return;
}
mInputType = EInputType::FromConnection;
mConnection = conn;
AllocateBuffer();
}
void AUInputElement::Disconnect()
{
mInputType = EInputType::NoInput;
IOBuffer().Deallocate();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// AUInputElement::SetInputCallback
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AUInputElement::SetInputCallback(AURenderCallback proc, void* refCon)
{
if (proc == nullptr) {
Disconnect();
} else {
mInputType = EInputType::FromCallback;
mInputProc = proc;
mInputProcRefCon = refCon;
AllocateBuffer();
}
}
OSStatus AUInputElement::SetStreamFormat(const AudioStreamBasicDescription& fmt)
{
const OSStatus err = AUIOElement::SetStreamFormat(fmt);
if (err == noErr) {
AllocateBuffer();
}
return err;
}
OSStatus AUInputElement::PullInput(AudioUnitRenderActionFlags& ioActionFlags,
const AudioTimeStamp& inTimeStamp, AudioUnitElement inElement, UInt32 nFrames)
{
if (!IsActive()) {
return kAudioUnitErr_NoConnection;
}
auto& iob = IOBuffer();
AudioBufferList& pullBuffer = (HasConnection() || !WillAllocateBuffer())
? iob.PrepareNullBuffer(GetStreamFormat(), nFrames)
: iob.PrepareBuffer(GetStreamFormat(), nFrames);
return PullInputWithBufferList(ioActionFlags, inTimeStamp, inElement, nFrames, pullBuffer);
}
} // namespace ausdk
@@ -0,0 +1,91 @@
/*!
@file AudioUnitSDK/AUInputElement.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_AUInputElement_h
#define AudioUnitSDK_AUInputElement_h
#include <AudioUnitSDK/AUBuffer.h>
#include <AudioUnitSDK/AUScopeElement.h>
namespace ausdk {
/*!
@class AUInputElement
@brief Implements an audio unit input element, managing the source of input from a callback
or connection.
*/
class AUInputElement : public AUIOElement {
public:
using AUIOElement::AUIOElement;
// AUElement override
OSStatus SetStreamFormat(const AudioStreamBasicDescription& fmt) override;
[[nodiscard]] bool NeedsBufferSpace() const override { return IsCallback(); }
void SetConnection(const AudioUnitConnection& conn);
void SetInputCallback(AURenderCallback proc, void* refCon);
[[nodiscard]] bool IsActive() const noexcept { return mInputType != EInputType::NoInput; }
[[nodiscard]] bool IsCallback() const noexcept
{
return mInputType == EInputType::FromCallback;
}
[[nodiscard]] bool HasConnection() const noexcept
{
return mInputType == EInputType::FromConnection;
}
OSStatus PullInput(AudioUnitRenderActionFlags& ioActionFlags, const AudioTimeStamp& inTimeStamp,
AudioUnitElement inElement, UInt32 nFrames);
OSStatus PullInputWithBufferList(AudioUnitRenderActionFlags& ioActionFlags,
const AudioTimeStamp& inTimeStamp, AudioUnitElement inElement, UInt32 nFrames,
AudioBufferList& inBufferList);
protected:
void Disconnect();
private:
enum class EInputType { NoInput, FromConnection, FromCallback };
EInputType mInputType{ EInputType::NoInput };
// if from callback:
AURenderCallback mInputProc{ nullptr };
void* mInputProcRefCon{ nullptr };
// if from connection:
AudioUnitConnection mConnection{};
};
inline OSStatus AUInputElement::PullInputWithBufferList(AudioUnitRenderActionFlags& ioActionFlags,
const AudioTimeStamp& inTimeStamp, AudioUnitElement inElement, UInt32 nFrames,
AudioBufferList& inBufferList)
{
OSStatus theResult = noErr;
if (HasConnection()) {
// only support connections for V2 audio units
theResult = AudioUnitRender(mConnection.sourceAudioUnit, &ioActionFlags, &inTimeStamp,
mConnection.sourceOutputNumber, nFrames, &inBufferList);
} else {
// kFromCallback:
theResult = (mInputProc)(mInputProcRefCon, &ioActionFlags, &inTimeStamp, inElement, nFrames,
&inBufferList);
}
if (mInputType == EInputType::NoInput) { // defense: the guy upstream could have disconnected
// it's a horrible thing to do, but may happen!
return kAudioUnitErr_NoConnection;
}
#if !TARGET_OS_IPHONE || DEBUG
if (theResult == noErr) { // if there's already an error, there's no point (and maybe some harm)
// in validating.
if (ABL::IsBogusAudioBufferList(inBufferList) & 1) {
return kAudioUnitErr_InvalidPropertyValue;
}
}
#endif
return theResult;
}
} // namespace ausdk
#endif // AudioUnitSDK_AUInputElement_h
@@ -0,0 +1,251 @@
/*!
@file AudioUnitSDK/AUMIDIBase.cpp
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#include <AudioUnitSDK/AUMIDIBase.h>
#include <CoreMIDI/CoreMIDI.h>
namespace ausdk {
// MIDI CC data bytes
constexpr uint8_t kMIDIController_AllSoundOff = 120u;
constexpr uint8_t kMIDIController_ResetAllControllers = 121u;
constexpr uint8_t kMIDIController_AllNotesOff = 123u;
OSStatus AUMIDIBase::DelegateGetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable)
{
(void)inScope;
(void)inElement;
(void)outDataSize;
(void)outWritable;
switch (inID) { // NOLINT if/else?!
#if AUSDK_HAVE_XML_NAMES
case kMusicDeviceProperty_MIDIXMLNames:
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
AUSDK_Require(GetXMLNames(nullptr) == noErr, kAudioUnitErr_InvalidProperty);
outDataSize = sizeof(CFURLRef);
outWritable = false;
return noErr;
#endif
#if AUSDK_HAVE_MIDI_MAPPING
case kAudioUnitProperty_AllParameterMIDIMappings:
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
outWritable = true;
outDataSize = sizeof(AUParameterMIDIMapping) * mMIDIMapper->GetNumberMaps();
return noErr;
case kAudioUnitProperty_HotMapParameterMIDIMapping:
case kAudioUnitProperty_AddParameterMIDIMapping:
case kAudioUnitProperty_RemoveParameterMIDIMapping:
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
outWritable = true;
outDataSize = sizeof(AUParameterMIDIMapping);
return noErr;
#endif
default:
return kAudioUnitErr_InvalidProperty;
}
}
OSStatus AUMIDIBase::DelegateGetProperty(
AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData)
{
(void)inScope;
(void)inElement;
(void)outData;
switch (inID) { // NOLINT if/else?!
#if AUSDK_HAVE_XML_NAMES
case kMusicDeviceProperty_MIDIXMLNames:
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
return GetXMLNames(static_cast<CFURLRef*>(outData));
#endif
#if AUSDK_HAVE_MIDI_MAPPING
case kAudioUnitProperty_AllParameterMIDIMappings: {
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
AUParameterMIDIMapping* const maps = (static_cast<AUParameterMIDIMapping*>(outData));
mMIDIMapper->GetMaps(maps);
return noErr;
}
case kAudioUnitProperty_HotMapParameterMIDIMapping: {
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
AUParameterMIDIMapping* const map = (static_cast<AUParameterMIDIMapping*>(outData));
mMIDIMapper->GetHotParameterMap(*map);
return noErr;
}
#endif
default:
return kAudioUnitErr_InvalidProperty;
}
}
OSStatus AUMIDIBase::DelegateSetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, const void* inData, UInt32 inDataSize)
{
(void)inScope;
(void)inElement;
(void)inData;
(void)inDataSize;
switch (inID) {
#if AUSDK_HAVE_MIDI_MAPPING
case kAudioUnitProperty_AddParameterMIDIMapping: {
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
const auto* const maps = static_cast<const AUParameterMIDIMapping*>(inData);
mMIDIMapper->AddParameterMapping(
maps, (inDataSize / sizeof(AUParameterMIDIMapping)), mAUBaseInstance);
mAUBaseInstance.PropertyChanged(
kAudioUnitProperty_AllParameterMIDIMappings, kAudioUnitScope_Global, 0);
return noErr;
}
case kAudioUnitProperty_RemoveParameterMIDIMapping: {
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
const auto* const maps = static_cast<const AUParameterMIDIMapping*>(inData);
bool didChange = false;
mMIDIMapper->RemoveParameterMapping(
maps, (inDataSize / sizeof(AUParameterMIDIMapping)), didChange);
if (didChange) {
mAUBaseInstance.PropertyChanged(
kAudioUnitProperty_AllParameterMIDIMappings, kAudioUnitScope_Global, 0);
}
return noErr;
}
case kAudioUnitProperty_HotMapParameterMIDIMapping: {
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
const auto& map = *static_cast<const AUParameterMIDIMapping*>(inData);
mMIDIMapper->SetHotMapping(map);
return noErr;
}
case kAudioUnitProperty_AllParameterMIDIMappings: {
AUSDK_Require(mMIDIMapper, kAudioUnitErr_InvalidProperty);
AUSDK_Require(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope);
AUSDK_Require(inElement == 0, kAudioUnitErr_InvalidElement);
const auto* const mappings = static_cast<const AUParameterMIDIMapping*>(inData);
mMIDIMapper->ReplaceAllMaps(
mappings, (inDataSize / sizeof(AUParameterMIDIMapping)), mAUBaseInstance);
return noErr;
}
#endif
default:
return kAudioUnitErr_InvalidProperty;
}
}
constexpr uint8_t MIDIStatusNibbleValue(uint8_t status) noexcept { return (status & 0xF0U) >> 4u; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// AUMIDIBase::HandleMIDIEvent
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OSStatus AUMIDIBase::HandleMIDIEvent(
UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame)
{
if (!mAUBaseInstance.IsInitialized()) {
return kAudioUnitErr_Uninitialized;
}
#if AUSDK_HAVE_MIDI_MAPPING
// you potentially have a choice to make here - if a param mapping matches, do you still want to
// process the MIDI event or not. The default behaviour is to continue on with the MIDI event.
if (mMIDIMapper) {
if (mMIDIMapper->HandleHotMapping(status, channel, data1, mAUBaseInstance)) {
mAUBaseInstance.PropertyChanged(
kAudioUnitProperty_HotMapParameterMIDIMapping, kAudioUnitScope_Global, 0);
} else {
mMIDIMapper->FindParameterMapEventMatch(
status, channel, data1, data2, inStartFrame, mAUBaseInstance);
}
}
#endif
switch (MIDIStatusNibbleValue(status)) {
case kMIDICVStatusNoteOn:
if (data2 != 0u) {
return HandleNoteOn(channel, data1, data2, inStartFrame);
} else {
// zero velocity translates to note off
return HandleNoteOff(channel, data1, data2, inStartFrame);
}
case kMIDICVStatusNoteOff:
return HandleNoteOff(channel, data1, data2, inStartFrame);
default:
return HandleNonNoteEvent(status, channel, data1, data2, inStartFrame);
}
}
OSStatus AUMIDIBase::HandleNonNoteEvent(
UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame)
{
switch (MIDIStatusNibbleValue(status)) {
case kMIDICVStatusPitchBend:
return HandlePitchWheel(channel, data1, data2, inStartFrame);
case kMIDICVStatusProgramChange:
return HandleProgramChange(channel, data1);
case kMIDICVStatusChannelPressure:
return HandleChannelPressure(channel, data1, inStartFrame);
case kMIDICVStatusControlChange: {
switch (data1) {
case kMIDIController_AllNotesOff:
return HandleAllNotesOff(channel);
case kMIDIController_ResetAllControllers:
return HandleResetAllControllers(channel);
case kMIDIController_AllSoundOff:
return HandleAllSoundOff(channel);
default:
return HandleControlChange(channel, data1, data2, inStartFrame);
}
}
case kMIDICVStatusPolyPressure:
return HandlePolyPressure(channel, data1, data2, inStartFrame);
default:
return noErr;
}
}
OSStatus AUMIDIBase::SysEx(const UInt8* inData, UInt32 inLength)
{
if (!mAUBaseInstance.IsInitialized()) {
return kAudioUnitErr_Uninitialized;
}
return HandleSysEx(inData, inLength);
}
} // namespace ausdk
@@ -0,0 +1,169 @@
/*!
@file AudioUnitSDK/AUMIDIBase.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_AUMIDIBase_h
#define AudioUnitSDK_AUMIDIBase_h
#include <AudioUnitSDK/AUBase.h>
#ifndef AUSDK_HAVE_XML_NAMES
#define AUSDK_HAVE_XML_NAMES TARGET_OS_OSX // NOLINT(cppcoreguidelines-macro-usage)
#endif
#ifndef AUSDK_HAVE_MIDI_MAPPING
#define AUSDK_HAVE_MIDI_MAPPING TARGET_OS_OSX // NOLINT(cppcoreguidelines-macro-usage)
#endif
namespace ausdk {
#if AUSDK_HAVE_MIDI_MAPPING
/// Abstract interface for parameter MIDI mapping
class AUMIDIMapper {
public:
AUMIDIMapper() = default;
virtual ~AUMIDIMapper() = default;
AUMIDIMapper(const AUMIDIMapper&) = delete;
AUMIDIMapper(AUMIDIMapper&&) = delete;
AUMIDIMapper& operator=(const AUMIDIMapper&) = delete;
AUMIDIMapper& operator=(AUMIDIMapper&&) = delete;
[[nodiscard]] virtual UInt32 GetNumberMaps() const = 0;
virtual void GetMaps(AUParameterMIDIMapping* outMapping) = 0;
virtual void GetHotParameterMap(AUParameterMIDIMapping& outMapping) = 0;
virtual void AddParameterMapping(
const AUParameterMIDIMapping* maps, UInt32 count, AUBase& auBase) = 0;
virtual void RemoveParameterMapping(
const AUParameterMIDIMapping* maps, UInt32 count, bool& outDidChange) = 0;
virtual void SetHotMapping(const AUParameterMIDIMapping& mapping) = 0;
virtual void ReplaceAllMaps(
const AUParameterMIDIMapping* maps, UInt32 count, AUBase& auBase) = 0;
virtual bool HandleHotMapping(UInt8 status, UInt8 channel, UInt8 data1, AUBase& auBase) = 0;
virtual bool FindParameterMapEventMatch(UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2,
UInt32 inStartFrame, AUBase& auBase) = 0;
};
#endif
// ________________________________________________________________________
// AUMIDIBase
//
/*!
@class AUMIDIBase
@brief Auxiliary class supporting MIDI events.
*/
class AUMIDIBase {
public:
explicit AUMIDIBase(AUBase& inBase) : mAUBaseInstance(inBase) {}
virtual ~AUMIDIBase() = default;
AUMIDIBase(const AUMIDIBase&) = delete;
AUMIDIBase(AUMIDIBase&&) = delete;
AUMIDIBase& operator=(const AUMIDIBase&) = delete;
AUMIDIBase& operator=(AUMIDIBase&&) = delete;
virtual OSStatus MIDIEvent(
UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame)
{
const UInt32 strippedStatus = inStatus & 0xf0U; // NOLINT
const UInt32 channel = inStatus & 0x0fU; // NOLINT
return HandleMIDIEvent(strippedStatus, channel, inData1, inData2, inOffsetSampleFrame);
}
#if AUSDK_MIDI2_AVAILABLE
virtual OSStatus MIDIEventList(
UInt32 /*inOffsetSampleFrame*/, const MIDIEventList* /*eventList*/)
{
return kAudio_UnimplementedError;
}
#endif
virtual OSStatus SysEx(const UInt8* inData, UInt32 inLength);
virtual OSStatus DelegateGetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable);
virtual OSStatus DelegateGetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, void* outData);
virtual OSStatus DelegateSetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
protected:
// MIDI dispatch
virtual OSStatus HandleMIDIEvent(
UInt8 inStatus, UInt8 inChannel, UInt8 inData1, UInt8 inData2, UInt32 inStartFrame);
virtual OSStatus HandleNonNoteEvent(
UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame);
// Old name
AUSDK_DEPRECATED("HandleMIDIEvent")
OSStatus HandleMidiEvent(
UInt8 inStatus, UInt8 inChannel, UInt8 inData1, UInt8 inData2, UInt32 inStartFrame)
{
return HandleMIDIEvent(inStatus, inChannel, inData1, inData2, inStartFrame);
}
#if AUSDK_HAVE_XML_NAMES
virtual OSStatus GetXMLNames(CFURLRef* /*outNameDocument*/)
{
return kAudioUnitErr_InvalidProperty;
} // if not overridden, it's unsupported
#endif
// channel messages
virtual OSStatus HandleNoteOn(
UInt8 /*inChannel*/, UInt8 /*inNoteNumber*/, UInt8 /*inVelocity*/, UInt32 /*inStartFrame*/)
{
return noErr;
}
virtual OSStatus HandleNoteOff(
UInt8 /*inChannel*/, UInt8 /*inNoteNumber*/, UInt8 /*inVelocity*/, UInt32 /*inStartFrame*/)
{
return noErr;
}
virtual OSStatus HandleControlChange(
UInt8 /*inChannel*/, UInt8 /*inController*/, UInt8 /*inValue*/, UInt32 /*inStartFrame*/)
{
return noErr;
}
virtual OSStatus HandlePitchWheel(
UInt8 /*inChannel*/, UInt8 /*inPitch1*/, UInt8 /*inPitch2*/, UInt32 /*inStartFrame*/)
{
return noErr;
}
virtual OSStatus HandleChannelPressure(
UInt8 /*inChannel*/, UInt8 /*inValue*/, UInt32 /*inStartFrame*/)
{
return noErr;
}
virtual OSStatus HandleProgramChange(UInt8 /*inChannel*/, UInt8 /*inValue*/) { return noErr; }
virtual OSStatus HandlePolyPressure(
UInt8 /*inChannel*/, UInt8 /*inKey*/, UInt8 /*inValue*/, UInt32 /*inStartFrame*/)
{
return noErr;
}
virtual OSStatus HandleResetAllControllers(UInt8 /*inChannel*/) { return noErr; }
virtual OSStatus HandleAllNotesOff(UInt8 /*inChannel*/) { return noErr; }
virtual OSStatus HandleAllSoundOff(UInt8 /*inChannel*/) { return noErr; }
// System messages
virtual OSStatus HandleSysEx(const UInt8* /*inData*/, UInt32 /*inLength*/) { return noErr; }
#if AUSDK_HAVE_MIDI_MAPPING
void SetMIDIMapper(const std::shared_ptr<AUMIDIMapper>& mapper) { mMIDIMapper = mapper; }
#endif
private:
AUBase& mAUBaseInstance;
#if AUSDK_HAVE_MIDI_MAPPING
std::shared_ptr<AUMIDIMapper> mMIDIMapper;
#endif
};
} // namespace ausdk
#endif // AudioUnitSDK_AUMIDIBase_h
@@ -0,0 +1,53 @@
/*!
@file AudioUnitSDK/AUMIDIEffectBase.cpp
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#include <AudioUnitSDK/AUMIDIEffectBase.h>
namespace ausdk {
AUMIDIEffectBase::AUMIDIEffectBase(AudioComponentInstance inInstance, bool inProcessesInPlace)
: AUEffectBase(inInstance, inProcessesInPlace), AUMIDIBase(*static_cast<AUBase*>(this))
{
}
OSStatus AUMIDIEffectBase::GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable)
{
OSStatus result =
AUEffectBase::GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable);
if (result == kAudioUnitErr_InvalidProperty) {
result =
AUMIDIBase::DelegateGetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable);
}
return result;
}
OSStatus AUMIDIEffectBase::GetProperty(
AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData)
{
OSStatus result = AUEffectBase::GetProperty(inID, inScope, inElement, outData);
if (result == kAudioUnitErr_InvalidProperty) {
result = AUMIDIBase::DelegateGetProperty(inID, inScope, inElement, outData);
}
return result;
}
OSStatus AUMIDIEffectBase::SetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, const void* inData, UInt32 inDataSize)
{
OSStatus result = AUEffectBase::SetProperty(inID, inScope, inElement, inData, inDataSize);
if (result == kAudioUnitErr_InvalidProperty) {
result = AUMIDIBase::DelegateSetProperty(inID, inScope, inElement, inData, inDataSize);
}
return result;
}
} // namespace ausdk
@@ -0,0 +1,40 @@
/*!
@file AudioUnitSDK/AUMIDIEffectBase.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_AUMIDIEffectBase_h
#define AudioUnitSDK_AUMIDIEffectBase_h
#include <AudioUnitSDK/AUEffectBase.h>
#include <AudioUnitSDK/AUMIDIBase.h>
namespace ausdk {
/*!
@class AUMIDIEffectBase
@brief Subclass of AUEffectBase and AUMIDIBase, providing an abstract base class for
music effects.
*/
class AUMIDIEffectBase : public AUEffectBase, public AUMIDIBase {
public:
explicit AUMIDIEffectBase(AudioComponentInstance inInstance, bool inProcessesInPlace = false);
OSStatus MIDIEvent(
UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame) override
{
return AUMIDIBase::MIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame);
}
OSStatus SysEx(const UInt8* inData, UInt32 inLength) override
{
return AUMIDIBase::SysEx(inData, inLength);
}
OSStatus GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable) override;
OSStatus GetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, void* outData) override;
OSStatus SetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, const void* inData, UInt32 inDataSize) override;
};
} // namespace ausdk
#endif // AudioUnitSDK_AUMIDIEffectBase_h
@@ -0,0 +1,20 @@
/*!
@file AudioUnitSDK/AUMIDIUtility.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_AUMIDIUtility_h
#define AudioUnitSDK_AUMIDIUtility_h
// OS
#if defined __has_include && __has_include(<AvailabilityVersions.h>)
#include <AvailabilityVersions.h>
#endif
#if defined(__MAC_12_0) || defined(__IPHONE_15_0)
#define AUSDK_MIDI2_AVAILABLE 1
#endif
#if AUSDK_MIDI2_AVAILABLE
#include <CoreMIDI/MIDIServices.h>
#endif
#endif // AudioUnitSDK_AUMIDIUtility_h
@@ -0,0 +1,27 @@
/*!
@file AudioUnitSDK/AUOutputElement.cpp
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#include <AudioUnitSDK/AUBase.h>
#include <AudioUnitSDK/AUOutputElement.h>
namespace ausdk {
AUOutputElement::AUOutputElement(AUBase& audioUnit) : AUIOElement(audioUnit) { AllocateBuffer(); }
AUOutputElement::AUOutputElement(AUBase& audioUnit, const AudioStreamBasicDescription& format)
: AUIOElement{ audioUnit, format }
{
AllocateBuffer();
}
OSStatus AUOutputElement::SetStreamFormat(const AudioStreamBasicDescription& desc)
{
const OSStatus result = AUIOElement::SetStreamFormat(desc); // inherited
if (result == noErr) {
AllocateBuffer();
}
return result;
}
} // namespace ausdk
@@ -0,0 +1,33 @@
/*!
@file AudioUnitSDK/AUOutputElement.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_AUOutputElement_h
#define AudioUnitSDK_AUOutputElement_h
#include <AudioUnitSDK/AUBuffer.h>
#include <AudioUnitSDK/AUScopeElement.h>
namespace ausdk {
/*!
@class AUOutputElement
@brief Implements an audio unit output element.
*/
class AUOutputElement : public AUIOElement {
public:
explicit AUOutputElement(AUBase& audioUnit);
AUOutputElement(AUBase& audioUnit, const AudioStreamBasicDescription& format);
AUSDK_DEPRECATED("Construct with a reference")
explicit AUOutputElement(AUBase* audioUnit) : AUOutputElement(*audioUnit) {}
// AUElement override
OSStatus SetStreamFormat(const AudioStreamBasicDescription& desc) override;
[[nodiscard]] bool NeedsBufferSpace() const override { return true; }
};
} // namespace ausdk
#endif // AudioUnitSDK_AUOutputElement_h
@@ -0,0 +1,773 @@
/*!
@file AudioUnitSDK/AUPlugInDispatch.cpp
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#include <AudioUnitSDK/AUBase.h>
#include <AudioUnitSDK/AUPlugInDispatch.h>
#include <AudioUnitSDK/AUUtility.h>
#include <AudioUnitSDK/ComponentBase.h>
#include <cmath>
#include <cstddef>
#define CATCH_EXCEPTIONS_IN_RENDER_METHODS TARGET_OS_OSX // NOLINT
#define HAVE_MUSICDEVICE_PREPARE_RELEASE TARGET_OS_OSX // NOLINT
namespace ausdk {
// ------------------------------------------------------------------------------------------------
static auto AUInstance(void* self)
{
return reinterpret_cast<AUBase*>( // NOLINT reinterpret_cast
&(static_cast<AudioComponentPlugInInstance*>(self)->mInstanceStorage));
}
// ------------------------------------------------------------------------------------------------
class AUInstanceGuard {
public:
explicit AUInstanceGuard(void* self) : mGuard(AUInstance(self)->GetMutex()) {}
private:
const AUEntryGuard mGuard;
};
// ------------------------------------------------------------------------------------------------
static bool IsValidParameterValue(AudioUnitParameterValue value) { return std::isfinite(value); }
static bool AreValidParameterEvents(const AudioUnitParameterEvent* events, UInt32 numEvents)
{
if (events == nullptr) {
return true;
}
for (UInt32 i = 0; i < numEvents; ++i) {
const auto& event = events[i]; // NOLINT
switch (event.eventType) {
case kParameterEvent_Immediate: {
if (!IsValidParameterValue(event.eventValues.immediate.value)) { // NOLINT
return false;
}
break;
}
case kParameterEvent_Ramped: {
if (!IsValidParameterValue(event.eventValues.ramp.startValue) || // NOLINT
!IsValidParameterValue(event.eventValues.ramp.endValue)) { // NOLINT
return false;
}
break;
}
default:
break;
}
}
return true;
}
// ------------------------------------------------------------------------------------------------
static OSStatus AUMethodInitialize(void* self)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
result = AUInstance(self)->DoInitialize();
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodUninitialize(void* self)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
AUInstance(self)->DoCleanup();
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodGetPropertyInfo(void* self, AudioUnitPropertyID prop, AudioUnitScope scope,
AudioUnitElement elem, UInt32* outDataSize, Boolean* outWritable)
{
OSStatus result = noErr;
try {
UInt32 dataSize = 0; // 13517289 GetPropetyInfo was returning an uninitialized value when
// there is an error. This is a problem for auval.
bool writable = false;
const AUInstanceGuard guard(self);
result = AUInstance(self)->DispatchGetPropertyInfo(prop, scope, elem, dataSize, writable);
if (outDataSize != nullptr) {
*outDataSize = dataSize;
}
if (outWritable != nullptr) {
*outWritable = static_cast<Boolean>(writable);
}
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodGetProperty(void* self, AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, void* outData, UInt32* ioDataSize)
{
OSStatus result = noErr;
try {
bool writable = false;
const AUInstanceGuard guard(self);
if (ioDataSize == nullptr) {
AUSDK_LogError("AudioUnitGetProperty: null size pointer");
return kAudio_ParamError;
}
if (outData == nullptr) {
UInt32 dataSize = 0;
result = AUInstance(self)->DispatchGetPropertyInfo(
inID, inScope, inElement, dataSize, writable);
*ioDataSize = dataSize;
return result;
}
const auto clientBufferSize = *ioDataSize;
if (clientBufferSize == 0) {
AUSDK_LogError("AudioUnitGetProperty: *ioDataSize == 0 on entry");
return kAudio_ParamError;
}
UInt32 actualPropertySize = 0;
result = AUInstance(self)->DispatchGetPropertyInfo(
inID, inScope, inElement, actualPropertySize, writable);
if (result != noErr) {
return result;
}
std::vector<std::byte> tempBuffer;
void* destBuffer = nullptr;
if (clientBufferSize < actualPropertySize) {
tempBuffer.resize(actualPropertySize);
destBuffer = tempBuffer.data();
} else {
destBuffer = outData;
}
result = AUInstance(self)->DispatchGetProperty(inID, inScope, inElement, destBuffer);
if (result == noErr) {
if (clientBufferSize < actualPropertySize && !tempBuffer.empty()) {
memcpy(outData, tempBuffer.data(), clientBufferSize);
// ioDataSize remains correct, the number of bytes we wrote
} else {
*ioDataSize = actualPropertySize;
}
} else {
*ioDataSize = 0;
}
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodSetProperty(void* self, AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, const void* inData, UInt32 inDataSize)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
if ((inData != nullptr) && (inDataSize != 0u)) {
result =
AUInstance(self)->DispatchSetProperty(inID, inScope, inElement, inData, inDataSize);
} else {
if (inData == nullptr && inDataSize == 0) {
result = AUInstance(self)->DispatchRemovePropertyValue(inID, inScope, inElement);
} else {
if (inData == nullptr) {
AUSDK_LogError("AudioUnitSetProperty: inData == NULL");
return kAudio_ParamError;
}
if (inDataSize == 0) {
AUSDK_LogError("AudioUnitSetProperty: inDataSize == 0");
return kAudio_ParamError;
}
}
}
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodAddPropertyListener(
void* self, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void* userData)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
result = AUInstance(self)->AddPropertyListener(prop, proc, userData);
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodRemovePropertyListener(
void* self, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
result = AUInstance(self)->RemovePropertyListener(prop, proc, nullptr, false);
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodRemovePropertyListenerWithUserData(
void* self, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void* userData)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
result = AUInstance(self)->RemovePropertyListener(prop, proc, userData, true);
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodAddRenderNotify(void* self, AURenderCallback proc, void* userData)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
result = AUInstance(self)->SetRenderNotification(proc, userData);
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodRemoveRenderNotify(void* self, AURenderCallback proc, void* userData)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
result = AUInstance(self)->RemoveRenderNotification(proc, userData);
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodGetParameter(void* self, AudioUnitParameterID param, AudioUnitScope scope,
AudioUnitElement elem, AudioUnitParameterValue* value)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
result = (value == nullptr ? kAudio_ParamError
: AUInstance(self)->GetParameter(param, scope, elem, *value));
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodSetParameter(void* self, AudioUnitParameterID param, AudioUnitScope scope,
AudioUnitElement elem, AudioUnitParameterValue value, UInt32 bufferOffset)
{
if (!IsValidParameterValue(value)) {
return kAudioUnitErr_InvalidParameterValue;
}
OSStatus result = noErr;
try {
// this is a (potentially) realtime method; no lock
result = AUInstance(self)->SetParameter(param, scope, elem, value, bufferOffset);
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodScheduleParameters(
void* self, const AudioUnitParameterEvent* events, UInt32 numEvents)
{
if (!AreValidParameterEvents(events, numEvents)) {
return kAudioUnitErr_InvalidParameterValue;
}
OSStatus result = noErr;
try {
// this is a (potentially) realtime method; no lock
result = AUInstance(self)->ScheduleParameter(events, numEvents);
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodRender(void* self, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames,
AudioBufferList* ioData)
{
OSStatus result = noErr;
#if CATCH_EXCEPTIONS_IN_RENDER_METHODS
try {
#endif
// this is a processing method; no lock
AudioUnitRenderActionFlags tempFlags{};
if (inTimeStamp == nullptr || ioData == nullptr) {
result = kAudio_ParamError;
} else {
if (ioActionFlags == nullptr) {
tempFlags = 0;
ioActionFlags = &tempFlags;
}
result = AUInstance(self)->DoRender(
*ioActionFlags, *inTimeStamp, inOutputBusNumber, inNumberFrames, *ioData);
}
#if CATCH_EXCEPTIONS_IN_RENDER_METHODS
}
AUSDK_Catch(result)
#endif
return result;
}
static OSStatus AUMethodComplexRender(void* self, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberOfPackets,
UInt32* outNumberOfPackets, AudioStreamPacketDescription* outPacketDescriptions,
AudioBufferList* ioData, void* outMetadata, UInt32* outMetadataByteSize)
{
OSStatus result = noErr;
#if CATCH_EXCEPTIONS_IN_RENDER_METHODS
try {
#endif
// this is a processing method; no lock
AudioUnitRenderActionFlags tempFlags{};
if (inTimeStamp == nullptr || ioData == nullptr) {
result = kAudio_ParamError;
} else {
if (ioActionFlags == nullptr) {
tempFlags = 0;
ioActionFlags = &tempFlags;
}
result = AUInstance(self)->ComplexRender(*ioActionFlags, *inTimeStamp,
inOutputBusNumber, inNumberOfPackets, outNumberOfPackets, outPacketDescriptions,
*ioData, outMetadata, outMetadataByteSize);
}
#if CATCH_EXCEPTIONS_IN_RENDER_METHODS
}
AUSDK_Catch(result)
#endif
return result;
}
static OSStatus AUMethodReset(void* self, AudioUnitScope scope, AudioUnitElement elem)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
result = AUInstance(self)->Reset(scope, elem);
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodProcess(void* self, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inNumberFrames, AudioBufferList* ioData)
{
OSStatus result = noErr;
#if CATCH_EXCEPTIONS_IN_RENDER_METHODS
try {
#endif
// this is a processing method; no lock
bool doParamCheck = true;
AudioUnitRenderActionFlags tempFlags{};
if (ioActionFlags == nullptr) {
tempFlags = 0;
ioActionFlags = &tempFlags;
} else {
if ((*ioActionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) != 0u) {
doParamCheck = false;
}
}
if (doParamCheck && (inTimeStamp == nullptr || ioData == nullptr)) {
result = kAudio_ParamError;
} else {
result =
AUInstance(self)->DoProcess(*ioActionFlags, *inTimeStamp, inNumberFrames, *ioData);
}
#if CATCH_EXCEPTIONS_IN_RENDER_METHODS
}
AUSDK_Catch(result)
#endif
return result;
}
static OSStatus AUMethodProcessMultiple(void* self, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inNumberFrames, UInt32 inNumberInputBufferLists,
const AudioBufferList** inInputBufferLists, UInt32 inNumberOutputBufferLists,
AudioBufferList** ioOutputBufferLists)
{
OSStatus result = noErr;
#if CATCH_EXCEPTIONS_IN_RENDER_METHODS
try {
#endif
// this is a processing method; no lock
bool doParamCheck = true;
AudioUnitRenderActionFlags tempFlags{};
if (ioActionFlags == nullptr) {
tempFlags = 0;
ioActionFlags = &tempFlags;
} else {
if ((*ioActionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) != 0u) {
doParamCheck = false;
}
}
if (doParamCheck && (inTimeStamp == nullptr || inInputBufferLists == nullptr ||
ioOutputBufferLists == nullptr)) {
result = kAudio_ParamError;
} else {
result = AUInstance(self)->DoProcessMultiple(*ioActionFlags, *inTimeStamp,
inNumberFrames, inNumberInputBufferLists, inInputBufferLists,
inNumberOutputBufferLists, ioOutputBufferLists);
}
#if CATCH_EXCEPTIONS_IN_RENDER_METHODS
}
AUSDK_Catch(result)
#endif
return result;
}
// ------------------------------------------------------------------------------------------------
static OSStatus AUMethodStart(void* self)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
result = AUInstance(self)->Start();
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodStop(void* self)
{
OSStatus result = noErr;
try {
const AUInstanceGuard guard(self);
result = AUInstance(self)->Stop();
}
AUSDK_Catch(result)
return result;
}
// ------------------------------------------------------------------------------------------------
// I don't know what I'm doing here; conflicts with the multiple inheritence in MusicDeviceBase.
static OSStatus AUMethodMIDIEvent(
void* self, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame)
{
OSStatus result = noErr;
try {
// this is a potential render-time method; no lock
result = AUInstance(self)->MIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame);
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodSysEx(void* self, const UInt8* inData, UInt32 inLength)
{
OSStatus result = noErr;
try {
// this is a potential render-time method; no lock
result = AUInstance(self)->SysEx(inData, inLength);
}
AUSDK_Catch(result)
return result;
}
#if AUSDK_MIDI2_AVAILABLE
static OSStatus AUMethodMIDIEventList(
void* self, UInt32 inOffsetSampleFrame, const struct MIDIEventList* eventList)
{
if (eventList == nullptr) {
return kAudio_ParamError;
}
OSStatus result = noErr;
try {
// this is a potential render-time method; no lock
// Note that a MIDIEventList is variably-sized and can be backed by less memory than
// required, so it is Undefined Behavior to form a reference to it; we must only use
// pointers.
result = AUInstance(self)->MIDIEventList(inOffsetSampleFrame, eventList);
}
AUSDK_Catch(result)
return result;
}
#endif
static OSStatus AUMethodStartNote(void* self, MusicDeviceInstrumentID inInstrument,
MusicDeviceGroupID inGroupID, NoteInstanceID* outNoteInstanceID, UInt32 inOffsetSampleFrame,
const MusicDeviceNoteParams* inParams)
{
OSStatus result = noErr;
try {
// this is a potential render-time method; no lock
if (inParams == nullptr) {
result = kAudio_ParamError;
} else {
result = AUInstance(self)->StartNote(
inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, *inParams);
}
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodStopNote(void* self, MusicDeviceGroupID inGroupID,
NoteInstanceID inNoteInstanceID, UInt32 inOffsetSampleFrame)
{
OSStatus result = noErr;
try {
// this is a potential render-time method; no lock
result = AUInstance(self)->StopNote(inGroupID, inNoteInstanceID, inOffsetSampleFrame);
}
AUSDK_Catch(result)
return result;
}
#if HAVE_MUSICDEVICE_PREPARE_RELEASE
static OSStatus AUMethodPrepareInstrument(void* self, MusicDeviceInstrumentID inInstrument)
{
OSStatus result = noErr;
try {
// this is a potential render-time method; no lock
result = AUInstance(self)->PrepareInstrument(inInstrument); // NOLINT static via instance
}
AUSDK_Catch(result)
return result;
}
static OSStatus AUMethodReleaseInstrument(void* self, MusicDeviceInstrumentID inInstrument)
{
OSStatus result = noErr;
try {
// this is a potential render-time method; no lock
result = AUInstance(self)->ReleaseInstrument(inInstrument); // NOLINT static via instance
}
AUSDK_Catch(result)
return result;
}
#endif // HAVE_MUSICDEVICE_PREPARE_RELEASE
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#pragma mark -
#pragma mark Lookup Methods
AudioComponentMethod AUBaseLookup::Lookup(SInt16 selector)
{
switch (selector) {
case kAudioUnitInitializeSelect:
return (AudioComponentMethod)AUMethodInitialize; // NOLINT cast
case kAudioUnitUninitializeSelect:
return (AudioComponentMethod)AUMethodUninitialize; // NOLINT cast
case kAudioUnitGetPropertyInfoSelect:
return (AudioComponentMethod)AUMethodGetPropertyInfo; // NOLINT cast
case kAudioUnitGetPropertySelect:
return (AudioComponentMethod)AUMethodGetProperty; // NOLINT cast
case kAudioUnitSetPropertySelect:
return (AudioComponentMethod)AUMethodSetProperty; // NOLINT cast
case kAudioUnitAddPropertyListenerSelect:
return (AudioComponentMethod)AUMethodAddPropertyListener; // NOLINT cast
case kAudioUnitRemovePropertyListenerSelect:
return (AudioComponentMethod)AUMethodRemovePropertyListener; // NOLINT cast
case kAudioUnitRemovePropertyListenerWithUserDataSelect:
return (AudioComponentMethod)AUMethodRemovePropertyListenerWithUserData; // NOLINT cast
case kAudioUnitAddRenderNotifySelect:
return (AudioComponentMethod)AUMethodAddRenderNotify; // NOLINT cast
case kAudioUnitRemoveRenderNotifySelect:
return (AudioComponentMethod)AUMethodRemoveRenderNotify; // NOLINT cast
case kAudioUnitGetParameterSelect:
return (AudioComponentMethod)AUMethodGetParameter; // NOLINT cast
case kAudioUnitSetParameterSelect:
return (AudioComponentMethod)AUMethodSetParameter; // NOLINT cast
case kAudioUnitScheduleParametersSelect:
return (AudioComponentMethod)AUMethodScheduleParameters; // NOLINT cast
case kAudioUnitRenderSelect:
return (AudioComponentMethod)AUMethodRender; // NOLINT cast
case kAudioUnitResetSelect:
return (AudioComponentMethod)AUMethodReset; // NOLINT cast
default:
break;
}
return nullptr;
}
AudioComponentMethod AUOutputLookup::Lookup(SInt16 selector)
{
const AudioComponentMethod method = AUBaseLookup::Lookup(selector);
if (method != nullptr) {
return method;
}
switch (selector) {
case kAudioOutputUnitStartSelect:
return (AudioComponentMethod)AUMethodStart; // NOLINT cast
case kAudioOutputUnitStopSelect:
return (AudioComponentMethod)AUMethodStop; // NOLINT cast
default:
break;
}
return nullptr;
}
AudioComponentMethod AUComplexOutputLookup::Lookup(SInt16 selector)
{
AudioComponentMethod method = AUBaseLookup::Lookup(selector);
if (method != nullptr) {
return method;
}
method = AUOutputLookup::Lookup(selector);
if (method != nullptr) {
return method;
}
if (selector == kAudioUnitComplexRenderSelect) {
return (AudioComponentMethod)AUMethodComplexRender; // NOLINT cast
}
return nullptr;
}
AudioComponentMethod AUBaseProcessLookup::Lookup(SInt16 selector)
{
const AudioComponentMethod method = AUBaseLookup::Lookup(selector);
if (method != nullptr) {
return method;
}
if (selector == kAudioUnitProcessSelect) {
return (AudioComponentMethod)AUMethodProcess; // NOLINT cast
}
return nullptr;
}
AudioComponentMethod AUBaseProcessMultipleLookup::Lookup(SInt16 selector)
{
const AudioComponentMethod method = AUBaseLookup::Lookup(selector);
if (method != nullptr) {
return method;
}
if (selector == kAudioUnitProcessMultipleSelect) {
return (AudioComponentMethod)AUMethodProcessMultiple; // NOLINT cast
}
return nullptr;
}
AudioComponentMethod AUBaseProcessAndMultipleLookup::Lookup(SInt16 selector)
{
AudioComponentMethod method = AUBaseLookup::Lookup(selector);
if (method != nullptr) {
return method;
}
method = AUBaseProcessMultipleLookup::Lookup(selector);
if (method != nullptr) {
return method;
}
method = AUBaseProcessLookup::Lookup(selector);
if (method != nullptr) {
return method;
}
return nullptr;
}
inline AudioComponentMethod MIDI_Lookup(SInt16 selector)
{
switch (selector) {
case kMusicDeviceMIDIEventSelect:
return (AudioComponentMethod)AUMethodMIDIEvent; // NOLINT cast
case kMusicDeviceSysExSelect:
return (AudioComponentMethod)AUMethodSysEx; // NOLINT cast
#if AUSDK_MIDI2_AVAILABLE
case kMusicDeviceMIDIEventListSelect:
return (AudioComponentMethod)AUMethodMIDIEventList; // NOLINT cast
#endif
default:
break;
}
return nullptr;
}
AudioComponentMethod AUMIDILookup::Lookup(SInt16 selector)
{
const AudioComponentMethod method = AUBaseLookup::Lookup(selector);
if (method != nullptr) {
return method;
}
return MIDI_Lookup(selector);
}
AudioComponentMethod AUMIDIProcessLookup::Lookup(SInt16 selector)
{
const AudioComponentMethod method = AUBaseProcessLookup::Lookup(selector);
if (method != nullptr) {
return method;
}
return MIDI_Lookup(selector);
}
AudioComponentMethod AUMusicLookup::Lookup(SInt16 selector)
{
const AudioComponentMethod method = AUBaseLookup::Lookup(selector);
if (method != nullptr) {
return method;
}
switch (selector) {
case kMusicDeviceStartNoteSelect:
return (AudioComponentMethod)AUMethodStartNote; // NOLINT cast
case kMusicDeviceStopNoteSelect:
return (AudioComponentMethod)AUMethodStopNote; // NOLINT cast
#if HAVE_MUSICDEVICE_PREPARE_RELEASE
case kMusicDevicePrepareInstrumentSelect:
return (AudioComponentMethod)AUMethodPrepareInstrument; // NOLINT cast
case kMusicDeviceReleaseInstrumentSelect:
return (AudioComponentMethod)AUMethodReleaseInstrument; // NOLINT cast
#endif
default:
break;
}
return MIDI_Lookup(selector);
}
} // namespace ausdk
@@ -0,0 +1,110 @@
/*!
@file AudioUnitSDK/AUPlugInDispatch.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_AUPlugInDispatch_h
#define AudioUnitSDK_AUPlugInDispatch_h
#include <AudioUnitSDK/ComponentBase.h>
namespace ausdk {
/// Method lookup for a basic AUBase subclass.
struct AUBaseLookup {
static AudioComponentMethod Lookup(SInt16 selector);
};
/// Factory for a basic AUBase subclass.
template <class Implementor>
class AUBaseFactory : public APFactory<AUBaseLookup, Implementor> {
};
/// Method lookup for a AUBase subclass which implements I/O methods (Start, Stop).
struct AUOutputLookup {
static AudioComponentMethod Lookup(SInt16 selector);
};
/// Factory for an AUBase subclass which implements I/O methods (Start, Stop).
template <class Implementor>
class AUOutputBaseFactory : public APFactory<AUOutputLookup, Implementor> {
};
/// Method lookup for an AUBase subclass which implements I/O methods (Start, Stop) and
/// ComplexRender.
struct AUComplexOutputLookup {
static AudioComponentMethod Lookup(SInt16 selector);
};
/// Factory for an AUBase subclass which implements I/O methods (Start, Stop) and ComplexRender.
template <class Implementor>
class AUOutputComplexBaseFactory : public APFactory<AUComplexOutputLookup, Implementor> {
};
/// Method lookup for an AUBase subclass which implements Process.
struct AUBaseProcessLookup {
static AudioComponentMethod Lookup(SInt16 selector);
};
/// Factory for an AUBase subclass which implements Process.
template <class Implementor>
class AUBaseProcessFactory : public APFactory<AUBaseProcessLookup, Implementor> {
};
/// Method lookup for an AUBase subclass which implements ProcessMultiple.
struct AUBaseProcessMultipleLookup {
static AudioComponentMethod Lookup(SInt16 selector);
};
/// Factory for an AUBase subclass which implements ProcessMultiple.
template <class Implementor>
class AUBaseProcessMultipleFactory : public APFactory<AUBaseProcessMultipleLookup, Implementor> {
};
/// Method lookup for an AUBase subclass which implements Process and ProcessMultiple.
struct AUBaseProcessAndMultipleLookup {
static AudioComponentMethod Lookup(SInt16 selector);
};
/// Factory for an AUBase subclass which implements Process and ProcessMultiple.
template <class Implementor>
class AUBaseProcessAndMultipleFactory
: public APFactory<AUBaseProcessAndMultipleLookup, Implementor> {
};
/// Method lookup for an AUBase subclass which implements MusicDevice methods (MIDIEvent and SysEx).
struct AUMIDILookup {
static AudioComponentMethod Lookup(SInt16 selector);
};
/// Factory for an AUBase subclass which implements MusicDevice methods (MIDIEvent and SysEx).
template <class Implementor>
class AUMIDIEffectFactory : public APFactory<AUMIDILookup, Implementor> {
};
/// Method lookup for an AUBase subclass which implements Process and MusicDevice methods (MIDIEvent
/// and SysEx).
struct AUMIDIProcessLookup {
static AudioComponentMethod Lookup(SInt16 selector);
};
/// Factory for an AUBase subclass which implements Process and MusicDevice methods (MIDIEvent
/// and SysEx).
template <class Implementor>
class AUMIDIProcessFactory : public APFactory<AUMIDIProcessLookup, Implementor> {
};
/// Method lookup for an AUBase subclass which implements the full set of MusicDevice methods
/// (MIDIEvent, SysEx, StartNote, StopNote).
struct AUMusicLookup {
static AudioComponentMethod Lookup(SInt16 selector);
};
/// Factory for an AUBase subclass which implements the full set of MusicDevice methods
/// (MIDIEvent, SysEx, StartNote, StopNote).
template <class Implementor>
class AUMusicDeviceFactory : public APFactory<AUMusicLookup, Implementor> {
};
} // namespace ausdk
#endif // AudioUnitSDK_AUPlugInDispatch_h
@@ -0,0 +1,446 @@
/*!
@file AudioUnitSDK/AUScopeElement.cpp
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#include <AudioUnitSDK/AUBase.h>
#include <AudioUnitSDK/AUScopeElement.h>
#include <AudioToolbox/AudioUnitProperties.h>
#include <array>
namespace ausdk {
//_____________________________________________________________________________
//
// By default, parameterIDs may be arbitrarily spaced, and a flat map
// will be used for access. Calling UseIndexedParameters() will
// instead use an STL vector for faster indexed access.
// This assumes the paramIDs are numbered 0.....inNumberOfParameters-1
// Call this before defining/adding any parameters with SetParameter()
//
void AUElement::UseIndexedParameters(UInt32 inNumberOfParameters)
{
mIndexedParameters.resize(inNumberOfParameters);
mUseIndexedParameters = true;
}
//_____________________________________________________________________________
//
// Helper method.
// returns whether the specified paramID is known to the element
//
bool AUElement::HasParameterID(AudioUnitParameterID paramID) const
{
if (mUseIndexedParameters) {
return paramID < mIndexedParameters.size();
}
return mParameters.find(paramID) != mParameters.end();
}
//_____________________________________________________________________________
//
// caller assumes that this is actually an immediate parameter
//
AudioUnitParameterValue AUElement::GetParameter(AudioUnitParameterID paramID) const
{
if (mUseIndexedParameters) {
ausdk::ThrowExceptionIf(
paramID >= mIndexedParameters.size(), kAudioUnitErr_InvalidParameter);
return mIndexedParameters[paramID].load(std::memory_order_acquire);
}
const auto i = mParameters.find(paramID);
ausdk::ThrowExceptionIf(i == mParameters.end(), kAudioUnitErr_InvalidParameter);
return (*i).second.load(std::memory_order_acquire);
}
//_____________________________________________________________________________
//
void AUElement::SetParameter(
AudioUnitParameterID paramID, AudioUnitParameterValue inValue, bool okWhenInitialized)
{
if (mUseIndexedParameters) {
ausdk::ThrowExceptionIf(
paramID >= mIndexedParameters.size(), kAudioUnitErr_InvalidParameter);
mIndexedParameters[paramID].store(inValue, std::memory_order_release);
} else {
const auto i = mParameters.find(paramID);
if (i == mParameters.end()) {
if (mAudioUnit.IsInitialized() && !okWhenInitialized) {
// The AU should not be creating new parameters once initialized.
// If a client tries to set an undefined parameter, we could throw as follows,
// but this might cause a regression. So it is better to just fail silently.
// Throw(kAudioUnitErr_InvalidParameter);
AUSDK_LogError(
"Warning: %s SetParameter for undefined param ID %u while initialized. "
"Ignoring.",
mAudioUnit.GetLoggingString(), static_cast<unsigned>(paramID));
} else {
// create new entry in map for the paramID (only happens first time)
mParameters[paramID] = ParameterValue{ inValue };
}
} else {
// paramID already exists in map so simply change its value
(*i).second.store(inValue, std::memory_order_release);
}
}
}
//_____________________________________________________________________________
//
void AUElement::SetScheduledEvent(AudioUnitParameterID paramID,
const AudioUnitParameterEvent& inEvent, UInt32 /*inSliceOffsetInBuffer*/,
UInt32 /*inSliceDurationFrames*/, bool okWhenInitialized)
{
if (inEvent.eventType != kParameterEvent_Immediate) {
AUSDK_LogError("Warning: %s was passed a ramped parameter event but does not implement "
"them. Ignoring.",
mAudioUnit.GetLoggingString());
return;
}
SetParameter(paramID, inEvent.eventValues.immediate.value, okWhenInitialized); // NOLINT
}
//_____________________________________________________________________________
//
void AUElement::GetParameterList(AudioUnitParameterID* outList)
{
if (mUseIndexedParameters) {
const auto nparams = static_cast<UInt32>(mIndexedParameters.size());
for (UInt32 i = 0; i < nparams; i++) {
*outList++ = (AudioUnitParameterID)i; // NOLINT
}
} else {
for (const auto& param : mParameters) {
*outList++ = param.first; // NOLINT
}
}
}
//_____________________________________________________________________________
//
void AUElement::SaveState(AudioUnitScope scope, CFMutableDataRef data)
{
AudioUnitParameterInfo paramInfo{};
const CFIndex countOffset = CFDataGetLength(data);
uint32_t paramsWritten = 0;
const auto appendBytes = [data](const void* bytes, CFIndex length) {
CFDataAppendBytes(data, static_cast<const UInt8*>(bytes), length);
};
const auto appendParameter = [&](AudioUnitParameterID paramID, AudioUnitParameterValue value) {
struct {
UInt32 paramID;
UInt32 value; // really a big-endian float
} entry{};
static_assert(sizeof(entry) == (sizeof(entry.paramID) + sizeof(entry.value)));
if (mAudioUnit.GetParameterInfo(scope, paramID, paramInfo) == noErr) {
if ((paramInfo.flags & kAudioUnitParameterFlag_CFNameRelease) != 0u) {
if (paramInfo.cfNameString != nullptr) {
CFRelease(paramInfo.cfNameString);
}
if (paramInfo.unit == kAudioUnitParameterUnit_CustomUnit &&
paramInfo.unitName != nullptr) {
CFRelease(paramInfo.unitName);
}
}
if (((paramInfo.flags & kAudioUnitParameterFlag_OmitFromPresets) != 0u) ||
((paramInfo.flags & kAudioUnitParameterFlag_MeterReadOnly) != 0u)) {
return;
}
}
entry.paramID = CFSwapInt32HostToBig(paramID);
entry.value = CFSwapInt32HostToBig(*reinterpret_cast<UInt32*>(&value)); // NOLINT
appendBytes(&entry, sizeof(entry));
++paramsWritten;
};
constexpr UInt32 placeholderCount = 0;
appendBytes(&placeholderCount, sizeof(placeholderCount));
if (mUseIndexedParameters) {
const auto nparams = static_cast<UInt32>(mIndexedParameters.size());
for (UInt32 i = 0; i < nparams; i++) {
appendParameter(i, mIndexedParameters[i]);
}
} else {
for (const auto& item : mParameters) {
appendParameter(item.first, item.second);
}
}
const auto count_BE = CFSwapInt32HostToBig(paramsWritten);
memcpy(CFDataGetMutableBytePtr(data) + countOffset, // NOLINT ptr math
&count_BE, sizeof(count_BE));
}
//_____________________________________________________________________________
//
const UInt8* AUElement::RestoreState(const UInt8* state)
{
union FloatInt32 {
UInt32 i;
AudioUnitParameterValue f;
};
const UInt8* p = state;
const UInt32 nparams = CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p)); // NOLINT
p += sizeof(UInt32); // NOLINT
for (UInt32 i = 0; i < nparams; ++i) {
struct {
AudioUnitParameterID paramID;
AudioUnitParameterValue value;
} entry{};
static_assert(sizeof(entry) == (sizeof(entry.paramID) + sizeof(entry.value)));
entry.paramID = CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p)); // NOLINT
p += sizeof(UInt32); // NOLINT
FloatInt32 temp{}; // NOLINT
temp.i = CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p)); // NOLINT
entry.value = temp.f; // NOLINT
p += sizeof(AudioUnitParameterValue); // NOLINT
SetParameter(entry.paramID, entry.value);
}
return p;
}
//_____________________________________________________________________________
//
AUIOElement::AUIOElement(AUBase& audioUnit) : AUElement(audioUnit), mWillAllocate(true)
{
mStreamFormat = AudioStreamBasicDescription{ .mSampleRate = AUBase::kAUDefaultSampleRate,
.mFormatID = kAudioFormatLinearPCM,
.mFormatFlags = AudioFormatFlags(kAudioFormatFlagsNativeFloatPacked) |
AudioFormatFlags(kAudioFormatFlagIsNonInterleaved), // NOLINT
.mBytesPerPacket = sizeof(float),
.mFramesPerPacket = 1,
.mBytesPerFrame = sizeof(float),
.mChannelsPerFrame = 2,
.mBitsPerChannel = 32, // NOLINT
.mReserved = 0 };
}
//_____________________________________________________________________________
//
OSStatus AUIOElement::SetStreamFormat(const AudioStreamBasicDescription& format)
{
mStreamFormat = format;
// Clear the previous channel layout if it is inconsistent with the newly set format;
// preserve it if it is acceptable, in case the new format has no layout.
if (ChannelLayout().IsValid() && NumberChannels() != ChannelLayout().NumberChannels()) {
RemoveAudioChannelLayout();
}
return noErr;
}
//_____________________________________________________________________________
// inFramesToAllocate == 0 implies the AudioUnit's max-frames-per-slice will be used
void AUIOElement::AllocateBuffer(UInt32 inFramesToAllocate)
{
if (GetAudioUnit().HasBegunInitializing()) {
UInt32 framesToAllocate =
inFramesToAllocate > 0 ? inFramesToAllocate : GetAudioUnit().GetMaxFramesPerSlice();
mIOBuffer.Allocate(
mStreamFormat, (mWillAllocate && NeedsBufferSpace()) ? framesToAllocate : 0);
}
}
//_____________________________________________________________________________
//
void AUIOElement::DeallocateBuffer() { mIOBuffer.Deallocate(); }
//_____________________________________________________________________________
//
// AudioChannelLayout support
// return an empty vector (ie. NO channel layouts) if the AU doesn't require channel layout
// knowledge
std::vector<AudioChannelLayoutTag> AUIOElement::GetChannelLayoutTags() { return {}; }
// outLayoutPtr WILL be NULL if called to determine layout size
UInt32 AUIOElement::GetAudioChannelLayout(AudioChannelLayout* outLayoutPtr, bool& outWritable)
{
outWritable = true;
UInt32 size = mChannelLayout.IsValid() ? mChannelLayout.Size() : 0;
if (size > 0 && outLayoutPtr != nullptr) {
memcpy(outLayoutPtr, &mChannelLayout.Layout(), size);
}
return size;
}
// the incoming channel map will be at least as big as a basic AudioChannelLayout
// but its contents will determine its actual size
// Subclass should overide if channel map is writable
OSStatus AUIOElement::SetAudioChannelLayout(const AudioChannelLayout& inLayout)
{
if (NumberChannels() != AUChannelLayout::NumberChannels(inLayout)) {
return kAudioUnitErr_InvalidPropertyValue;
}
mChannelLayout = inLayout;
return noErr;
}
// Some units support optional usage of channel maps - typically converter units
// that can do channel remapping between different maps. In that optional case
// the user should be able to remove a channel map if that is possible.
// Typically this is NOT the case (e.g., the 3DMixer even in the stereo case
// needs to know if it is rendering to speakers or headphones)
OSStatus AUIOElement::RemoveAudioChannelLayout()
{
mChannelLayout = {};
return noErr;
}
//_____________________________________________________________________________
//
void AUScope::SetNumberOfElements(UInt32 numElements)
{
if (mDelegate != nullptr) {
return mDelegate->SetNumberOfElements(numElements);
}
if (numElements > mElements.size()) {
mElements.reserve(numElements);
while (numElements > mElements.size()) {
auto elem = mCreator->CreateElement(GetScope(), static_cast<UInt32>(mElements.size()));
mElements.push_back(std::move(elem));
}
} else {
while (numElements < mElements.size()) {
mElements.pop_back();
}
}
}
//_____________________________________________________________________________
//
bool AUScope::HasElementWithName() const
{
for (UInt32 i = 0; i < GetNumberOfElements(); ++i) {
AUElement* const el = GetElement(i);
if ((el != nullptr) && el->HasName()) {
return true;
}
}
return false;
}
//_____________________________________________________________________________
//
void AUScope::AddElementNamesToDict(CFMutableDictionaryRef inNameDict) const
{
if (HasElementWithName()) {
const auto elementDict =
Owned<CFMutableDictionaryRef>::from_create(CFDictionaryCreateMutable(
nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
for (UInt32 i = 0; i < GetNumberOfElements(); ++i) {
AUElement* const el = GetElement(i);
if (el != nullptr && el->HasName()) {
const auto key = Owned<CFStringRef>::from_create(CFStringCreateWithFormat(
nullptr, nullptr, CFSTR("%u"), static_cast<unsigned>(i)));
CFDictionarySetValue(*elementDict, *key, *el->GetName());
}
}
const auto key = Owned<CFStringRef>::from_create(
CFStringCreateWithFormat(nullptr, nullptr, CFSTR("%u"), static_cast<unsigned>(mScope)));
CFDictionarySetValue(inNameDict, *key, *elementDict);
}
}
//_____________________________________________________________________________
//
std::vector<AudioUnitElement> AUScope::RestoreElementNames(CFDictionaryRef inNameDict) const
{
// first we have to see if we have enough elements
std::vector<AudioUnitElement> restoredElements;
const auto maxElNum = GetNumberOfElements();
const auto dictSize =
static_cast<size_t>(std::max(CFDictionaryGetCount(inNameDict), CFIndex(0)));
std::vector<CFStringRef> keys(dictSize);
CFDictionaryGetKeysAndValues(
inNameDict, reinterpret_cast<const void**>(keys.data()), nullptr); // NOLINT
for (size_t i = 0; i < dictSize; i++) {
unsigned int intKey = 0;
std::array<char, 32> string{};
CFStringGetCString(keys[i], string.data(), string.size(), kCFStringEncodingASCII);
const int result = sscanf(string.data(), "%u", &intKey); // NOLINT
// check if sscanf succeeded and element index is less than max elements.
if ((result != 0) && (static_cast<UInt32>(intKey) < maxElNum)) {
auto* const elName =
static_cast<CFStringRef>(CFDictionaryGetValue(inNameDict, keys[i]));
if ((elName != nullptr) && (CFGetTypeID(elName) == CFStringGetTypeID())) {
AUElement* const element = GetElement(intKey);
if (element != nullptr) {
auto* const currentName = element->GetName().get();
if (currentName == nullptr || CFStringCompare(elName, currentName, 0) != kCFCompareEqualTo) {
element->SetName(elName);
restoredElements.push_back(intKey);
}
}
}
}
}
return restoredElements;
}
void AUScope::SaveState(CFMutableDataRef data) const
{
const AudioUnitElement nElems = GetNumberOfElements();
for (AudioUnitElement ielem = 0; ielem < nElems; ++ielem) {
AUElement* const element = GetElement(ielem);
const UInt32 nparams = element->GetNumberOfParameters();
if (nparams > 0) {
struct {
const UInt32 scope;
const UInt32 element;
} hdr{ .scope = CFSwapInt32HostToBig(GetScope()),
.element = CFSwapInt32HostToBig(ielem) };
static_assert(sizeof(hdr) == (sizeof(hdr.scope) + sizeof(hdr.element)));
CFDataAppendBytes(data, reinterpret_cast<const UInt8*>(&hdr), sizeof(hdr)); // NOLINT
element->SaveState(mScope, data);
}
}
}
const UInt8* AUScope::RestoreState(const UInt8* state) const
{
const UInt8* p = state;
const UInt32 elementIdx = CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p)); // NOLINT
p += sizeof(UInt32); // NOLINT
AUElement* const element = GetElement(elementIdx);
if (element == nullptr) {
struct {
AudioUnitParameterID paramID;
AudioUnitParameterValue value;
} entry{};
static_assert(sizeof(entry) == (sizeof(entry.paramID) + sizeof(entry.value)));
const UInt32 nparams = CFSwapInt32BigToHost(*reinterpret_cast<const UInt32*>(p)); // NOLINT
p += sizeof(UInt32); // NOLINT
p += nparams * sizeof(entry); // NOLINT
} else {
p = element->RestoreState(p);
}
return p;
}
} // namespace ausdk
@@ -0,0 +1,435 @@
/*!
@file AudioUnitSDK/AUScopeElement.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_AUScopeElement_h
#define AudioUnitSDK_AUScopeElement_h
// module
#include <AudioUnitSDK/AUBuffer.h>
#include <AudioUnitSDK/AUUtility.h>
#include <AudioUnitSDK/ComponentBase.h>
// OS
#include <AudioToolbox/AudioUnit.h>
// std
#include <algorithm>
#include <atomic>
#include <memory>
#include <vector>
namespace ausdk {
class AUBase;
/// Wrap an atomic in a copy-constructible/assignable object. This allows storing atomic values in a
/// vector (not directly possible since atomics are not copy-constructible/assignable).
template <typename T>
class AtomicValue {
public:
AtomicValue() = default;
explicit AtomicValue(T val) : mValue{ val } {}
~AtomicValue() = default;
AtomicValue(const AtomicValue& other) : mValue{ other.mValue.load() } {}
AtomicValue(AtomicValue&& other) noexcept : mValue{ other.mValue.load() } {}
AtomicValue& operator=(const AtomicValue& other)
{
if (&other != this) {
mValue.store(other.mValue.load());
}
return *this;
}
AtomicValue& operator=(AtomicValue&& other) noexcept
{
mValue.store(other.mValue.load());
return *this;
}
T load(std::memory_order m = std::memory_order_seq_cst) const { return mValue.load(m); }
void store(T v, std::memory_order m = std::memory_order_seq_cst) { mValue.store(v, m); }
operator T() const { return load(); } // NOLINT implicit conversions OK
AtomicValue& operator=(T value)
{
store(value);
return *this;
}
private:
std::atomic<T> mValue{};
};
/// A bare-bones reinvention of boost::flat_map, just enough to hold parameters in sorted vectors.
template <typename Key, typename Value>
class flat_map {
using KVPair = std::pair<Key, Value>;
using Impl = std::vector<std::pair<Key, Value>>;
static bool keyless(const KVPair& item, Key k) { return k > item.first; }
Impl mImpl;
public:
using iterator = typename Impl::iterator;
using const_iterator = typename Impl::const_iterator;
[[nodiscard]] bool empty() const { return mImpl.empty(); }
[[nodiscard]] size_t size() const { return mImpl.size(); }
[[nodiscard]] const_iterator begin() const { return mImpl.begin(); }
[[nodiscard]] const_iterator end() const { return mImpl.end(); }
iterator begin() { return mImpl.begin(); }
iterator end() { return mImpl.end(); }
const_iterator cbegin() { return mImpl.cbegin(); }
const_iterator cend() { return mImpl.cend(); }
[[nodiscard]] const_iterator lower_bound(Key k) const
{
return std::lower_bound(mImpl.begin(), mImpl.end(), k, keyless);
}
iterator lower_bound(Key k) { return std::lower_bound(mImpl.begin(), mImpl.end(), k, keyless); }
[[nodiscard]] const_iterator find(Key k) const
{
auto iter = lower_bound(k);
if (iter != mImpl.end()) {
if ((*iter).first != k) {
iter = mImpl.end();
}
}
return iter;
}
iterator find(Key k)
{
auto iter = lower_bound(k);
if (iter != mImpl.end()) {
if ((*iter).first != k) {
iter = mImpl.end();
}
}
return iter;
}
class ItemProxy {
public:
ItemProxy(flat_map& map, Key k) : mMap{ map }, mKey{ k } {}
operator Value() const // NOLINT implicit conversion is OK
{
const auto iter = mMap.find(mKey);
if (iter == mMap.end()) {
throw std::runtime_error("Invalid map key");
}
return (*iter).second;
}
ItemProxy& operator=(const Value& v)
{
const auto iter = mMap.lower_bound(mKey);
if (iter != mMap.end() && (*iter).first == mKey) {
(*iter).second = v;
} else {
mMap.mImpl.insert(iter, { mKey, v });
}
return *this;
}
private:
flat_map& mMap;
const Key mKey;
};
ItemProxy operator[](Key k) { return ItemProxy{ *this, k }; }
};
// ____________________________________________________________________________
//
class AUIOElement;
/// An organizational unit for parameters, with a name.
class AUElement {
using ParameterValue = AtomicValue<float>;
using ParameterMap = flat_map<AudioUnitParameterID, ParameterValue>;
public:
explicit AUElement(AUBase& audioUnit) : mAudioUnit(audioUnit), mUseIndexedParameters(false) {}
AUSDK_DEPRECATED("Construct with a reference")
explicit AUElement(AUBase* audioUnit) : AUElement(*audioUnit) {}
AUElement(const AUElement&) = delete;
AUElement(AUElement&&) = delete;
AUElement& operator=(const AUElement&) = delete;
AUElement& operator=(AUElement&&) = delete;
virtual ~AUElement() = default;
virtual UInt32 GetNumberOfParameters()
{
return mUseIndexedParameters ? static_cast<UInt32>(mIndexedParameters.size())
: static_cast<UInt32>(mParameters.size());
}
virtual void GetParameterList(AudioUnitParameterID* outList);
[[nodiscard]] bool HasParameterID(AudioUnitParameterID paramID) const;
[[nodiscard]] AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID) const;
// Only set okWhenInitialized to true when you know the outside world cannot access this
// element. Otherwise the parameter map could get corrupted.
void SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value,
bool okWhenInitialized = false);
// Only set okWhenInitialized to true when you know the outside world cannot access this
// element. Otherwise the parameter map could get corrupted. N.B. This only handles
// immediate parameters. Override to implement ramping. Called from
// AUBase::ProcessForScheduledParams.
virtual void SetScheduledEvent(AudioUnitParameterID paramID,
const AudioUnitParameterEvent& inEvent, UInt32 inSliceOffsetInBuffer,
UInt32 inSliceDurationFrames, bool okWhenInitialized = false);
[[nodiscard]] AUBase& GetAudioUnit() const noexcept { return mAudioUnit; }
void SaveState(AudioUnitScope scope, CFMutableDataRef data);
const UInt8* RestoreState(const UInt8* state);
[[nodiscard]] Owned<CFStringRef> GetName() const { return mElementName; }
void SetName(CFStringRef inName) { mElementName = inName; }
[[nodiscard]] bool HasName() const { return *mElementName != nil; }
virtual void UseIndexedParameters(UInt32 inNumberOfParameters);
virtual AUIOElement* AsIOElement() { return nullptr; }
private:
// --
AUBase& mAudioUnit;
ParameterMap mParameters;
bool mUseIndexedParameters;
std::vector<ParameterValue> mIndexedParameters;
Owned<CFStringRef> mElementName;
};
// ____________________________________________________________________________
//
/// A subclass of AUElement which represents an input or output bus, and has an associated
/// audio format and buffers.
class AUIOElement : public AUElement {
public:
explicit AUIOElement(AUBase& audioUnit);
AUIOElement(AUBase& audioUnit, const AudioStreamBasicDescription& format)
: AUIOElement{ audioUnit }
{
mStreamFormat = format;
}
AUSDK_DEPRECATED("Construct with a reference")
explicit AUIOElement(AUBase* audioUnit) : AUIOElement(*audioUnit) {}
[[nodiscard]] const AudioStreamBasicDescription& GetStreamFormat() const noexcept
{
return mStreamFormat;
}
virtual OSStatus SetStreamFormat(const AudioStreamBasicDescription& format);
virtual void AllocateBuffer(UInt32 inFramesToAllocate = 0);
void DeallocateBuffer();
/// Determines (via subclass override) whether the element's buffer list needs to be allocated.
[[nodiscard]] virtual bool NeedsBufferSpace() const = 0;
void SetWillAllocateBuffer(bool inFlag) noexcept { mWillAllocate = inFlag; }
[[nodiscard]] bool WillAllocateBuffer() const noexcept { return mWillAllocate; }
AudioBufferList& PrepareBuffer(UInt32 nFrames)
{
if (mWillAllocate) {
return mIOBuffer.PrepareBuffer(mStreamFormat, nFrames);
}
Throw(kAudioUnitErr_InvalidPropertyValue);
}
AudioBufferList& PrepareNullBuffer(UInt32 nFrames)
{
return mIOBuffer.PrepareNullBuffer(mStreamFormat, nFrames);
}
AudioBufferList& SetBufferList(AudioBufferList& abl) { return mIOBuffer.SetBufferList(abl); }
void SetBuffer(UInt32 index, AudioBuffer& ab) { mIOBuffer.SetBuffer(index, ab); }
void InvalidateBufferList() { mIOBuffer.InvalidateBufferList(); }
[[nodiscard]] AudioBufferList& GetBufferList() const { return mIOBuffer.GetBufferList(); }
[[nodiscard]] float* GetFloat32ChannelData(UInt32 ch)
{
if (IsInterleaved()) {
return static_cast<float*>(mIOBuffer.GetBufferList().mBuffers[0].mData) + ch; // NOLINT
}
return static_cast<float*>(mIOBuffer.GetBufferList().mBuffers[ch].mData); // NOLINT
}
void CopyBufferListTo(AudioBufferList& abl) const { mIOBuffer.CopyBufferListTo(abl); }
void CopyBufferContentsTo(AudioBufferList& abl) const { mIOBuffer.CopyBufferContentsTo(abl); }
[[nodiscard]] bool IsInterleaved() const noexcept { return ASBD::IsInterleaved(mStreamFormat); }
[[nodiscard]] UInt32 NumberChannels() const noexcept { return mStreamFormat.mChannelsPerFrame; }
[[nodiscard]] UInt32 NumberInterleavedChannels() const noexcept
{
return ASBD::NumberInterleavedChannels(mStreamFormat);
}
virtual std::vector<AudioChannelLayoutTag> GetChannelLayoutTags();
[[nodiscard]] const AUChannelLayout& ChannelLayout() const { return mChannelLayout; }
// Old layout methods
virtual OSStatus SetAudioChannelLayout(const AudioChannelLayout& inLayout);
virtual UInt32 GetAudioChannelLayout(AudioChannelLayout* outLayoutPtr, bool& outWritable);
virtual OSStatus RemoveAudioChannelLayout();
/*! @fn AsIOElement*/
AUIOElement* AsIOElement() override { return this; }
protected:
AUBufferList& IOBuffer() noexcept { return mIOBuffer; }
void ForceSetAudioChannelLayout(const AudioChannelLayout& inLayout)
{
mChannelLayout = inLayout;
}
private:
AudioStreamBasicDescription mStreamFormat{};
AUChannelLayout mChannelLayout{};
AUBufferList mIOBuffer; // for input: input proc buffer, only allocated when needed
// for output: output cache, usually allocated early on
bool mWillAllocate{ false };
};
// ____________________________________________________________________________
//
/*!
@class AUScopeDelegate
@brief Provides a way to customize a scope, thereby obtaining virtual scopes.
Can be used to implement scopes with variable numbers of elements.
*/
class AUScopeDelegate {
public:
AUScopeDelegate() = default;
virtual ~AUScopeDelegate() = default;
AUScopeDelegate(const AUScopeDelegate&) = delete;
AUScopeDelegate(AUScopeDelegate&&) = delete;
AUScopeDelegate& operator=(const AUScopeDelegate&) = delete;
AUScopeDelegate& operator=(AUScopeDelegate&&) = delete;
void Initialize(AUBase* creator, AudioUnitScope scope, UInt32 numElements)
{
mCreator = creator;
mScope = scope;
SetNumberOfElements(numElements);
}
virtual void SetNumberOfElements(UInt32 numElements) = 0;
virtual UInt32 GetNumberOfElements() = 0;
virtual AUElement* GetElement(UInt32 elementIndex) = 0;
[[nodiscard]] AUBase* GetCreator() const noexcept { return mCreator; }
[[nodiscard]] AudioUnitScope GetScope() const noexcept { return mScope; }
private:
AUBase* mCreator{ nullptr };
AudioUnitScope mScope{ 0 };
};
// ____________________________________________________________________________
//
/*!
@class AUScope
@brief Organizes one or more elements into an addressable group (e.g. global, input, output).
*/
class AUScope {
public:
AUScope() = default;
~AUScope() = default;
AUScope(const AUScope&) = delete;
AUScope(AUScope&&) = delete;
AUScope& operator=(const AUScope&) = delete;
AUScope& operator=(AUScope&&) = delete;
void Initialize(AUBase* creator, AudioUnitScope scope, UInt32 numElements)
{
mCreator = creator;
mScope = scope;
if (mDelegate != nullptr) {
return mDelegate->Initialize(creator, scope, numElements);
}
SetNumberOfElements(numElements);
}
void SetNumberOfElements(UInt32 numElements);
[[nodiscard]] UInt32 GetNumberOfElements() const
{
if (mDelegate != nullptr) {
return mDelegate->GetNumberOfElements();
}
return static_cast<UInt32>(mElements.size());
}
[[nodiscard]] AUElement* GetElement(UInt32 elementIndex) const
{
if (mDelegate != nullptr) {
return mDelegate->GetElement(elementIndex);
}
return elementIndex < mElements.size() ? mElements[elementIndex].get() : nullptr;
}
[[nodiscard]] AUElement* SafeGetElement(UInt32 elementIndex) const
{
AUElement* const element = GetElement(elementIndex);
ausdk::ThrowExceptionIf(element == nullptr, kAudioUnitErr_InvalidElement);
return element;
}
[[nodiscard]] AUIOElement* GetIOElement(UInt32 elementIndex) const
{
AUElement* const element = GetElement(elementIndex);
AUIOElement* const ioel = element != nullptr ? element->AsIOElement() : nullptr;
ausdk::ThrowExceptionIf(ioel == nullptr, kAudioUnitErr_InvalidElement);
return ioel;
}
[[nodiscard]] bool HasElementWithName() const;
void AddElementNamesToDict(CFMutableDictionaryRef inNameDict) const;
[[nodiscard]] std::vector<AudioUnitElement> RestoreElementNames(
CFDictionaryRef inNameDict) const;
[[nodiscard]] AudioUnitScope GetScope() const noexcept { return mScope; }
void SetDelegate(AUScopeDelegate* inDelegate) noexcept { mDelegate = inDelegate; }
void SaveState(CFMutableDataRef data) const;
const UInt8* RestoreState(const UInt8* state) const;
private:
using ElementVector = std::vector<std::unique_ptr<AUElement>>;
AUBase* mCreator{ nullptr };
AudioUnitScope mScope{ 0 };
ElementVector mElements;
AUScopeDelegate* mDelegate{ nullptr };
};
} // namespace ausdk
#endif // AudioUnitSDK_AUScopeElement_h
@@ -0,0 +1,49 @@
/*!
@file AudioUnitSDK/AUSilentTimeout.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_AUSilentTimeout_h
#define AudioUnitSDK_AUSilentTimeout_h
#include <CoreFoundation/CFBase.h> // for UInt32
#include <algorithm>
namespace ausdk {
/*!
@class AUSilentTimeout
@brief Utility to assist in propagating a silence flag from signal-processing
input to output, factoring in a processing delay.
*/
class AUSilentTimeout {
public:
AUSilentTimeout() = default;
void Process(UInt32 inFramesToProcess, UInt32 inTimeoutLimit, bool& ioSilence)
{
if (ioSilence) {
if (mResetTimer) {
mTimeoutCounter = inTimeoutLimit;
mResetTimer = false;
}
if (mTimeoutCounter > 0) {
mTimeoutCounter -= std::min(inFramesToProcess, mTimeoutCounter);
ioSilence = false;
}
} else {
// signal to reset the next time we receive silence
mResetTimer = true;
}
}
void Reset() { mResetTimer = true; }
private:
UInt32 mTimeoutCounter{ 0 };
bool mResetTimer{ false };
};
} // namespace ausdk
#endif // AudioUnitSDK_AUSilentTimeout_h
@@ -0,0 +1,524 @@
/*!
@file AudioUnitSDK/AUUtility.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_AUUtility_h
#define AudioUnitSDK_AUUtility_h
// OS
#if defined __has_include && __has_include(<CoreAudioTypes/CoreAudioTypes.h>)
#include <CoreAudioTypes/CoreAudioTypes.h>
#else
#include <CoreAudio/CoreAudioTypes.h>
#endif
#include <libkern/OSByteOrder.h>
#include <mach/mach_time.h>
#include <os/log.h>
#include <syslog.h>
// std
#include <bitset>
#include <cstddef>
#include <exception>
#include <mutex>
#include <string>
#include <system_error>
#include <vector>
// -------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark General
#ifdef AUSDK_NO_DEPRECATIONS
#define AUSDK_DEPRECATED(msg)
#else
#define AUSDK_DEPRECATED(msg) [[deprecated(msg)]] // NOLINT macro
#endif
#ifndef AUSDK_LOG_OBJECT
#define AUSDK_LOG_OBJECT OS_LOG_DEFAULT // NOLINT macro
#endif
// -------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark Version
#define AUSDK_VERSION_MAJOR 1
#define AUSDK_VERSION_MINOR 1
#define AUSDK_VERSION_PATCH 0
// -------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark Error-handling macros
#ifdef AUSDK_NO_LOGGING
#define AUSDK_LogError(...) /* NOLINT macro */
#else
#define AUSDK_LogError(...) /* NOLINT macro */ \
if (__builtin_available(macOS 10.11, *)) { \
os_log_error(AUSDK_LOG_OBJECT, __VA_ARGS__); \
} else { \
syslog(LOG_ERR, __VA_ARGS__); \
}
#endif
#define AUSDK_Catch(result) /* NOLINT(cppcoreguidelines-macro-usage) */ \
catch (const ausdk::AUException& exc) { (result) = exc.mError; } \
catch (const std::bad_alloc&) { (result) = kAudio_MemFullError; } \
catch (const OSStatus& catch_err) { (result) = catch_err; } \
catch (const std::system_error& exc) { (result) = exc.code().value(); } \
catch (...) { (result) = -1; }
#define AUSDK_Require(expr, error) /* NOLINT(cppcoreguidelines-macro-usage) */ \
do { \
if (!(expr)) { \
return error; \
} \
} while (0) /* NOLINT */
#define AUSDK_Require_noerr(expr) /* NOLINT(cppcoreguidelines-macro-usage) */ \
do { \
if (const auto status_tmp_macro_detail_ = (expr); status_tmp_macro_detail_ != noErr) { \
return status_tmp_macro_detail_; \
} \
} while (0)
#pragma mark -
// -------------------------------------------------------------------------------------------------
namespace ausdk {
// -------------------------------------------------------------------------------------------------
/// A subclass of std::runtime_error that holds an OSStatus error.
class AUException : public std::runtime_error {
public:
explicit AUException(OSStatus err)
: std::runtime_error{ std::string("OSStatus ") + std::to_string(err) }, mError{ err }
{
}
const OSStatus mError;
};
inline void ThrowExceptionIf(bool condition, OSStatus err)
{
if (condition) {
AUSDK_LogError("throwing %d", static_cast<int>(err));
throw AUException{ err };
}
}
[[noreturn]] inline void Throw(OSStatus err)
{
AUSDK_LogError("throwing %d", static_cast<int>(err));
throw AUException{ err };
}
inline void ThrowQuietIf(bool condition, OSStatus err)
{
if (condition) {
throw AUException{ err };
}
}
[[noreturn]] inline void ThrowQuiet(OSStatus err) { throw AUException{ err }; }
// -------------------------------------------------------------------------------------------------
/// Wrap a std::recursive_mutex in a C++ Mutex (named requirement). Methods are virtual to support
/// customization.
class AUMutex {
public:
AUMutex() = default;
virtual ~AUMutex() = default;
AUMutex(const AUMutex&) = delete;
AUMutex(AUMutex&&) = delete;
AUMutex& operator=(const AUMutex&) = delete;
AUMutex& operator=(AUMutex&&) = delete;
virtual void lock() { mImpl.lock(); }
virtual void unlock() { mImpl.unlock(); }
virtual bool try_lock() { return mImpl.try_lock(); }
private:
std::recursive_mutex mImpl;
};
// -------------------------------------------------------------------------------------------------
/// Implement optional locking at AudioUnit non-realtime entry points (required only for a small
/// number of plug-ins which must synchronize against external entry points).
class AUEntryGuard {
public:
explicit AUEntryGuard(AUMutex* maybeMutex) : mMutex{ maybeMutex }
{
if (mMutex != nullptr) {
mMutex->lock();
}
}
~AUEntryGuard()
{
if (mMutex != nullptr) {
mMutex->unlock();
}
}
AUEntryGuard(const AUEntryGuard&) = delete;
AUEntryGuard(AUEntryGuard&&) = delete;
AUEntryGuard& operator=(const AUEntryGuard&) = delete;
AUEntryGuard& operator=(AUEntryGuard&&) = delete;
private:
AUMutex* mMutex;
};
// -------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark ASBD
/// Utility functions relating to AudioStreamBasicDescription.
namespace ASBD {
constexpr bool IsInterleaved(const AudioStreamBasicDescription& format) noexcept
{
return (format.mFormatFlags & kLinearPCMFormatFlagIsNonInterleaved) == 0u;
}
constexpr UInt32 NumberInterleavedChannels(const AudioStreamBasicDescription& format) noexcept
{
return IsInterleaved(format) ? format.mChannelsPerFrame : 1;
}
constexpr UInt32 NumberChannelStreams(const AudioStreamBasicDescription& format) noexcept
{
return IsInterleaved(format) ? 1 : format.mChannelsPerFrame;
}
constexpr bool IsCommonFloat32(const AudioStreamBasicDescription& format) noexcept
{
return (
format.mFormatID == kAudioFormatLinearPCM && format.mFramesPerPacket == 1 &&
format.mBytesPerPacket == format.mBytesPerFrame
// so far, it's a valid PCM format
&& (format.mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0 &&
(format.mChannelsPerFrame == 1 ||
(format.mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) &&
((format.mFormatFlags & kAudioFormatFlagIsBigEndian) == kAudioFormatFlagsNativeEndian) &&
format.mBitsPerChannel == 32 // NOLINT
&& format.mBytesPerFrame == NumberInterleavedChannels(format) * sizeof(float));
}
constexpr AudioStreamBasicDescription CreateCommonFloat32(
Float64 sampleRate, UInt32 numChannels, bool interleaved = false) noexcept
{
constexpr auto sampleSize = sizeof(Float32);
AudioStreamBasicDescription asbd{};
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kAudioFormatFlagIsFloat |
static_cast<AudioFormatFlags>(kAudioFormatFlagsNativeEndian) |
kAudioFormatFlagIsPacked;
asbd.mBitsPerChannel = 8 * sampleSize; // NOLINT magic number
asbd.mChannelsPerFrame = numChannels;
asbd.mFramesPerPacket = 1;
asbd.mSampleRate = sampleRate;
if (interleaved) {
asbd.mBytesPerPacket = asbd.mBytesPerFrame = numChannels * sampleSize;
} else {
asbd.mBytesPerPacket = asbd.mBytesPerFrame = sampleSize;
asbd.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
}
return asbd;
}
constexpr bool MinimalSafetyCheck(const AudioStreamBasicDescription& x) noexcept
{
// This function returns false if there are sufficiently unreasonable values in any field.
// It is very conservative so even some very unlikely values will pass.
// This is just meant to catch the case where the data from a file is corrupted.
return (x.mSampleRate >= 0.) && (x.mSampleRate < 3e6) // NOLINT SACD sample rate is 2.8224 MHz
&& (x.mBytesPerPacket < 1000000) // NOLINT
&& (x.mFramesPerPacket < 1000000) // NOLINT
&& (x.mBytesPerFrame < 1000000) // NOLINT
&& (x.mChannelsPerFrame > 0) && (x.mChannelsPerFrame <= 1024) // NOLINT
&& (x.mBitsPerChannel <= 1024) // NOLINT
&& (x.mFormatID != 0) &&
!(x.mFormatID == kAudioFormatLinearPCM &&
(x.mFramesPerPacket != 1 || x.mBytesPerPacket != x.mBytesPerFrame));
}
inline bool IsEqual(
const AudioStreamBasicDescription& lhs, const AudioStreamBasicDescription& rhs) noexcept
{
return memcmp(&lhs, &rhs, sizeof(AudioStreamBasicDescription)) == 0;
}
} // namespace ASBD
// -------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark ACL
/// Utility functions relating to AudioChannelLayout.
namespace ACL {
constexpr bool operator==(const AudioChannelLayout& lhs, const AudioChannelLayout& rhs) noexcept
{
if (lhs.mChannelLayoutTag != rhs.mChannelLayoutTag) {
return false;
}
if (lhs.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
return lhs.mChannelBitmap == rhs.mChannelBitmap;
}
if (lhs.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
if (lhs.mNumberChannelDescriptions != rhs.mNumberChannelDescriptions) {
return false;
}
for (auto i = 0u; i < lhs.mNumberChannelDescriptions; ++i) {
const auto& lhdesc = lhs.mChannelDescriptions[i]; // NOLINT array subscript
const auto& rhdesc = rhs.mChannelDescriptions[i]; // NOLINT array subscript
if (lhdesc.mChannelLabel != rhdesc.mChannelLabel) {
return false;
}
if (lhdesc.mChannelLabel == kAudioChannelLabel_UseCoordinates) {
if (memcmp(&lhdesc, &rhdesc, sizeof(AudioChannelDescription)) != 0) {
return false;
}
}
}
}
return true;
}
} // namespace ACL
// -------------------------------------------------------------------------------------------------
/// Utility wrapper for the variably-sized AudioChannelLayout struct.
class AUChannelLayout {
public:
AUChannelLayout() : AUChannelLayout(0, kAudioChannelLayoutTag_UseChannelDescriptions, 0) {}
/// Can construct from a layout tag.
explicit AUChannelLayout(AudioChannelLayoutTag inTag) : AUChannelLayout(0, inTag, 0) {}
AUChannelLayout(uint32_t inNumberChannelDescriptions, AudioChannelLayoutTag inChannelLayoutTag,
AudioChannelBitmap inChannelBitMap)
: mStorage(
kHeaderSize + (inNumberChannelDescriptions * sizeof(AudioChannelDescription)), {})
{
auto* const acl = reinterpret_cast<AudioChannelLayout*>(mStorage.data()); // NOLINT
acl->mChannelLayoutTag = inChannelLayoutTag;
acl->mChannelBitmap = inChannelBitMap;
acl->mNumberChannelDescriptions = inNumberChannelDescriptions;
}
/// Implicit conversion from AudioChannelLayout& is allowed.
AUChannelLayout(const AudioChannelLayout& acl) // NOLINT
: mStorage(kHeaderSize + (acl.mNumberChannelDescriptions * sizeof(AudioChannelDescription)))
{
memcpy(mStorage.data(), &acl, mStorage.size());
}
bool operator==(const AUChannelLayout& other) const noexcept
{
return ACL::operator==(Layout(), other.Layout());
}
bool operator!=(const AUChannelLayout& y) const noexcept { return !(*this == y); }
[[nodiscard]] bool IsValid() const noexcept { return NumberChannels() > 0; }
[[nodiscard]] const AudioChannelLayout& Layout() const noexcept { return *LayoutPtr(); }
[[nodiscard]] const AudioChannelLayout* LayoutPtr() const noexcept
{
return reinterpret_cast<const AudioChannelLayout*>(mStorage.data()); // NOLINT
}
/// After default construction, this method will return
/// kAudioChannelLayoutTag_UseChannelDescriptions with 0 channel descriptions.
[[nodiscard]] AudioChannelLayoutTag Tag() const noexcept { return Layout().mChannelLayoutTag; }
[[nodiscard]] uint32_t NumberChannels() const noexcept { return NumberChannels(*LayoutPtr()); }
[[nodiscard]] uint32_t Size() const noexcept { return static_cast<uint32_t>(mStorage.size()); }
static uint32_t NumberChannels(const AudioChannelLayout& inLayout) noexcept
{
if (inLayout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
return inLayout.mNumberChannelDescriptions;
}
if (inLayout.mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
return static_cast<uint32_t>(
std::bitset<32>(inLayout.mChannelBitmap).count()); // NOLINT magic #
}
return AudioChannelLayoutTag_GetNumberOfChannels(inLayout.mChannelLayoutTag);
}
private:
constexpr static size_t kHeaderSize = offsetof(AudioChannelLayout, mChannelDescriptions[0]);
std::vector<std::byte> mStorage;
};
// -------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark AudioBufferList
/// Utility functions relating to AudioBufferList.
namespace ABL {
// if the return result is odd, there was a null buffer.
inline uint32_t IsBogusAudioBufferList(const AudioBufferList& abl)
{
const AudioBuffer *buf = abl.mBuffers, *const bufEnd = buf + abl.mNumberBuffers;
uint32_t sum =
0; // defeat attempts by the compiler to optimize away the code that touches the buffers
uint32_t anyNull = 0;
for (; buf < bufEnd; ++buf) {
const uint32_t* const p = static_cast<const uint32_t*>(buf->mData);
if (p == nullptr) {
anyNull = 1;
continue;
}
const auto dataSize = buf->mDataByteSize;
if (dataSize >= sizeof(*p)) {
const size_t frameCount = dataSize / sizeof(*p);
sum += p[0];
sum += p[frameCount - 1];
}
}
return anyNull | (sum & ~1u);
}
} // namespace ABL
// -------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark HostTime
/// Utility functions relating to Mach absolute time.
namespace HostTime {
/// Returns the current host time
inline uint64_t Current() { return mach_absolute_time(); }
/// Returns the frequency of the host timebase, in ticks per second.
inline double Frequency()
{
struct mach_timebase_info timeBaseInfo {
}; // NOLINT
mach_timebase_info(&timeBaseInfo);
// the frequency of that clock is: (sToNanosDenominator / sToNanosNumerator) * 10^9
return static_cast<double>(timeBaseInfo.denom) / static_cast<double>(timeBaseInfo.numer) *
1.0e9; // NOLINT
}
} // namespace HostTime
// -------------------------------------------------------------------------------------------------
/// Basic RAII wrapper for CoreFoundation types
template <typename T>
class Owned {
explicit Owned(T obj, bool fromget) noexcept : mImpl{ obj }
{
if (fromget) {
retainRef();
}
}
public:
static Owned from_get(T obj) noexcept { return Owned{ obj, true }; }
static Owned from_create(T obj) noexcept { return Owned{ obj, false }; }
static Owned from_copy(T obj) noexcept { return Owned{ obj, false }; }
Owned() noexcept = default;
~Owned() noexcept { releaseRef(); }
Owned(const Owned& other) noexcept : mImpl{ other.mImpl } { retainRef(); }
Owned(Owned&& other) noexcept : mImpl{ std::exchange(other.mImpl, nullptr) } {}
Owned& operator=(const Owned& other) noexcept
{
if (this != &other) {
releaseRef();
mImpl = other.mImpl;
retainRef();
}
return *this;
}
Owned& operator=(Owned&& other) noexcept
{
std::swap(mImpl, other.mImpl);
return *this;
}
T operator*() const noexcept { return get(); }
T get() const noexcept { return mImpl; }
/// As with `unique_ptr<T>::release()`, releases ownership of the reference to the caller (not
/// to be confused with decrementing the reference count as with `CFRelease()`).
T release() noexcept { return std::exchange(mImpl, nullptr); }
/// This is a from_get operation.
Owned& operator=(T cfobj) noexcept
{
if (mImpl != cfobj) {
releaseRef();
mImpl = cfobj;
retainRef();
}
return *this;
}
private:
void retainRef() noexcept
{
if (mImpl != nullptr) {
CFRetain(mImpl);
}
}
void releaseRef() noexcept
{
if (mImpl != nullptr) {
CFRelease(mImpl);
}
}
T mImpl{ nullptr };
};
// -------------------------------------------------------------------------------------------------
constexpr bool safe_isprint(char in_char) noexcept { return (in_char >= ' ') && (in_char <= '~'); }
inline std::string make_string_from_4cc(uint32_t in_4cc) noexcept
{
#if !TARGET_RT_BIG_ENDIAN
in_4cc = OSSwapInt32(in_4cc); // NOLINT
#endif
char* const string = reinterpret_cast<char*>(&in_4cc); // NOLINT
for (size_t i = 0; i < sizeof(in_4cc); ++i) {
if (!safe_isprint(string[i])) { // NOLINT
string[i] = '.'; // NOLINT
}
}
return std::string{ string, sizeof(in_4cc) };
}
} // namespace ausdk
#endif // AudioUnitSDK_AUUtility_h
@@ -0,0 +1,22 @@
/*!
@file AudioUnitSDK/AudioUnitSDK.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_h
#define AudioUnitSDK_h
#include <AudioUnitSDK/AUBase.h>
#include <AudioUnitSDK/AUBuffer.h>
#include <AudioUnitSDK/AUEffectBase.h>
#include <AudioUnitSDK/AUInputElement.h>
#include <AudioUnitSDK/AUMIDIBase.h>
#include <AudioUnitSDK/AUMIDIEffectBase.h>
#include <AudioUnitSDK/AUOutputElement.h>
#include <AudioUnitSDK/AUPlugInDispatch.h>
#include <AudioUnitSDK/AUScopeElement.h>
#include <AudioUnitSDK/AUSilentTimeout.h>
#include <AudioUnitSDK/AUUtility.h>
#include <AudioUnitSDK/ComponentBase.h>
#include <AudioUnitSDK/MusicDeviceBase.h>
#endif /* AudioUnitSDK_h */
@@ -0,0 +1,98 @@
/*!
@file AudioUnitSDK/ComponentBase.cpp
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
// self
#include <AudioUnitSDK/AUUtility.h>
#include <AudioUnitSDK/ComponentBase.h>
// std
#include <mutex>
namespace ausdk {
static OSStatus CB_GetComponentDescription(
AudioComponentInstance inInstance, AudioComponentDescription* outDesc);
std::recursive_mutex& ComponentBase::InitializationMutex()
{
__attribute__ ((no_destroy)) static std::recursive_mutex global;
return global;
}
ComponentBase::ComponentBase(AudioComponentInstance inInstance) : mComponentInstance(inInstance)
{
(void)GetComponentDescription();
}
void ComponentBase::DoPostConstructor()
{
PostConstructorInternal();
PostConstructor();
}
void ComponentBase::DoPreDestructor()
{
PreDestructor();
PreDestructorInternal();
}
OSStatus ComponentBase::AP_Open(void* self, AudioComponentInstance compInstance)
{
OSStatus result = noErr;
const auto acpi = static_cast<AudioComponentPlugInInstance*>(self);
try {
const std::lock_guard guard{ InitializationMutex() };
auto* const cb =
static_cast<ComponentBase*>((*acpi->mConstruct)(&acpi->mInstanceStorage, compInstance));
cb->DoPostConstructor(); // allows base class to do additional initialization
// once the derived class is fully constructed
result = noErr;
}
AUSDK_Catch(result)
if (result != noErr) {
delete acpi; // NOLINT
}
return result;
}
OSStatus ComponentBase::AP_Close(void* self)
{
OSStatus result = noErr;
try {
const auto acpi = static_cast<AudioComponentPlugInInstance*>(self);
if (const auto acImp =
reinterpret_cast<ComponentBase*>(&acpi->mInstanceStorage)) { // NOLINT
acImp->DoPreDestructor();
(*acpi->mDestruct)(&acpi->mInstanceStorage);
free(self); // NOLINT manual memory management
}
}
AUSDK_Catch(result)
return result;
}
AudioComponentDescription ComponentBase::GetComponentDescription() const
{
AudioComponentDescription desc = {};
if (CB_GetComponentDescription(mComponentInstance, &desc) == noErr) {
return desc;
}
return {};
}
static OSStatus CB_GetComponentDescription(
AudioComponentInstance inInstance, AudioComponentDescription* outDesc)
{
const AudioComponent comp = AudioComponentInstanceGetComponent(inInstance);
if (comp != nullptr) {
return AudioComponentGetDescription(comp, outDesc);
}
return kAudio_ParamError;
}
} // namespace ausdk
@@ -0,0 +1,162 @@
/*!
@file AudioUnitSDK/ComponentBase.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_ComponentBase_h
#define AudioUnitSDK_ComponentBase_h
// module
#include <AudioUnitSDK/AUUtility.h>
// OS
#include <AudioToolbox/AudioUnit.h>
// std
#include <array>
#include <mutex>
#include <new>
namespace ausdk {
/*!
@class ComponentBase
@brief Base class for implementing an `AudioComponentInstance`.
*/
class ComponentBase {
public:
/// Construct given an AudioComponentInstance, typically from APFactory::Constuct.
explicit ComponentBase(AudioComponentInstance inInstance);
virtual ~ComponentBase() = default;
ComponentBase(const ComponentBase&) = delete;
ComponentBase(ComponentBase&&) = delete;
ComponentBase& operator=(const ComponentBase&) = delete;
ComponentBase& operator=(ComponentBase&&) = delete;
/// Called from dispatchers after constructing an instance.
void DoPostConstructor();
/// Called from dispatchers before destroying an instance.
void DoPreDestructor();
/// Obtain the wrapped `AudioComponentInstance` (underlying type of `AudioUnit`, `AudioCodec`,
/// and others).
[[nodiscard]] AudioComponentInstance GetComponentInstance() const noexcept
{
return mComponentInstance;
}
/// Return the instance's `AudioComponentDescription`.
[[nodiscard]] AudioComponentDescription GetComponentDescription() const;
/// Component dispatch method.
static OSStatus AP_Open(void* self, AudioComponentInstance compInstance);
/// Component dispatch method.
static OSStatus AP_Close(void* self);
/// A mutex which is held during `Open`, since some AU's and the Component Manager itself
/// are not thread-safe against globals.
static std::recursive_mutex& InitializationMutex();
protected:
// subclasses are free to to override these methods to add functionality
virtual void PostConstructor() {}
virtual void PreDestructor() {}
// these methods, however, are reserved for override only within this SDK
virtual void PostConstructorInternal() {}
virtual void PreDestructorInternal() {}
private:
AudioComponentInstance mComponentInstance;
};
/*!
@class AudioComponentPlugInInstance
@brief Object which implements an AudioComponentPlugInInterface for the framework, and
which holds the C++ implementation object.
*/
struct AudioComponentPlugInInstance {
// The AudioComponentPlugInInterface must remain first
AudioComponentPlugInInterface mPlugInInterface;
void* (*mConstruct)(void* memory, AudioComponentInstance ci);
void (*mDestruct)(void* memory);
std::array<void*, 2> mPad; // pad to a 16-byte boundary (in either 32 or 64 bit mode)
UInt32
mInstanceStorage; // the ACI implementation object is constructed into this memory
// this member is just a placeholder. it is aligned to a 16byte boundary
};
/*!
@class APFactory
@tparam APMethodLookup A class (e.g. AUBaseLookup) which provides a method selector lookup
function.
@tparam Implementor The class which implements the full plug-in (AudioUnit) interface.
@brief Provides an AudioComponentFactoryFunction and a convenience wrapper for
AudioComponentRegister.
*/
template <class APMethodLookup, class Implementor>
class APFactory {
public:
static void* Construct(void* memory, AudioComponentInstance compInstance)
{
return new (memory) Implementor(compInstance); // NOLINT manual memory management
}
static void Destruct(void* memory) { static_cast<Implementor*>(memory)->~Implementor(); }
// This is the AudioComponentFactoryFunction. It returns an AudioComponentPlugInInstance.
// The actual implementation object is not created until Open().
static AudioComponentPlugInInterface* Factory(const AudioComponentDescription* /* inDesc */)
{
auto* const acpi = // NOLINT owning memory
static_cast<AudioComponentPlugInInstance*>(malloc( // NOLINT manual memory management
offsetof(AudioComponentPlugInInstance, mInstanceStorage) + sizeof(Implementor)));
acpi->mPlugInInterface.Open = ComponentBase::AP_Open;
acpi->mPlugInInterface.Close = ComponentBase::AP_Close;
acpi->mPlugInInterface.Lookup = APMethodLookup::Lookup;
acpi->mPlugInInterface.reserved = nullptr;
acpi->mConstruct = Construct;
acpi->mDestruct = Destruct;
acpi->mPad[0] = nullptr;
acpi->mPad[1] = nullptr;
return &acpi->mPlugInInterface;
}
// This is for runtime registration (not for plug-ins loaded from bundles).
static AudioComponent Register(
UInt32 type, UInt32 subtype, UInt32 manuf, CFStringRef name, UInt32 vers, UInt32 flags = 0)
{
const AudioComponentDescription desc = { type, subtype, manuf, flags, 0 };
return AudioComponentRegister(&desc, name, vers, Factory);
}
};
#ifndef AUSDK_EXPORT
#if __GNUC__
#define AUSDK_EXPORT __attribute__((visibility("default"))) // NOLINT
#else
#warning export?
#endif
#endif
/// Macro to generate the factory function for the specified Audio Component. Factory is an
/// APFactory such as AUBaseFactory. Class is the name of the final ComponentBase class which
/// implements instances of the class.
#define AUSDK_COMPONENT_ENTRY(FactoryType, Class) /* NOLINT macro */ \
AUSDK_EXPORT \
extern "C" void* Class##Factory(const AudioComponentDescription* inDesc); \
extern "C" void* Class##Factory(const AudioComponentDescription* inDesc) \
{ \
return FactoryType<Class>::Factory(inDesc); /* NOLINT parens */ \
}
} // namespace ausdk
#endif // AudioUnitSDK_ComponentBase_h
@@ -0,0 +1,7 @@
AUScopeElement.cpp - The method AUScope::RestoreElementNames was changed to only call AUElement::SetName if the name actually changed (instead of always). This is a workaround for a Ableton Live 11 bug which crashes on duplicating AUs with more than 16 output busses.
AUBase.h - The line that reads
CFStringGetCString(inName, std::data(ioInfo.name), std::size(ioInfo.name), ...
previously read
CFStringGetCString(inName, &ioInfo.name[0], offsetof(AudioUnitParameterInfo, clumpID), ...
This change is necessary because AudioUnitParameterInfo includes another data member between the `name` and `clumpID` members.
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,117 @@
/*!
@file AudioUnitSDK/MusicDeviceBase.cpp
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#include <AudioUnitSDK/MusicDeviceBase.h>
namespace ausdk {
MusicDeviceBase::MusicDeviceBase(
AudioComponentInstance inInstance, UInt32 numInputs, UInt32 numOutputs, UInt32 numGroups)
: AUBase(inInstance, numInputs, numOutputs, numGroups), AUMIDIBase(*static_cast<AUBase*>(this))
{
}
OSStatus MusicDeviceBase::GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable)
{
OSStatus result = noErr;
switch (inID) { // NOLINT if/else
case kMusicDeviceProperty_InstrumentCount:
if (inScope != kAudioUnitScope_Global) {
return kAudioUnitErr_InvalidScope;
}
outDataSize = sizeof(UInt32);
outWritable = false;
result = noErr;
break;
default:
result = AUBase::GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable);
if (result == kAudioUnitErr_InvalidProperty) {
result = AUMIDIBase::DelegateGetPropertyInfo(
inID, inScope, inElement, outDataSize, outWritable);
}
break;
}
return result;
}
OSStatus MusicDeviceBase::GetProperty(
AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData)
{
OSStatus result = noErr;
switch (inID) { // NOLINT if/else
case kMusicDeviceProperty_InstrumentCount:
if (inScope != kAudioUnitScope_Global) {
return kAudioUnitErr_InvalidScope;
}
return GetInstrumentCount(*static_cast<UInt32*>(outData));
default:
result = AUBase::GetProperty(inID, inScope, inElement, outData);
if (result == kAudioUnitErr_InvalidProperty) {
result = AUMIDIBase::DelegateGetProperty(inID, inScope, inElement, outData);
}
}
return result;
}
OSStatus MusicDeviceBase::SetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, const void* inData, UInt32 inDataSize)
{
OSStatus result = AUBase::SetProperty(inID, inScope, inElement, inData, inDataSize);
if (result == kAudioUnitErr_InvalidProperty) {
result = AUMIDIBase::DelegateSetProperty(inID, inScope, inElement, inData, inDataSize);
}
return result;
}
// For a MusicDevice that doesn't support separate instruments (ie. is mono-timbral)
// then this call should return an instrument count of zero and noErr
OSStatus MusicDeviceBase::GetInstrumentCount(UInt32& outInstCount) const
{
outInstCount = 0;
return noErr;
}
OSStatus MusicDeviceBase::HandleNoteOn(
UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame)
{
const MusicDeviceNoteParams params{ .argCount = 2,
.mPitch = static_cast<Float32>(inNoteNumber),
.mVelocity = static_cast<Float32>(inVelocity) };
return StartNote(kMusicNoteEvent_UseGroupInstrument, inChannel, nullptr, inStartFrame, params);
}
OSStatus MusicDeviceBase::HandleNoteOff(
UInt8 inChannel, UInt8 inNoteNumber, UInt8 /*inVelocity*/, UInt32 inStartFrame)
{
return StopNote(inChannel, inNoteNumber, inStartFrame);
}
OSStatus MusicDeviceBase::HandleStartNoteMessage(MusicDeviceInstrumentID inInstrument,
MusicDeviceGroupID inGroupID, NoteInstanceID* outNoteInstanceID, UInt32 inOffsetSampleFrame,
const MusicDeviceNoteParams* inParams)
{
if (inParams == nullptr || outNoteInstanceID == nullptr) {
return kAudio_ParamError;
}
if (!IsInitialized()) {
return kAudioUnitErr_Uninitialized;
}
return StartNote(inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, *inParams);
}
} // namespace ausdk
@@ -0,0 +1,64 @@
/*!
@file AudioUnitSDK/MusicDeviceBase.h
@copyright © 2000-2021 Apple Inc. All rights reserved.
*/
#ifndef AudioUnitSDK_MusicDeviceBase_h
#define AudioUnitSDK_MusicDeviceBase_h
#include <AudioUnitSDK/AUMIDIBase.h>
namespace ausdk {
// ________________________________________________________________________
// MusicDeviceBase
//
/*!
@class MusicDeviceBase
@brief Deriving from AUBase and AUMIDIBase, an abstract base class for Music Device
subclasses.
*/
class MusicDeviceBase : public AUBase, public AUMIDIBase {
public:
MusicDeviceBase(AudioComponentInstance inInstance, UInt32 numInputs, UInt32 numOutputs,
UInt32 numGroups = 0);
OSStatus MIDIEvent(
UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame) override
{
return AUMIDIBase::MIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame);
}
OSStatus SysEx(const UInt8* inData, UInt32 inLength) override
{
return AUMIDIBase::SysEx(inData, inLength);
}
#if AUSDK_MIDI2_AVAILABLE
OSStatus MIDIEventList(
UInt32 inOffsetSampleFrame, const struct MIDIEventList* eventList) override
{
return AUMIDIBase::MIDIEventList(inOffsetSampleFrame, eventList);
}
#endif
OSStatus GetPropertyInfo(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable) override;
OSStatus GetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, void* outData) override;
OSStatus SetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement, const void* inData, UInt32 inDataSize) override;
OSStatus HandleNoteOn(
UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame) override;
OSStatus HandleNoteOff(
UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame) override;
virtual OSStatus GetInstrumentCount(UInt32& outInstCount) const;
private:
OSStatus HandleStartNoteMessage(MusicDeviceInstrumentID inInstrument,
MusicDeviceGroupID inGroupID, NoteInstanceID* outNoteInstanceID, UInt32 inOffsetSampleFrame,
const MusicDeviceNoteParams* inParams);
};
} // namespace ausdk
#endif // AudioUnitSDK_MusicDeviceBase_h
@@ -0,0 +1,172 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <vector>
#ifdef _WIN32
#undef UNICODE
#undef _UNICODE
#define UNICODE 1
#define _UNICODE 1
#include <windows.h>
#include <tchar.h>
HMODULE dlopen (const TCHAR* filename, int) { return LoadLibrary (filename); }
FARPROC dlsym (HMODULE handle, const char* name) { return GetProcAddress (handle, name); }
static void printError()
{
constexpr DWORD numElements = 256;
TCHAR messageBuffer[numElements]{};
FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
GetLastError(),
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
messageBuffer,
numElements - 1,
nullptr);
_tprintf (_T ("%s"), messageBuffer);
}
enum { RTLD_LAZY = 0 };
class ArgList
{
public:
ArgList (int, const char**) {}
ArgList (const ArgList&) = delete;
ArgList (ArgList&&) = delete;
ArgList& operator= (const ArgList&) = delete;
ArgList& operator= (ArgList&&) = delete;
~ArgList() { LocalFree (argv); }
LPWSTR get (int i) const { return argv[i]; }
int size() const { return argc; }
private:
int argc = 0;
LPWSTR* argv = CommandLineToArgvW (GetCommandLineW(), &argc);
};
static std::vector<char> toUTF8 (const TCHAR* str)
{
const auto numBytes = WideCharToMultiByte (CP_UTF8, 0, str, -1, nullptr, 0, nullptr, nullptr);
std::vector<char> result (numBytes);
WideCharToMultiByte (CP_UTF8, 0, str, -1, result.data(), static_cast<int> (result.size()), nullptr, nullptr);
return result;
}
#else
#include <dlfcn.h>
static void printError() { printf ("%s\n", dlerror()); }
class ArgList
{
public:
ArgList (int argcIn, const char** argvIn) : argc (argcIn), argv (argvIn) {}
ArgList (const ArgList&) = delete;
ArgList (ArgList&&) = delete;
ArgList& operator= (const ArgList&) = delete;
ArgList& operator= (ArgList&&) = delete;
~ArgList() = default;
const char* get (int i) const { return argv[i]; }
int size() const { return argc; }
private:
int argc = 0;
const char** argv = nullptr;
};
static std::vector<char> toUTF8 (const char* str) { return std::vector<char> (str, str + std::strlen (str) + 1); }
#endif
// Replicating part of the LV2 header here so that we don't have to set up any
// custom include paths for this file.
// Normally this would be a bad idea, but the LV2 API has to keep these definitions
// in order to remain backwards-compatible.
extern "C"
{
typedef struct LV2_Descriptor
{
const void* a;
const void* b;
const void* c;
const void* d;
const void* e;
const void* f;
const void* g;
const void* (*extension_data)(const char* uri);
} LV2_Descriptor;
}
int main (int argc, const char** argv)
{
const ArgList argList { argc, argv };
if (argList.size() != 2)
return 1;
const auto* libraryPath = argList.get (1);
struct RecallFeature
{
int (*doRecall) (const char*);
};
if (auto* handle = dlopen (libraryPath, RTLD_LAZY))
{
if (auto* getDescriptor = reinterpret_cast<const LV2_Descriptor* (*) (uint32_t)> (dlsym (handle, "lv2_descriptor")))
{
if (auto* descriptor = getDescriptor (0))
{
if (auto* extensionData = descriptor->extension_data)
{
if (auto* recallFeature = reinterpret_cast<const RecallFeature*> (extensionData ("https://lv2-extensions.juce.com/turtle_recall")))
{
if (auto* doRecall = recallFeature->doRecall)
{
const auto converted = toUTF8 (libraryPath);
return doRecall (converted.data());
}
}
}
}
}
}
else
{
printError();
}
return 1;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,194 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
#define UNITY_AUDIO_PLUGIN_API_VERSION 0x010401
#if JUCE_WINDOWS
#define UNITY_INTERFACE_API __stdcall
#define UNITY_INTERFACE_EXPORT __declspec (dllexport)
#else
#define UNITY_INTERFACE_API
#define UNITY_INTERFACE_EXPORT __attribute__ ((visibility ("default")))
#endif
//==============================================================================
struct UnityAudioEffectState;
typedef int (UNITY_INTERFACE_API * createCallback) (UnityAudioEffectState* state);
typedef int (UNITY_INTERFACE_API * releaseCallback) (UnityAudioEffectState* state);
typedef int (UNITY_INTERFACE_API * resetCallback) (UnityAudioEffectState* state);
typedef int (UNITY_INTERFACE_API * processCallback) (UnityAudioEffectState* state, float* inBuffer, float* outBuffer, unsigned int bufferSize,
int numInChannels, int numOutChannels);
typedef int (UNITY_INTERFACE_API * setPositionCallback) (UnityAudioEffectState* state, unsigned int pos);
typedef int (UNITY_INTERFACE_API * setFloatParameterCallback) (UnityAudioEffectState* state, int index, float value);
typedef int (UNITY_INTERFACE_API * getFloatParameterCallback) (UnityAudioEffectState* state, int index, float* value, char* valuestr);
typedef int (UNITY_INTERFACE_API * getFloatBufferCallback) (UnityAudioEffectState* state, const char* name, float* buffer, int numsamples);
typedef int (UNITY_INTERFACE_API * distanceAttenuationCallback) (UnityAudioEffectState* state, float distanceIn, float attenuationIn, float* attenuationOut);
typedef void (UNITY_INTERFACE_API * renderCallback) (int eventId);
//==============================================================================
enum UnityAudioEffectDefinitionFlags
{
isSideChainTarget = 1,
isSpatializer = 2,
isAmbisonicDecoder = 4,
appliesDistanceAttenuation = 8
};
enum UnityAudioEffectStateFlags
{
stateIsPlaying = 1,
stateIsPaused = 2,
stateIsMuted = 8,
statIsSideChainTarget = 16
};
enum UnityEventModifiers
{
shift = 1,
control = 2,
alt = 4,
command = 8,
numeric = 16,
capsLock = 32,
functionKey = 64
};
//==============================================================================
#ifndef DOXYGEN
struct UnityAudioSpatializerData
{
float listenerMatrix[16];
float sourceMatrix[16];
float spatialBlend;
float reverbZoneMix;
float spread;
float stereoPan;
distanceAttenuationCallback attenuationCallback;
float minDistance;
float maxDistance;
};
struct UnityAudioAmbisonicData
{
float listenerMatrix[16];
float sourceMatrix[16];
float spatialBlend;
float reverbZoneMix;
float spread;
float stereoPan;
distanceAttenuationCallback attenuationCallback;
int ambisonicOutChannels;
float volume;
};
struct UnityAudioEffectState
{
juce::uint32 structSize;
juce::uint32 sampleRate;
juce::uint64 dspCurrentTick;
juce::uint64 dspPreviousTick;
float* sidechainBuffer;
void* effectData;
juce::uint32 flags;
void* internal;
UnityAudioSpatializerData* spatializerData;
juce::uint32 dspBufferSize;
juce::uint32 hostAPIVersion;
UnityAudioAmbisonicData* ambisonicData;
template <typename T>
inline T* getEffectData() const
{
jassert (effectData != nullptr);
jassert (internal != nullptr);
return (T*) effectData;
}
};
struct UnityAudioParameterDefinition
{
char name[16];
char unit[16];
const char* description;
float min;
float max;
float defaultVal;
float displayScale;
float displayExponent;
};
struct UnityAudioEffectDefinition
{
juce::uint32 structSize;
juce::uint32 parameterStructSize;
juce::uint32 apiVersion;
juce::uint32 pluginVersion;
juce::uint32 channels;
juce::uint32 numParameters;
juce::uint64 flags;
char name[32];
createCallback create;
releaseCallback release;
resetCallback reset;
processCallback process;
setPositionCallback setPosition;
UnityAudioParameterDefinition* parameterDefintions;
setFloatParameterCallback setFloatParameter;
getFloatParameterCallback getFloatParameter;
getFloatBufferCallback getFloatBuffer;
};
#endif
//==============================================================================
// Unity callback
extern "C" UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API UnityGetAudioEffectDefinitions (UnityAudioEffectDefinition*** definitionsPtr);
// GUI script callbacks
extern "C" UNITY_INTERFACE_EXPORT renderCallback UNITY_INTERFACE_API getRenderCallback();
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityInitialiseTexture (int id, void* textureHandle, int w, int h);
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDown (int id, float x, float y, UnityEventModifiers mods, int button);
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDrag (int id, float x, float y, UnityEventModifiers mods, int button);
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseUp (int id, float x, float y, UnityEventModifiers mods);
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityKeyEvent (int id, int code, UnityEventModifiers mods, const char* name);
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unitySetScreenBounds (int id, float x, float y, float w, float h);
@@ -0,0 +1,73 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
// This suppresses a warning in juce_TargetPlatform.h
#ifndef JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED
#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1
#endif
#include <juce_core/system/juce_CompilerWarnings.h>
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wc++98-compat-extra-semi",
"-Wdeprecated-declarations",
"-Wexpansion-to-defined",
"-Wfloat-equal",
"-Wformat",
"-Wmissing-prototypes",
"-Wpragma-pack",
"-Wredundant-decls",
"-Wshadow",
"-Wshadow-field",
"-Wshorten-64-to-32",
"-Wsign-conversion",
"-Wzero-as-null-pointer-constant")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6387 6031)
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#if JUCE_MAC
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_mac.mm>
#elif JUCE_WINDOWS
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_win32.cpp>
#elif JUCE_LINUX
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_linux.cpp>
#endif
#include <juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/coreiids.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/pluginterfaces/base/funknown.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/samples/vst-utilities/moduleinfotool/source/main.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/memorystream.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/common/readfile.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp>
#include <juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstinitiids.cpp>
JUCE_END_IGNORE_WARNINGS_MSVC
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
@@ -0,0 +1,26 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include "juce_VST3ManifestHelper.cpp"
@@ -0,0 +1,77 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
// The following checks should cause a compile error if you've forgotten to
// define all your plugin settings properly..
#if ! (JucePlugin_Build_VST || JucePlugin_Build_VST3 \
|| JucePlugin_Build_AU || JucePlugin_Build_AUv3 \
|| JucePlugin_Build_AAX || JucePlugin_Build_Standalone \
|| JucePlugin_Build_LV2 || JucePlugin_Build_Unity)
#error "You need to enable at least one plugin format!"
#endif
#ifdef JUCE_CHECKSETTINGMACROS_H
#error "This header should never be included twice! Otherwise something is wrong."
#endif
#define JUCE_CHECKSETTINGMACROS_H
#ifndef JucePlugin_IsSynth
#error "You need to define the JucePlugin_IsSynth value!"
#endif
#ifndef JucePlugin_ManufacturerCode
#error "You need to define the JucePlugin_ManufacturerCode value!"
#endif
#ifndef JucePlugin_PluginCode
#error "You need to define the JucePlugin_PluginCode value!"
#endif
#ifndef JucePlugin_ProducesMidiOutput
#error "You need to define the JucePlugin_ProducesMidiOutput value!"
#endif
#ifndef JucePlugin_WantsMidiInput
#error "You need to define the JucePlugin_WantsMidiInput value!"
#endif
#ifdef JucePlugin_Latency
#error "JucePlugin_Latency is now deprecated - instead, call the AudioProcessor::setLatencySamples() method if your plugin has a non-zero delay"
#endif
#ifndef JucePlugin_EditorRequiresKeyboardFocus
#error "You need to define the JucePlugin_EditorRequiresKeyboardFocus value!"
#endif
//==============================================================================
#if JucePlugin_Build_AAX && ! defined (JucePlugin_AAXIdentifier)
#error "You need to define the JucePlugin_AAXIdentifier value!"
#endif
#if defined (__ppc__)
#undef JucePlugin_Build_AAX
#define JucePlugin_Build_AAX 0
#endif
@@ -0,0 +1,48 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
namespace juce
{
inline std::unique_ptr<AudioProcessor> createPluginFilterOfType (AudioProcessor::WrapperType type)
{
PluginHostType::jucePlugInClientCurrentWrapperType = type;
AudioProcessor::setTypeOfNextNewPlugin (type);
auto pluginInstance = rawToUniquePtr (::createPluginFilter());
AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::wrapperType_Undefined);
// your createPluginFilter() method must return an object!
jassert (pluginInstance != nullptr && pluginInstance->wrapperType == type);
#if JucePlugin_Enable_ARA
jassert (dynamic_cast<juce::AudioProcessorARAExtension*> (pluginInstance.get()) != nullptr);
#endif
return pluginInstance;
}
} // namespace juce
@@ -0,0 +1,34 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
#include <juce_audio_plugin_client/juce_audio_plugin_client.h>
#define Component juce::Component
#if JUCE_MAC
#define Point juce::Point
#endif
@@ -0,0 +1,57 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#if JUCE_WINDOWS
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x500
#undef STRICT
#define STRICT 1
#include <windows.h>
#include <float.h>
#if JUCE_MSVC
#pragma warning (disable : 4312 4355)
#endif
#ifdef __INTEL_COMPILER
#pragma warning (disable : 1899)
#endif
#elif JUCE_LINUX || JUCE_BSD
#include <float.h>
#include <sys/time.h>
#include <arpa/inet.h>
#elif JUCE_MAC || JUCE_IOS
#ifdef __OBJC__
#if JUCE_MAC
#include <Cocoa/Cocoa.h>
#elif JUCE_IOS
#include <UIKit/UIKit.h>
#else
#error
#endif
#endif
#include <objc/runtime.h>
#include <objc/objc.h>
#include <objc/message.h>
#endif
@@ -0,0 +1,115 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#if JUCE_LINUX || JUCE_BSD
namespace juce::detail
{
// Implemented in juce_Messaging_linux.cpp
bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages);
class MessageThread : public Thread
{
public:
MessageThread() : Thread ("JUCE Plugin Message Thread")
{
start();
}
~MessageThread() override
{
MessageManager::getInstance()->stopDispatchLoop();
stop();
}
void start()
{
startThread (Priority::high);
// Wait for setCurrentThreadAsMessageThread() and getInstance to be executed
// before leaving this method
threadInitialised.wait (10000);
}
void stop()
{
signalThreadShouldExit();
stopThread (-1);
}
bool isRunning() const noexcept { return isThreadRunning(); }
void run() override
{
MessageManager::getInstance()->setCurrentThreadAsMessageThread();
XWindowSystem::getInstance();
threadInitialised.signal();
while (! threadShouldExit())
{
if (! dispatchNextMessageOnSystemQueue (true))
Thread::sleep (1);
}
}
private:
WaitableEvent threadInitialised;
JUCE_DECLARE_NON_MOVEABLE (MessageThread)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageThread)
};
//==============================================================================
class HostDrivenEventLoop
{
public:
HostDrivenEventLoop()
{
messageThread->stop();
MessageManager::getInstance()->setCurrentThreadAsMessageThread();
}
void processPendingEvents()
{
MessageManager::getInstance()->setCurrentThreadAsMessageThread();
for (;;)
if (! dispatchNextMessageOnSystemQueue (true))
return;
}
~HostDrivenEventLoop()
{
messageThread->start();
}
private:
SharedResourcePointer<MessageThread> messageThread;
};
} // namespace juce::detail
#endif
@@ -0,0 +1,167 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
#include <juce_audio_plugin_client/detail/juce_IncludeModuleHeaders.h>
namespace juce::detail
{
struct PluginUtilities
{
PluginUtilities() = delete;
static int getDesktopFlags (const AudioProcessorEditor& editor)
{
return editor.wantsLayerBackedView()
? 0
: ComponentPeer::windowRequiresSynchronousCoreGraphicsRendering;
}
static int getDesktopFlags (const AudioProcessorEditor* editor)
{
return editor != nullptr ? getDesktopFlags (*editor) : 0;
}
static void addToDesktop (AudioProcessorEditor& editor, void* parent)
{
editor.addToDesktop (getDesktopFlags (editor), parent);
}
static const PluginHostType& getHostType()
{
static PluginHostType hostType;
return hostType;
}
#ifndef JUCE_VST3_CAN_REPLACE_VST2
#define JUCE_VST3_CAN_REPLACE_VST2 1
#endif
// NB: Nasty old-fashioned code in here because it's copied from the Steinberg example code.
static void getUUIDForVST2ID (bool forControllerUID, uint8 uuid[16])
{
#if JUCE_WINDOWS && ! JUCE_MINGW
const auto juce_sprintf = [] (auto&& head, auto&&... tail) { sprintf_s (head, (size_t) numElementsInArray (head), tail...); };
const auto juce_strcpy = [] (auto&& head, auto&&... tail) { strcpy_s (head, (size_t) numElementsInArray (head), tail...); };
const auto juce_strcat = [] (auto&& head, auto&&... tail) { strcat_s (head, (size_t) numElementsInArray (head), tail...); };
const auto juce_sscanf = [] (auto&&... args) { sscanf_s (args...); };
#else
const auto juce_sprintf = [] (auto&& head, auto&&... tail) { snprintf (head, (size_t) numElementsInArray (head), tail...); };
const auto juce_strcpy = [] (auto&&... args) { strcpy (args...); };
const auto juce_strcat = [] (auto&&... args) { strcat (args...); };
const auto juce_sscanf = [] (auto&&... args) { sscanf (args...); };
#endif
char uidString[33];
const int vstfxid = (('V' << 16) | ('S' << 8) | (forControllerUID ? 'E' : 'T'));
char vstfxidStr[7] = { 0 };
juce_sprintf (vstfxidStr, "%06X", vstfxid);
juce_strcpy (uidString, vstfxidStr);
char uidStr[9] = { 0 };
juce_sprintf (uidStr, "%08X", JucePlugin_VSTUniqueID);
juce_strcat (uidString, uidStr);
char nameidStr[3] = { 0 };
const size_t len = strlen (JucePlugin_Name);
for (size_t i = 0; i <= 8; ++i)
{
juce::uint8 c = i < len ? static_cast<juce::uint8> (JucePlugin_Name[i]) : 0;
if (c >= 'A' && c <= 'Z')
c += 'a' - 'A';
juce_sprintf (nameidStr, "%02X", c);
juce_strcat (uidString, nameidStr);
}
unsigned long p0;
unsigned int p1, p2;
unsigned int p3[8];
juce_sscanf (uidString, "%08lX%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X",
&p0, &p1, &p2, &p3[0], &p3[1], &p3[2], &p3[3], &p3[4], &p3[5], &p3[6], &p3[7]);
union q0_u {
uint32 word;
uint8 bytes[4];
} q0;
union q1_u {
uint16 half;
uint8 bytes[2];
} q1, q2;
q0.word = static_cast<uint32> (p0);
q1.half = static_cast<uint16> (p1);
q2.half = static_cast<uint16> (p2);
// VST3 doesn't use COM compatible UUIDs on non windows platforms
#if ! JUCE_WINDOWS
q0.word = ByteOrder::swap (q0.word);
q1.half = ByteOrder::swap (q1.half);
q2.half = ByteOrder::swap (q2.half);
#endif
for (int i = 0; i < 4; ++i)
uuid[i+0] = q0.bytes[i];
for (int i = 0; i < 2; ++i)
uuid[i+4] = q1.bytes[i];
for (int i = 0; i < 2; ++i)
uuid[i+6] = q2.bytes[i];
for (int i = 0; i < 8; ++i)
uuid[i+8] = static_cast<uint8> (p3[i]);
}
#if JucePlugin_Build_VST
static bool handleManufacturerSpecificVST2Opcode ([[maybe_unused]] int32 index,
[[maybe_unused]] pointer_sized_int value,
[[maybe_unused]] void* ptr,
float)
{
#if JUCE_VST3_CAN_REPLACE_VST2
if ((index == (int32) ByteOrder::bigEndianInt ("stCA") || index == (int32) ByteOrder::bigEndianInt ("stCa"))
&& value == (int32) ByteOrder::bigEndianInt ("FUID") && ptr != nullptr)
{
uint8 fuid[16];
getUUIDForVST2ID (false, fuid);
::memcpy (ptr, fuid, 16);
return true;
}
#endif
return false;
}
#endif
};
} // namespace juce::detail
@@ -0,0 +1,98 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
#if JUCE_MAC
#include <juce_audio_plugin_client/detail/juce_IncludeModuleHeaders.h>
namespace juce::detail
{
struct VSTWindowUtilities
{
VSTWindowUtilities() = delete;
static void* attachComponentToWindowRefVST (Component* comp,
int desktopFlags,
void* parentWindowOrView)
{
JUCE_AUTORELEASEPOOL
{
NSView* parentView = [(NSView*) parentWindowOrView retain];
const auto defaultFlags = JucePlugin_EditorRequiresKeyboardFocus
? 0
: ComponentPeer::windowIgnoresKeyPresses;
comp->addToDesktop (desktopFlags | defaultFlags, parentView);
// (this workaround is because Wavelab provides a zero-size parent view..)
if (approximatelyEqual ([parentView frame].size.height, 0.0))
[((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint];
comp->setVisible (true);
comp->toFront (false);
[[parentView window] setAcceptsMouseMovedEvents: YES];
return parentView;
}
}
static void detachComponentFromWindowRefVST (Component* comp,
void* window)
{
JUCE_AUTORELEASEPOOL
{
comp->removeFromDesktop();
[(id) window release];
}
}
static void setNativeHostWindowSizeVST (void* window,
Component* component,
int newWidth,
int newHeight)
{
JUCE_AUTORELEASEPOOL
{
if (NSView* hostView = (NSView*) window)
{
const int dx = newWidth - component->getWidth();
const int dy = newHeight - component->getHeight();
NSRect r = [hostView frame];
r.size.width += dx;
r.size.height += dy;
r.origin.y -= dy;
[hostView setFrame: r];
}
}
}
};
} // namespace juce::detail
#endif
@@ -0,0 +1,130 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this module, and is read by
the Projucer to automatically generate project code that uses it.
For details about the syntax and how to create or use a module, see the
JUCE Module Format.md file.
BEGIN_JUCE_MODULE_DECLARATION
ID: juce_audio_plugin_client
vendor: juce
version: 7.0.12
name: JUCE audio plugin wrapper classes
description: Classes for building VST, VST3, AU, AUv3, LV2 and AAX plugins.
website: http://www.juce.com/juce
license: GPL/Commercial
minimumCppStandard: 17
dependencies: juce_audio_processors
END_JUCE_MODULE_DECLARATION
*******************************************************************************/
#pragma once
#include <juce_gui_basics/juce_gui_basics.h>
#include <juce_audio_basics/juce_audio_basics.h>
#include <juce_audio_processors/juce_audio_processors.h>
/** Config: JUCE_VST3_CAN_REPLACE_VST2
Enable this if you want your VST3 plug-in to load and save VST2 compatible
state. This allows hosts to replace VST2 plug-ins with VST3 plug-ins. If
you change this option then your VST3 plug-in will be incompatible with
previous versions.
*/
#ifndef JUCE_VST3_CAN_REPLACE_VST2
#define JUCE_VST3_CAN_REPLACE_VST2 1
#endif
/** Config: JUCE_FORCE_USE_LEGACY_PARAM_IDS
Enable this if you want to force JUCE to use a continuous parameter
index to identify a parameter in a DAW (this was the default in old
versions of JUCE). This is index is usually used by the DAW to save
automation data and enabling this may mess up user's DAW projects.
*/
#ifndef JUCE_FORCE_USE_LEGACY_PARAM_IDS
#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 0
#endif
/** Config: JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
Enable this if you want to force JUCE to use a legacy scheme for
identifying plug-in parameters as either continuous or discrete.
DAW projects with automation data written by an AudioUnit, VST3 or
AAX plug-in built with JUCE version 5.1.1 or earlier may load
incorrectly when opened by an AudioUnit, VST3 or AAX plug-in built
with JUCE version 5.2.0 and later.
*/
#ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 0
#endif
/** Config: JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS
Enable this if you want JUCE to use parameter ids which are compatible
with Studio One, as Studio One ignores any parameter ids which are negative.
Enabling this option will make JUCE generate only positive parameter ids.
Note that if you have already released a plug-in prior to JUCE 4.3.0 then
enabling this will change your parameter ids, making your plug-in
incompatible with old automation data.
*/
#ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS
#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1
#endif
/** Config: JUCE_AU_WRAPPERS_SAVE_PROGRAM_STATES
Enable this if you want to receive get/setProgramStateInformation calls,
instead of get/setStateInformation calls, from the AU and AUv3 plug-in
wrappers. In JUCE version 5.4.5 and earlier this was the default behaviour,
so if you have modified the default implementations of get/setProgramStateInformation
(where the default implementations simply call through to get/setStateInformation)
then you may need to enable this configuration option to maintain backwards
compatibility with previously saved state.
*/
#ifndef JUCE_AU_WRAPPERS_SAVE_PROGRAM_STATES
#define JUCE_AU_WRAPPERS_SAVE_PROGRAM_STATES 0
#endif
/** Config: JUCE_STANDALONE_FILTER_WINDOW_USE_KIOSK_MODE
Enable this if you want your standalone plugin window to use kiosk mode.
By default, kiosk mode is enabled on iOS and Android.
*/
#ifndef JUCE_STANDALONE_FILTER_WINDOW_USE_KIOSK_MODE
#define JUCE_STANDALONE_FILTER_WINDOW_USE_KIOSK_MODE (JUCE_IOS || JUCE_ANDROID)
#endif
#include "detail/juce_CreatePluginFilter.h"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,27 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#define JUCE_INCLUDED_AAX_IN_MM 1
#include "juce_audio_plugin_client_AAX.cpp"
@@ -0,0 +1,107 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include <juce_core/system/juce_TargetPlatform.h>
#if JucePlugin_Build_AAX
#include <AAX_Version.h>
static_assert (AAX_SDK_CURRENT_REVISION >= AAX_SDK_2p4p0_REVISION, "JUCE requires AAX SDK version 2.4.0 or higher");
#if JUCE_INTEL || (JUCE_MAC && JUCE_ARM)
#include <juce_core/system/juce_CompilerWarnings.h>
// Utilities
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant")
#include <Libs/AAXLibrary/source/AAX_CAutoreleasePool.Win.cpp>
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations",
"-Wextra-semi",
"-Wfloat-equal",
"-Winconsistent-missing-destructor-override",
"-Wshift-sign-overflow",
"-Wunused-parameter",
"-Wzero-as-null-pointer-constant",
"-Wfour-char-constants",
"-Wdeprecated-copy-with-user-provided-dtor",
"-Wdeprecated")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6001 6053 4996 5033 4068 4996 5272)
#include <Libs/AAXLibrary/source/AAX_CChunkDataParser.cpp>
#include <Libs/AAXLibrary/source/AAX_CHostServices.cpp>
#if defined (_WIN32) && ! defined (WIN32)
#define WIN32
#endif
#include <Libs/AAXLibrary/source/AAX_CMutex.cpp>
#include <Libs/AAXLibrary/source/AAX_CommonConversions.cpp>
#include <Libs/AAXLibrary/source/AAX_CPacketDispatcher.cpp>
#include <Libs/AAXLibrary/source/AAX_CString.cpp>
// Versioned Interfaces
#include <Interfaces/ACF/CACFClassFactory.cpp>
#include <Libs/AAXLibrary/source/AAX_CACFUnknown.cpp>
#include <Libs/AAXLibrary/source/AAX_CUIDs.cpp>
#include <Libs/AAXLibrary/source/AAX_IEffectDirectData.cpp>
#include <Libs/AAXLibrary/source/AAX_IEffectGUI.cpp>
#include <Libs/AAXLibrary/source/AAX_IEffectParameters.cpp>
#include <Libs/AAXLibrary/source/AAX_IHostProcessor.cpp>
#include <Libs/AAXLibrary/source/AAX_Properties.cpp>
#include <Libs/AAXLibrary/source/AAX_VAutomationDelegate.cpp>
#include <Libs/AAXLibrary/source/AAX_VCollection.cpp>
#include <Libs/AAXLibrary/source/AAX_VComponentDescriptor.cpp>
#include <Libs/AAXLibrary/source/AAX_VController.cpp>
#include <Libs/AAXLibrary/source/AAX_VDescriptionHost.cpp>
#include <Libs/AAXLibrary/source/AAX_VEffectDescriptor.cpp>
#include <Libs/AAXLibrary/source/AAX_VFeatureInfo.cpp>
#include <Libs/AAXLibrary/source/AAX_VHostProcessorDelegate.cpp>
#include <Libs/AAXLibrary/source/AAX_VHostServices.cpp>
#include <Libs/AAXLibrary/source/AAX_VPageTable.cpp>
#include <Libs/AAXLibrary/source/AAX_VPrivateDataAccess.cpp>
#include <Libs/AAXLibrary/source/AAX_VPropertyMap.cpp>
#include <Libs/AAXLibrary/source/AAX_VTransport.cpp>
#include <Libs/AAXLibrary/source/AAX_VViewContainer.cpp>
#include <Libs/AAXLibrary/source/AAX_CEffectDirectData.cpp>
#include <Libs/AAXLibrary/source/AAX_CEffectGUI.cpp>
#include <Libs/AAXLibrary/source/AAX_CEffectParameters.cpp>
#include <Libs/AAXLibrary/source/AAX_CHostProcessor.cpp>
#include <Libs/AAXLibrary/source/AAX_CParameter.cpp>
#include <Libs/AAXLibrary/source/AAX_CParameterManager.cpp>
#include <Libs/AAXLibrary/source/AAX_Init.cpp>
#include <Libs/AAXLibrary/source/AAX_SliderConversions.cpp>
JUCE_END_IGNORE_WARNINGS_MSVC
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#else
#error "This version of the AAX SDK does not support the current platform."
#endif
#endif
@@ -0,0 +1,48 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include <juce_core/system/juce_TargetPlatform.h>
#include <juce_audio_plugin_client/detail/juce_CheckSettingMacros.h>
#if JucePlugin_Enable_ARA
#include <juce_audio_plugin_client/detail/juce_IncludeSystemHeaders.h>
#include <juce_audio_plugin_client/detail/juce_IncludeModuleHeaders.h>
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter",
"-Wgnu-zero-variadic-macro-arguments",
"-Wmissing-prototypes",
"-Wfloat-equal")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4100)
#include <ARA_Library/PlugIn/ARAPlug.cpp>
#include <ARA_Library/Dispatch/ARAPlugInDispatch.cpp>
#include <ARA_Library/Utilities/ARAPitchInterpretation.cpp>
#include <ARA_Library/Utilities/ARAChannelArrangement.cpp>
JUCE_END_IGNORE_WARNINGS_MSVC
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,108 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include <juce_core/system/juce_TargetPlatform.h>
#if JucePlugin_Build_AU
#include <juce_core/system/juce_CompilerWarnings.h>
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wambiguous-reversed-operator",
"-Wc99-extensions",
"-Wcast-align",
"-Wcomment",
"-Wconversion",
"-Wdeprecated-anon-enum-enum-conversion",
"-Wextra-semi",
"-Wextra-tokens",
"-Wfloat-equal",
"-Wformat-pedantic",
"-Wfour-char-constants",
"-Wgnu-zero-variadic-macro-arguments",
"-Wignored-qualifiers",
"-Wimplicit-fallthrough",
"-Wmissing-prototypes",
"-Wnullable-to-nonnull-conversion",
"-Wparentheses",
"-Wshadow-all",
"-Wswitch-enum",
"-Wunknown-attributes",
"-Wunused",
"-Wunused-parameter",
"-Wzero-as-null-pointer-constant")
// From MacOS 10.13 and iOS 11 Apple has (sensibly!) stopped defining a whole
// set of functions with rather generic names. However, we still need a couple
// of them to compile the files below.
#ifndef verify
#define verify(assertion) __Verify(assertion)
#endif
#ifndef verify_noerr
#define verify_noerr(errorCode) __Verify_noErr(errorCode)
#endif
#if ! defined (MAC_OS_VERSION_11_0) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_11_0
// These constants are only defined in the macOS 11+ SDKs
enum MIDICVStatus : unsigned int
{
kMIDICVStatusNoteOff = 0x8,
kMIDICVStatusNoteOn = 0x9,
kMIDICVStatusPolyPressure = 0xA,
kMIDICVStatusControlChange = 0xB,
kMIDICVStatusProgramChange = 0xC,
kMIDICVStatusChannelPressure = 0xD,
kMIDICVStatusPitchBend = 0xE,
kMIDICVStatusRegisteredPNC = 0x0,
kMIDICVStatusAssignablePNC = 0x1,
kMIDICVStatusRegisteredControl = 0x2,
kMIDICVStatusAssignableControl = 0x3,
kMIDICVStatusRelRegisteredControl = 0x4,
kMIDICVStatusRelAssignableControl = 0x5,
kMIDICVStatusPerNotePitchBend = 0x6,
kMIDICVStatusPerNoteMgmt = 0xF
};
#endif
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUBase.cpp>
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUBuffer.cpp>
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUBufferAllocator.cpp>
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUEffectBase.cpp>
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUInputElement.cpp>
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUMIDIBase.cpp>
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUMIDIEffectBase.cpp>
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUOutputElement.cpp>
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUPlugInDispatch.cpp>
#include <juce_audio_plugin_client/AU/AudioUnitSDK/AUScopeElement.cpp>
#include <juce_audio_plugin_client/AU/AudioUnitSDK/ComponentBase.cpp>
#include <juce_audio_plugin_client/AU/AudioUnitSDK/MusicDeviceBase.cpp>
#undef verify
#undef verify_noerr
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include "juce_audio_plugin_client_LV2.cpp"
@@ -0,0 +1,199 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include <juce_core/system/juce_TargetPlatform.h>
#if JucePlugin_Build_Standalone
#if ! JUCE_MODULE_AVAILABLE_juce_audio_utils
#error To compile AudioUnitv3 and/or Standalone plug-ins, you need to add the juce_audio_utils and juce_audio_devices modules!
#endif
#include <juce_core/system/juce_TargetPlatform.h>
#include <juce_audio_plugin_client/detail/juce_CheckSettingMacros.h>
#include <juce_audio_plugin_client/detail/juce_IncludeSystemHeaders.h>
#include <juce_audio_plugin_client/detail/juce_IncludeModuleHeaders.h>
#include <juce_gui_basics/native/juce_WindowsHooks_windows.h>
#include <juce_audio_plugin_client/detail/juce_PluginUtilities.h>
#include <juce_audio_devices/juce_audio_devices.h>
#include <juce_gui_extra/juce_gui_extra.h>
#include <juce_audio_utils/juce_audio_utils.h>
// You can set this flag in your build if you need to specify a different
// standalone JUCEApplication class for your app to use. If you don't
// set it then by default we'll just create a simple one as below.
#if ! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP
#include <juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h>
namespace juce
{
//==============================================================================
class StandaloneFilterApp final : public JUCEApplication
{
public:
StandaloneFilterApp()
{
PropertiesFile::Options options;
options.applicationName = appName;
options.filenameSuffix = ".settings";
options.osxLibrarySubFolder = "Application Support";
#if JUCE_LINUX || JUCE_BSD
options.folderName = "~/.config";
#else
options.folderName = "";
#endif
appProperties.setStorageParameters (options);
}
const String getApplicationName() override { return appName; }
const String getApplicationVersion() override { return JucePlugin_VersionString; }
bool moreThanOneInstanceAllowed() override { return true; }
void anotherInstanceStarted (const String&) override {}
virtual StandaloneFilterWindow* createWindow()
{
#ifdef JucePlugin_PreferredChannelConfigurations
StandalonePluginHolder::PluginInOuts channels[] = { JucePlugin_PreferredChannelConfigurations };
#endif
return new StandaloneFilterWindow (getApplicationName(),
LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId),
appProperties.getUserSettings(),
false, {}, nullptr
#ifdef JucePlugin_PreferredChannelConfigurations
, juce::Array<StandalonePluginHolder::PluginInOuts> (channels, juce::numElementsInArray (channels))
#else
, {}
#endif
#if JUCE_DONT_AUTO_OPEN_MIDI_DEVICES_ON_MOBILE
, false
#endif
);
}
//==============================================================================
void initialise (const String&) override
{
mainWindow.reset (createWindow());
#if JUCE_STANDALONE_FILTER_WINDOW_USE_KIOSK_MODE
Desktop::getInstance().setKioskModeComponent (mainWindow.get(), false);
#endif
mainWindow->setVisible (true);
}
void shutdown() override
{
mainWindow = nullptr;
appProperties.saveIfNeeded();
}
//==============================================================================
void systemRequestedQuit() override
{
if (mainWindow != nullptr)
mainWindow->pluginHolder->savePluginState();
if (ModalComponentManager::getInstance()->cancelAllModalComponents())
{
Timer::callAfterDelay (100, []()
{
if (auto app = JUCEApplicationBase::getInstance())
app->systemRequestedQuit();
});
}
else
{
quit();
}
}
protected:
ApplicationProperties appProperties;
std::unique_ptr<StandaloneFilterWindow> mainWindow;
private:
const String appName { CharPointer_UTF8 (JucePlugin_Name) };
};
} // namespace juce
#if JucePlugin_Build_Standalone && JUCE_IOS
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes")
using namespace juce;
bool JUCE_CALLTYPE juce_isInterAppAudioConnected()
{
if (auto holder = StandalonePluginHolder::getInstance())
return holder->isInterAppAudioConnected();
return false;
}
void JUCE_CALLTYPE juce_switchToHostApplication()
{
if (auto holder = StandalonePluginHolder::getInstance())
holder->switchToHostApplication();
}
Image JUCE_CALLTYPE juce_getIAAHostIcon (int size)
{
if (auto holder = StandalonePluginHolder::getInstance())
return holder->getIAAHostIcon (size);
return Image();
}
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#endif
#endif
#if JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP
extern juce::JUCEApplicationBase* juce_CreateApplication();
#if JUCE_IOS
extern void* juce_GetIOSCustomDelegateClass();
#endif
#else
JUCE_CREATE_APPLICATION_DEFINE (juce::StandaloneFilterApp)
#endif
#if ! JUCE_USE_CUSTOM_PLUGIN_STANDALONE_ENTRYPOINT
JUCE_MAIN_FUNCTION_DEFINITION
#endif
#endif
@@ -0,0 +1,777 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include <juce_core/system/juce_TargetPlatform.h>
#if JucePlugin_Build_Unity
#include <juce_audio_plugin_client/detail/juce_PluginUtilities.h>
#include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp>
#if JUCE_WINDOWS
#include <juce_audio_plugin_client/detail/juce_IncludeSystemHeaders.h>
#endif
#include <juce_audio_plugin_client/Unity/juce_UnityPluginInterface.h>
#include <juce_events/native/juce_RunningInUnity.h>
//==============================================================================
namespace juce
{
typedef ComponentPeer* (*createUnityPeerFunctionType) (Component&);
extern createUnityPeerFunctionType juce_createUnityPeerFn;
//==============================================================================
class UnityPeer final : public ComponentPeer,
public AsyncUpdater
{
public:
UnityPeer (Component& ed)
: ComponentPeer (ed, 0),
mouseWatcher (*this)
{
getEditor().setResizable (false, false);
}
//==============================================================================
Rectangle<int> getBounds() const override { return bounds; }
Point<float> localToGlobal (Point<float> relativePosition) override { return relativePosition + getBounds().getPosition().toFloat(); }
Point<float> globalToLocal (Point<float> screenPosition) override { return screenPosition - getBounds().getPosition().toFloat(); }
using ComponentPeer::localToGlobal;
using ComponentPeer::globalToLocal;
StringArray getAvailableRenderingEngines() override { return StringArray ("Software Renderer"); }
void setBounds (const Rectangle<int>& newBounds, bool) override
{
bounds = newBounds;
mouseWatcher.setBoundsToWatch (bounds);
}
bool contains (Point<int> localPos, bool) const override
{
if (isPositiveAndBelow (localPos.getX(), getBounds().getWidth())
&& isPositiveAndBelow (localPos.getY(), getBounds().getHeight()))
return true;
return false;
}
void handleAsyncUpdate() override
{
fillPixels();
}
//==============================================================================
AudioProcessorEditor& getEditor() { return *dynamic_cast<AudioProcessorEditor*> (&getComponent()); }
void setPixelDataHandle (uint8* handle, int width, int height)
{
pixelData = handle;
textureWidth = width;
textureHeight = height;
renderImage = Image (new UnityBitmapImage (pixelData, width, height));
}
// N.B. This is NOT an efficient way to do this and you shouldn't use this method in your own code.
// It works for our purposes here but a much more efficient way would be to use a GL texture.
void fillPixels()
{
if (pixelData == nullptr)
return;
LowLevelGraphicsSoftwareRenderer renderer (renderImage);
renderer.addTransform (AffineTransform::verticalFlip ((float) getComponent().getHeight()));
handlePaint (renderer);
for (int i = 0; i < textureWidth * textureHeight * 4; i += 4)
{
auto r = pixelData[i + 2];
auto g = pixelData[i + 1];
auto b = pixelData[i + 0];
pixelData[i + 0] = r;
pixelData[i + 1] = g;
pixelData[i + 2] = b;
}
}
void forwardMouseEvent (Point<float> position, ModifierKeys mods)
{
ModifierKeys::currentModifiers = mods;
handleMouseEvent (juce::MouseInputSource::mouse, position, mods, juce::MouseInputSource::defaultPressure,
juce::MouseInputSource::defaultOrientation, juce::Time::currentTimeMillis());
}
void forwardKeyPress (int code, String name, ModifierKeys mods)
{
ModifierKeys::currentModifiers = mods;
handleKeyPress (getKeyPress (code, name));
}
private:
//==============================================================================
struct UnityBitmapImage final : public ImagePixelData
{
UnityBitmapImage (uint8* data, int w, int h)
: ImagePixelData (Image::PixelFormat::ARGB, w, h),
imageData (data),
lineStride (width * pixelStride)
{
}
std::unique_ptr<ImageType> createType() const override
{
return std::make_unique<SoftwareImageType>();
}
std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override
{
return std::make_unique<LowLevelGraphicsSoftwareRenderer> (Image (this));
}
void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, [[maybe_unused]] Image::BitmapData::ReadWriteMode mode) override
{
const auto offset = (size_t) x * (size_t) pixelStride + (size_t) y * (size_t) lineStride;
bitmap.data = imageData + offset;
bitmap.size = (size_t) (lineStride * height) - offset;
bitmap.pixelFormat = pixelFormat;
bitmap.lineStride = lineStride;
bitmap.pixelStride = pixelStride;
}
ImagePixelData::Ptr clone() override
{
auto im = new UnityBitmapImage (imageData, width, height);
for (int i = 0; i < height; ++i)
memcpy (im->imageData + i * lineStride, imageData + i * lineStride, (size_t) lineStride);
return im;
}
uint8* imageData;
int pixelStride = 4, lineStride;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnityBitmapImage)
};
//==============================================================================
struct MouseWatcher final : public Timer
{
MouseWatcher (ComponentPeer& o) : owner (o) {}
void timerCallback() override
{
auto pos = Desktop::getMousePosition();
if (boundsToWatch.contains (pos) && pos != lastMousePos)
{
auto ms = Desktop::getInstance().getMainMouseSource();
if (! ms.getCurrentModifiers().isLeftButtonDown())
owner.handleMouseEvent (juce::MouseInputSource::mouse, owner.globalToLocal (pos.toFloat()), {},
juce::MouseInputSource::defaultPressure, juce::MouseInputSource::defaultOrientation, juce::Time::currentTimeMillis());
lastMousePos = pos;
}
}
void setBoundsToWatch (Rectangle<int> b)
{
if (boundsToWatch != b)
boundsToWatch = b;
startTimer (250);
}
ComponentPeer& owner;
Rectangle<int> boundsToWatch;
Point<int> lastMousePos;
};
//==============================================================================
KeyPress getKeyPress (int keyCode, String name)
{
if (keyCode >= 32 && keyCode <= 64)
return { keyCode, ModifierKeys::currentModifiers, juce::juce_wchar (keyCode) };
if (keyCode >= 91 && keyCode <= 122)
return { keyCode, ModifierKeys::currentModifiers, name[0] };
if (keyCode >= 256 && keyCode <= 265)
return { juce::KeyPress::numberPad0 + (keyCode - 256), ModifierKeys::currentModifiers, juce::String (keyCode - 256).getCharPointer()[0] };
if (keyCode == 8) return { juce::KeyPress::backspaceKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 127) return { juce::KeyPress::deleteKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 9) return { juce::KeyPress::tabKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 13) return { juce::KeyPress::returnKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 27) return { juce::KeyPress::escapeKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 32) return { juce::KeyPress::spaceKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 266) return { juce::KeyPress::numberPadDecimalPoint, ModifierKeys::currentModifiers, {} };
if (keyCode == 267) return { juce::KeyPress::numberPadDivide, ModifierKeys::currentModifiers, {} };
if (keyCode == 268) return { juce::KeyPress::numberPadMultiply, ModifierKeys::currentModifiers, {} };
if (keyCode == 269) return { juce::KeyPress::numberPadSubtract, ModifierKeys::currentModifiers, {} };
if (keyCode == 270) return { juce::KeyPress::numberPadAdd, ModifierKeys::currentModifiers, {} };
if (keyCode == 272) return { juce::KeyPress::numberPadEquals, ModifierKeys::currentModifiers, {} };
if (keyCode == 273) return { juce::KeyPress::upKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 274) return { juce::KeyPress::downKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 275) return { juce::KeyPress::rightKey, ModifierKeys::currentModifiers, {} };
if (keyCode == 276) return { juce::KeyPress::leftKey, ModifierKeys::currentModifiers, {} };
return {};
}
//==============================================================================
Rectangle<int> bounds;
MouseWatcher mouseWatcher;
uint8* pixelData = nullptr;
int textureWidth, textureHeight;
Image renderImage;
//==============================================================================
void setMinimised (bool) override {}
bool isMinimised() const override { return false; }
void setFullScreen (bool) override {}
bool isFullScreen() const override { return false; }
bool setAlwaysOnTop (bool) override { return false; }
void toFront (bool) override {}
void toBehind (ComponentPeer*) override {}
bool isFocused() const override { return true; }
void grabFocus() override {}
void* getNativeHandle() const override { return nullptr; }
OptionalBorderSize getFrameSizeIfPresent() const override { return {}; }
BorderSize<int> getFrameSize() const override { return {}; }
void setVisible (bool) override {}
void setTitle (const String&) override {}
void setIcon (const Image&) override {}
void textInputRequired (Point<int>, TextInputTarget&) override {}
void setAlpha (float) override {}
void performAnyPendingRepaintsNow() override {}
void repaint (const Rectangle<int>&) override {}
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnityPeer)
};
static ComponentPeer* createUnityPeer (Component& c) { return new UnityPeer (c); }
//==============================================================================
class AudioProcessorUnityWrapper
{
public:
AudioProcessorUnityWrapper (bool isTemporary)
{
detail::RunningInUnity::state = true;
pluginInstance = createPluginFilterOfType (AudioProcessor::wrapperType_Unity);
if (! isTemporary && pluginInstance->hasEditor())
{
pluginInstanceEditor.reset (pluginInstance->createEditorIfNeeded());
pluginInstanceEditor->setVisible (true);
detail::PluginUtilities::addToDesktop (*pluginInstanceEditor, nullptr);
}
juceParameters.update (*pluginInstance, false);
}
~AudioProcessorUnityWrapper()
{
if (pluginInstanceEditor != nullptr)
{
pluginInstanceEditor->removeFromDesktop();
PopupMenu::dismissAllActiveMenus();
pluginInstanceEditor->processor.editorBeingDeleted (pluginInstanceEditor.get());
pluginInstanceEditor = nullptr;
}
}
void create (UnityAudioEffectState* state)
{
// only supported in Unity plugin API > 1.0
if (state->structSize >= sizeof (UnityAudioEffectState))
samplesPerBlock = static_cast<int> (state->dspBufferSize);
#ifdef JucePlugin_PreferredChannelConfigurations
short configs[][2] = { JucePlugin_PreferredChannelConfigurations };
[[maybe_unused]] const int numConfigs = sizeof (configs) / sizeof (short[2]);
jassert (numConfigs > 0 && (configs[0][0] > 0 || configs[0][1] > 0));
pluginInstance->setPlayConfigDetails (configs[0][0], configs[0][1], state->sampleRate, samplesPerBlock);
#else
pluginInstance->setRateAndBufferSizeDetails (state->sampleRate, samplesPerBlock);
#endif
pluginInstance->prepareToPlay (state->sampleRate, samplesPerBlock);
scratchBuffer.setSize (jmax (pluginInstance->getTotalNumInputChannels(), pluginInstance->getTotalNumOutputChannels()), samplesPerBlock);
}
void release()
{
pluginInstance->releaseResources();
}
void reset()
{
pluginInstance->reset();
}
void process (float* inBuffer, float* outBuffer, int bufferSize, int numInChannels, int numOutChannels, bool isBypassed)
{
// If the plugin has a bypass parameter, set it to the current bypass state
if (auto* param = pluginInstance->getBypassParameter())
if (isBypassed != (param->getValue() >= 0.5f))
param->setValueNotifyingHost (isBypassed ? 1.0f : 0.0f);
for (int pos = 0; pos < bufferSize;)
{
auto max = jmin (bufferSize - pos, samplesPerBlock);
processBuffers (inBuffer + (pos * numInChannels), outBuffer + (pos * numOutChannels), max, numInChannels, numOutChannels, isBypassed);
pos += max;
}
}
void declareParameters (UnityAudioEffectDefinition& definition)
{
static std::unique_ptr<UnityAudioParameterDefinition> parametersPtr;
static int numParams = 0;
if (parametersPtr == nullptr)
{
numParams = (int) juceParameters.size();
parametersPtr.reset (static_cast<UnityAudioParameterDefinition*> (std::calloc (static_cast<size_t> (numParams),
sizeof (UnityAudioParameterDefinition))));
parameterDescriptions.clear();
for (int i = 0; i < numParams; ++i)
{
auto* parameter = juceParameters.getParamForIndex (i);
auto& paramDef = parametersPtr.get()[i];
const auto nameLength = (size_t) numElementsInArray (paramDef.name);
const auto unitLength = (size_t) numElementsInArray (paramDef.unit);
parameter->getName ((int) nameLength - 1).copyToUTF8 (paramDef.name, nameLength);
if (parameter->getLabel().isNotEmpty())
parameter->getLabel().copyToUTF8 (paramDef.unit, unitLength);
parameterDescriptions.add (parameter->getName (15));
paramDef.description = parameterDescriptions[i].toRawUTF8();
paramDef.defaultVal = parameter->getDefaultValue();
paramDef.min = 0.0f;
paramDef.max = 1.0f;
paramDef.displayScale = 1.0f;
paramDef.displayExponent = 1.0f;
}
}
definition.numParameters = static_cast<uint32> (numParams);
definition.parameterDefintions = parametersPtr.get();
}
void setParameter (int index, float value) { juceParameters.getParamForIndex (index)->setValueNotifyingHost (value); }
float getParameter (int index) const noexcept { return juceParameters.getParamForIndex (index)->getValue(); }
String getParameterString (int index) const noexcept
{
auto* param = juceParameters.getParamForIndex (index);
return param->getText (param->getValue(), 16);
}
int getNumInputChannels() const noexcept { return pluginInstance->getTotalNumInputChannels(); }
int getNumOutputChannels() const noexcept { return pluginInstance->getTotalNumOutputChannels(); }
bool hasEditor() const noexcept { return pluginInstance->hasEditor(); }
UnityPeer& getEditorPeer() const
{
auto* peer = dynamic_cast<UnityPeer*> (pluginInstanceEditor->getPeer());
jassert (peer != nullptr);
return *peer;
}
private:
//==============================================================================
void processBuffers (float* inBuffer, float* outBuffer, int bufferSize, int numInChannels, int numOutChannels, bool isBypassed)
{
int ch;
for (ch = 0; ch < numInChannels; ++ch)
{
using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const>;
DstSampleType dstData (scratchBuffer.getWritePointer (ch));
SrcSampleType srcData (inBuffer + ch, numInChannels);
dstData.convertSamples (srcData, bufferSize);
}
for (; ch < numOutChannels; ++ch)
scratchBuffer.clear (ch, 0, bufferSize);
{
const ScopedLock sl (pluginInstance->getCallbackLock());
if (pluginInstance->isSuspended())
{
scratchBuffer.clear();
}
else
{
MidiBuffer mb;
if (isBypassed && pluginInstance->getBypassParameter() == nullptr)
pluginInstance->processBlockBypassed (scratchBuffer, mb);
else
pluginInstance->processBlock (scratchBuffer, mb);
}
}
for (ch = 0; ch < numOutChannels; ++ch)
{
using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst>;
using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
DstSampleType dstData (outBuffer + ch, numOutChannels);
SrcSampleType srcData (scratchBuffer.getReadPointer (ch));
dstData.convertSamples (srcData, bufferSize);
}
}
//==============================================================================
std::unique_ptr<AudioProcessor> pluginInstance;
std::unique_ptr<AudioProcessorEditor> pluginInstanceEditor;
int samplesPerBlock = 1024;
StringArray parameterDescriptions;
AudioBuffer<float> scratchBuffer;
LegacyAudioParametersWrapper juceParameters;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorUnityWrapper)
};
//==============================================================================
static HashMap<int, AudioProcessorUnityWrapper*>& getWrapperMap()
{
static HashMap<int, AudioProcessorUnityWrapper*> wrapperMap;
return wrapperMap;
}
static void onWrapperCreation (AudioProcessorUnityWrapper* wrapperToAdd)
{
getWrapperMap().set (std::abs (Random::getSystemRandom().nextInt (65536)), wrapperToAdd);
}
static void onWrapperDeletion (AudioProcessorUnityWrapper* wrapperToRemove)
{
getWrapperMap().removeValue (wrapperToRemove);
}
//==============================================================================
static UnityAudioEffectDefinition getEffectDefinition()
{
const auto wrapper = std::make_unique<AudioProcessorUnityWrapper> (true);
const String originalName { JucePlugin_Name };
const auto name = (! originalName.startsWithIgnoreCase ("audioplugin") ? "audioplugin_" : "") + originalName;
UnityAudioEffectDefinition result{};
name.copyToUTF8 (result.name, (size_t) numElementsInArray (result.name));
result.structSize = sizeof (UnityAudioEffectDefinition);
result.parameterStructSize = sizeof (UnityAudioParameterDefinition);
result.apiVersion = UNITY_AUDIO_PLUGIN_API_VERSION;
result.pluginVersion = JucePlugin_VersionCode;
// effects must set this to 0, generators > 0
result.channels = (wrapper->getNumInputChannels() != 0 ? 0
: static_cast<uint32> (wrapper->getNumOutputChannels()));
wrapper->declareParameters (result);
result.create = [] (UnityAudioEffectState* state)
{
auto* pluginInstance = new AudioProcessorUnityWrapper (false);
pluginInstance->create (state);
state->effectData = pluginInstance;
onWrapperCreation (pluginInstance);
return 0;
};
result.release = [] (UnityAudioEffectState* state)
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
pluginInstance->release();
onWrapperDeletion (pluginInstance);
delete pluginInstance;
if (getWrapperMap().size() == 0)
shutdownJuce_GUI();
return 0;
};
result.reset = [] (UnityAudioEffectState* state)
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
pluginInstance->reset();
return 0;
};
result.setPosition = [] (UnityAudioEffectState* state, unsigned int pos)
{
ignoreUnused (state, pos);
return 0;
};
result.process = [] (UnityAudioEffectState* state,
float* inBuffer,
float* outBuffer,
unsigned int bufferSize,
int numInChannels,
int numOutChannels)
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
if (pluginInstance != nullptr)
{
auto isPlaying = ((state->flags & stateIsPlaying) != 0);
auto isMuted = ((state->flags & stateIsMuted) != 0);
auto isPaused = ((state->flags & stateIsPaused) != 0);
const auto bypassed = ! isPlaying || (isMuted || isPaused);
pluginInstance->process (inBuffer, outBuffer, static_cast<int> (bufferSize), numInChannels, numOutChannels, bypassed);
}
else
{
FloatVectorOperations::clear (outBuffer, static_cast<int> (bufferSize) * numOutChannels);
}
return 0;
};
result.setFloatParameter = [] (UnityAudioEffectState* state, int index, float value)
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
pluginInstance->setParameter (index, value);
return 0;
};
result.getFloatParameter = [] (UnityAudioEffectState* state, int index, float* value, char* valueStr)
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
*value = pluginInstance->getParameter (index);
pluginInstance->getParameterString (index).copyToUTF8 (valueStr, 15);
return 0;
};
result.getFloatBuffer = [] (UnityAudioEffectState* state, const char* kind, float* buffer, int numSamples)
{
ignoreUnused (numSamples);
const StringRef kindStr { kind };
if (kindStr == StringRef ("Editor"))
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
buffer[0] = pluginInstance->hasEditor() ? 1.0f : 0.0f;
}
else if (kindStr == StringRef ("ID"))
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
for (HashMap<int, AudioProcessorUnityWrapper*>::Iterator i (getWrapperMap()); i.next();)
{
if (i.getValue() == pluginInstance)
{
buffer[0] = (float) i.getKey();
break;
}
}
return 0;
}
else if (kindStr == StringRef ("Size"))
{
auto* pluginInstance = state->getEffectData<AudioProcessorUnityWrapper>();
auto& editor = pluginInstance->getEditorPeer().getEditor();
buffer[0] = (float) editor.getBounds().getWidth();
buffer[1] = (float) editor.getBounds().getHeight();
buffer[2] = (float) editor.getConstrainer()->getMinimumWidth();
buffer[3] = (float) editor.getConstrainer()->getMinimumHeight();
buffer[4] = (float) editor.getConstrainer()->getMaximumWidth();
buffer[5] = (float) editor.getConstrainer()->getMaximumHeight();
}
return 0;
};
return result;
}
} // namespace juce
// From reading the example code, it seems that the triple indirection indicates
// an out-value of an array of pointers. That is, after calling this function, definitionsPtr
// should point to a pre-existing/static array of pointer-to-effect-definition.
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API UnityGetAudioEffectDefinitions (UnityAudioEffectDefinition*** definitionsPtr)
{
if (juce::getWrapperMap().size() == 0)
juce::initialiseJuce_GUI();
static std::once_flag flag;
std::call_once (flag, [] { juce::juce_createUnityPeerFn = juce::createUnityPeer; });
static auto definition = juce::getEffectDefinition();
static UnityAudioEffectDefinition* definitions[] { &definition };
*definitionsPtr = definitions;
return 1;
}
//==============================================================================
static juce::ModifierKeys unityModifiersToJUCE (UnityEventModifiers mods, bool mouseDown, int mouseButton = -1)
{
int flags = 0;
if (mouseDown)
{
if (mouseButton == 0)
flags |= juce::ModifierKeys::leftButtonModifier;
else if (mouseButton == 1)
flags |= juce::ModifierKeys::rightButtonModifier;
else if (mouseButton == 2)
flags |= juce::ModifierKeys::middleButtonModifier;
}
if (mods == 0)
return flags;
if ((mods & UnityEventModifiers::shift) != 0) flags |= juce::ModifierKeys::shiftModifier;
if ((mods & UnityEventModifiers::control) != 0) flags |= juce::ModifierKeys::ctrlModifier;
if ((mods & UnityEventModifiers::alt) != 0) flags |= juce::ModifierKeys::altModifier;
if ((mods & UnityEventModifiers::command) != 0) flags |= juce::ModifierKeys::commandModifier;
return { flags };
}
//==============================================================================
static juce::AudioProcessorUnityWrapper* getWrapperChecked (int id)
{
auto* wrapper = juce::getWrapperMap()[id];
jassert (wrapper != nullptr);
return wrapper;
}
//==============================================================================
static void UNITY_INTERFACE_API onRenderEvent (int id)
{
getWrapperChecked (id)->getEditorPeer().triggerAsyncUpdate();
}
UNITY_INTERFACE_EXPORT renderCallback UNITY_INTERFACE_API getRenderCallback()
{
return onRenderEvent;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityInitialiseTexture (int id, void* data, int w, int h)
{
getWrapperChecked (id)->getEditorPeer().setPixelDataHandle (reinterpret_cast<juce::uint8*> (data), w, h);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDown (int id, float x, float y, UnityEventModifiers unityMods, int button)
{
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, true, button));
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseDrag (int id, float x, float y, UnityEventModifiers unityMods, int button)
{
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, true, button));
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityMouseUp (int id, float x, float y, UnityEventModifiers unityMods)
{
getWrapperChecked (id)->getEditorPeer().forwardMouseEvent ({ x, y }, unityModifiersToJUCE (unityMods, false));
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unityKeyEvent (int id, int code, UnityEventModifiers mods, const char* name)
{
getWrapperChecked (id)->getEditorPeer().forwardKeyPress (code, name, unityModifiersToJUCE (mods, false));
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API unitySetScreenBounds (int id, float x, float y, float w, float h)
{
getWrapperChecked (id)->getEditorPeer().getEditor().setBounds ({ (int) x, (int) y, (int) w, (int) h });
}
//==============================================================================
#if JUCE_WINDOWS
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-prototypes")
extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
juce::Process::setCurrentModuleInstanceHandle (instance);
return true;
}
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#endif
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include "juce_audio_plugin_client_VST2.cpp"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include "juce_audio_plugin_client_VST3.cpp"