build(deps): vendor JUCE 7.0.12
This commit is contained in:
+46
@@ -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
|
||||
+2061
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
|
||||
+188
@@ -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
|
||||
+166
@@ -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
|
||||
+15
@@ -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
|
||||
+400
@@ -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
|
||||
+194
@@ -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
|
||||
+85
@@ -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
|
||||
+91
@@ -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
|
||||
+251
@@ -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
|
||||
+169
@@ -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
|
||||
+53
@@ -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
|
||||
+40
@@ -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
|
||||
+20
@@ -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
|
||||
+27
@@ -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
|
||||
+33
@@ -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
|
||||
+773
@@ -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
|
||||
+110
@@ -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
|
||||
+446
@@ -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
|
||||
+435
@@ -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
|
||||
+49
@@ -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
|
||||
+524
@@ -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
|
||||
+22
@@ -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 */
|
||||
+98
@@ -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
|
||||
+162
@@ -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
|
||||
+7
@@ -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.
|
||||
+202
@@ -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.
|
||||
+117
@@ -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
|
||||
+64
@@ -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
|
||||
+172
@@ -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;
|
||||
}
|
||||
+1157
File diff suppressed because it is too large
Load Diff
+194
@@ -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);
|
||||
+73
@@ -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
|
||||
+26
@@ -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"
|
||||
+77
@@ -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
|
||||
+48
@@ -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
|
||||
+34
@@ -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
|
||||
+57
@@ -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
|
||||
+115
@@ -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
|
||||
+167
@@ -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
|
||||
+98
@@ -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
|
||||
+130
@@ -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"
|
||||
+2705
File diff suppressed because it is too large
Load Diff
+27
@@ -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"
|
||||
+107
@@ -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
|
||||
+48
@@ -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
|
||||
+2658
File diff suppressed because it is too large
Load Diff
+108
@@ -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
|
||||
+2014
File diff suppressed because it is too large
Load Diff
+1831
File diff suppressed because it is too large
Load Diff
+26
@@ -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"
|
||||
+199
@@ -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
|
||||
+777
@@ -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
|
||||
+2204
File diff suppressed because it is too large
Load Diff
+26
@@ -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"
|
||||
+4316
File diff suppressed because it is too large
Load Diff
+26
@@ -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"
|
||||
Reference in New Issue
Block a user