diff options
Diffstat (limited to 'libs/appleutility/CoreAudio/AudioUnits')
57 files changed, 17195 insertions, 0 deletions
diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUBase.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUBase.cpp new file mode 100644 index 0000000000..612bc4d308 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUBase.cpp @@ -0,0 +1,2393 @@ +/* + File: AUBase.cpp + Abstract: AUBase.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUBase.h" +#include "AUDispatch.h" +#include "AUInputElement.h" +#include "AUOutputElement.h" +#include <algorithm> +#include <syslog.h> +#include "CAAudioChannelLayout.h" +#include "CAHostTimeBase.h" +#include "CAVectorUnit.h" +#include "CAXException.h" + + + +#if TARGET_OS_MAC && (TARGET_CPU_X86 || TARGET_CPU_X86_64) + // our compiler does ALL floating point with SSE + inline int GETCSR () { int _result; asm volatile ("stmxcsr %0" : "=m" (*&_result) ); return _result; } + inline void SETCSR (int a) { int _temp = a; asm volatile( "ldmxcsr %0" : : "m" (*&_temp ) ); } + + #define DISABLE_DENORMALS int _savemxcsr = GETCSR(); SETCSR(_savemxcsr | 0x8040); + #define RESTORE_DENORMALS SETCSR(_savemxcsr); +#else + #define DISABLE_DENORMALS + #define RESTORE_DENORMALS +#endif + +static bool sAUBaseCFStringsInitialized = false; +// this is used for the presets +static CFStringRef kUntitledString = NULL; +//these are the current keys for the class info document +static CFStringRef kVersionString = NULL; +static CFStringRef kTypeString = NULL; +static CFStringRef kSubtypeString = NULL; +static CFStringRef kManufacturerString = NULL; +static CFStringRef kDataString = NULL; +static CFStringRef kNameString = NULL; +static CFStringRef kRenderQualityString = NULL; +static CFStringRef kCPULoadString = NULL; +static CFStringRef kElementNameString = NULL; +static CFStringRef kPartString = NULL; + +SInt32 AUBase::sVectorUnitType = kVecUninitialized; + +//_____________________________________________________________________________ +// +AUBase::AUBase( AudioComponentInstance inInstance, + UInt32 numInputElements, + UInt32 numOutputElements, + UInt32 numGroupElements) : + ComponentBase(inInstance), + mElementsCreated(false), + mInitialized(false), + mHasBegunInitializing(false), + mInitNumInputEls(numInputElements), mInitNumOutputEls(numOutputElements), +#if !CA_BASIC_AU_FEATURES + mInitNumGroupEls(numGroupElements), +#endif + mRenderCallbacksTouched(false), + mRenderThreadID (NULL), + mWantsRenderThreadID (false), + mLastRenderError(0), + mUsesFixedBlockSize(false), + mBuffersAllocated(false), + mLogString (NULL), + mNickName (NULL), + mAUMutex(NULL) + #if !CA_NO_AU_UI_FEATURES + , + mContextName(NULL) + #endif +{ + ResetRenderTime (); + + if(!sAUBaseCFStringsInitialized) + { + kUntitledString = CFSTR("Untitled"); + kVersionString = CFSTR(kAUPresetVersionKey); + kTypeString = CFSTR(kAUPresetTypeKey); + kSubtypeString = CFSTR(kAUPresetSubtypeKey); + kManufacturerString = CFSTR(kAUPresetManufacturerKey); + kDataString = CFSTR(kAUPresetDataKey); + kNameString = CFSTR(kAUPresetNameKey); + kRenderQualityString = CFSTR(kAUPresetRenderQualityKey); + kCPULoadString = CFSTR(kAUPresetCPULoadKey); + kElementNameString = CFSTR(kAUPresetElementNameKey); + kPartString = CFSTR(kAUPresetPartKey); + sAUBaseCFStringsInitialized = true; + } + + if (sVectorUnitType == kVecUninitialized) { + sVectorUnitType = CAVectorUnit::GetVectorUnitType() ; + } + + mAudioUnitAPIVersion = 2; + + SetMaxFramesPerSlice(kAUDefaultMaxFramesPerSlice); + + GlobalScope().Initialize(this, kAudioUnitScope_Global, 1); + +#if !CA_NO_AU_UI_FEATURES + memset (&mHostCallbackInfo, 0, sizeof (mHostCallbackInfo)); +#endif + + + mCurrentPreset.presetNumber = -1; + mCurrentPreset.presetName = kUntitledString; + CFRetain (mCurrentPreset.presetName); +} + +//_____________________________________________________________________________ +// +AUBase::~AUBase() +{ + if (mCurrentPreset.presetName) CFRelease (mCurrentPreset.presetName); +#if !CA_NO_AU_UI_FEATURES + if (mContextName) CFRelease (mContextName); +#endif + if (mLogString) delete [] mLogString; + if (mNickName) CFRelease(mNickName); +} + +//_____________________________________________________________________________ +// +void AUBase::CreateElements() +{ + if (!mElementsCreated) { + Inputs().Initialize(this, kAudioUnitScope_Input, mInitNumInputEls); + Outputs().Initialize(this, kAudioUnitScope_Output, mInitNumOutputEls); +#if !CA_BASIC_AU_FEATURES + Groups().Initialize(this, kAudioUnitScope_Group, mInitNumGroupEls); +#endif + CreateExtendedElements(); + + mElementsCreated = true; + } +} + +//_____________________________________________________________________________ +// +void AUBase::SetMaxFramesPerSlice(UInt32 nFrames) +{ + mMaxFramesPerSlice = nFrames; + if (mBuffersAllocated) + ReallocateBuffers(); + PropertyChanged(kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0); +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::CanSetMaxFrames() const +{ + return IsInitialized() ? kAudioUnitErr_Initialized : OSStatus(noErr); +} + +//_____________________________________________________________________________ +// +void AUBase::ReallocateBuffers() +{ + CreateElements(); + + UInt32 nOutputs = Outputs().GetNumberOfElements(); + for (UInt32 i = 0; i < nOutputs; ++i) { + AUOutputElement *output = GetOutput(i); + output->AllocateBuffer(); // does no work if already allocated + } + UInt32 nInputs = Inputs().GetNumberOfElements(); + for (UInt32 i = 0; i < nInputs; ++i) { + AUInputElement *input = GetInput(i); + input->AllocateBuffer(); // does no work if already allocated + } + mBuffersAllocated = true; +} + +//_____________________________________________________________________________ +// +void AUBase::DeallocateIOBuffers() +{ + if (!mBuffersAllocated) + return; + + UInt32 nOutputs = Outputs().GetNumberOfElements(); + for (UInt32 i = 0; i < nOutputs; ++i) { + AUOutputElement *output = GetOutput(i); + output->DeallocateBuffer(); + } + UInt32 nInputs = Inputs().GetNumberOfElements(); + for (UInt32 i = 0; i < nInputs; ++i) { + AUInputElement *input = GetInput(i); + input->DeallocateBuffer(); + } + mBuffersAllocated = false; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::DoInitialize() +{ + OSStatus result = noErr; + + if (!mInitialized) { + result = Initialize(); + if (result == noErr) { + if (CanScheduleParameters()) + mParamList.reserve(24); + mHasBegunInitializing = true; + ReallocateBuffers(); // calls CreateElements() + mInitialized = true; // signal that it's okay to render + CAMemoryBarrier(); + } + } + + return result; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::Initialize() +{ + return noErr; +} + +//_____________________________________________________________________________ +// +void AUBase::PreDestructor() +{ + // this is called from the ComponentBase dispatcher, which doesn't know anything about our (optional) lock + CAMutex::Locker lock(mAUMutex); + DoCleanup(); +} + +//_____________________________________________________________________________ +// +void AUBase::DoCleanup() +{ + if (mInitialized) + Cleanup(); + + DeallocateIOBuffers(); + ResetRenderTime (); + + mInitialized = false; + mHasBegunInitializing = false; +} + +//_____________________________________________________________________________ +// +void AUBase::Cleanup() +{ +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::Reset( AudioUnitScope inScope, + AudioUnitElement inElement) +{ + ResetRenderTime (); + return noErr; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::DispatchGetPropertyInfo(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable) +{ + OSStatus result = noErr; + bool validateElement = true; + + switch (inID) { + case kAudioUnitProperty_MakeConnection: + ca_require(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Global, InvalidScope); + outDataSize = sizeof(AudioUnitConnection); + outWritable = true; + break; + + + case kAudioUnitProperty_SetRenderCallback: + ca_require(AudioUnitAPIVersion() > 1, InvalidProperty); + ca_require(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Global, InvalidScope); + outDataSize = sizeof(AURenderCallbackStruct); + outWritable = true; + break; + + case kAudioUnitProperty_StreamFormat: + outDataSize = sizeof(CAStreamBasicDescription); + outWritable = IsStreamFormatWritable(inScope, inElement); + break; + + case kAudioUnitProperty_SampleRate: + outDataSize = sizeof(Float64); + outWritable = IsStreamFormatWritable(inScope, inElement); + break; + + case kAudioUnitProperty_ClassInfo: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + outDataSize = sizeof(CFPropertyListRef); + outWritable = true; + break; + + case kAudioUnitProperty_FactoryPresets: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + result = GetPresets(NULL); + if (!result) { + outDataSize = sizeof(CFArrayRef); + outWritable = false; + } + break; + + case kAudioUnitProperty_PresentPreset: +#if !CA_USE_AUDIO_PLUGIN_ONLY +#ifndef __LP64__ + case kAudioUnitProperty_CurrentPreset: +#endif +#endif + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + outDataSize = sizeof(AUPreset); + outWritable = true; + break; + + case kAudioUnitProperty_ElementName: + outDataSize = sizeof (CFStringRef); + outWritable = true; + break; + + case kAudioUnitProperty_ParameterList: + { + UInt32 nparams = 0; + result = GetParameterList(inScope, NULL, nparams); + + outDataSize = sizeof(AudioUnitParameterID) * nparams; + outWritable = false; + validateElement = false; + } + break; + + case kAudioUnitProperty_ParameterInfo: + outDataSize = sizeof(AudioUnitParameterInfo); + outWritable = false; + validateElement = false; + break; + + case kAudioUnitProperty_ParameterHistoryInfo: + outDataSize = sizeof(AudioUnitParameterHistoryInfo); + outWritable = false; + validateElement = false; + break; + + case kAudioUnitProperty_ElementCount: + outDataSize = sizeof(UInt32); + outWritable = BusCountWritable(inScope); + validateElement = false; + break; + + case kAudioUnitProperty_Latency: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + outDataSize = sizeof(Float64); + outWritable = false; + break; + + case kAudioUnitProperty_TailTime: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + if (SupportsTail()) { + outDataSize = sizeof(Float64); + outWritable = false; + } else + goto InvalidProperty; + break; + + case kAudioUnitProperty_MaximumFramesPerSlice: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + outDataSize = sizeof(UInt32); + outWritable = true; + break; + + case kAudioUnitProperty_LastRenderError: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + outDataSize = sizeof(OSStatus); + outWritable = false; + break; + + case kAudioUnitProperty_SupportedNumChannels: + { + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + UInt32 num = SupportedNumChannels (NULL); + if (num) { + outDataSize = sizeof (AUChannelInfo) * num; + result = noErr; + } else + goto InvalidProperty; + outWritable = false; + break; + } + + case kAudioUnitProperty_SupportedChannelLayoutTags: + { + UInt32 numLayouts = GetChannelLayoutTags(inScope, inElement, NULL); + if (numLayouts) { + outDataSize = numLayouts * sizeof(AudioChannelLayoutTag); + result = noErr; + } else + goto InvalidProperty; + outWritable = false; + validateElement = false; //already done it + break; + } + + case kAudioUnitProperty_AudioChannelLayout: + { + outWritable = false; + outDataSize = GetAudioChannelLayout(inScope, inElement, NULL, outWritable); + if (outDataSize) { + result = noErr; + } else { + if (GetChannelLayoutTags(inScope, inElement, NULL) == 0) + goto InvalidProperty; + else + result = kAudioUnitErr_InvalidPropertyValue; + } + validateElement = false; //already done it + break; + } + +#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || TARGET_OS_IPHONE + case kAudioUnitProperty_ShouldAllocateBuffer: + ca_require((inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output), InvalidScope); + outWritable = true; + outDataSize = sizeof(UInt32); + break; +#endif + +#if !CA_USE_AUDIO_PLUGIN_ONLY + case kAudioUnitProperty_FastDispatch: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + if (!IsCMgrObject()) goto InvalidProperty; + outDataSize = sizeof(void *); + outWritable = false; + validateElement = false; + break; + + case kAudioUnitProperty_GetUIComponentList: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + outDataSize = GetNumCustomUIComponents(); + if (outDataSize == 0) + goto InvalidProperty; + outDataSize *= sizeof (AudioComponentDescription); + + outWritable = false; + break; +#endif + + case kAudioUnitProperty_ParameterValueStrings: + result = GetParameterValueStrings(inScope, inElement, NULL); + if (result == noErr) { + outDataSize = sizeof(CFArrayRef); + outWritable = false; + validateElement = false; + } + break; + +#if !CA_NO_AU_HOST_CALLBACKS + case kAudioUnitProperty_HostCallbacks: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + outDataSize = sizeof(mHostCallbackInfo); + outWritable = true; + break; +#endif +#if !CA_NO_AU_UI_FEATURES + case kAudioUnitProperty_ContextName: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + outDataSize = sizeof(CFStringRef); + outWritable = true; + break; + + case kAudioUnitProperty_IconLocation: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + outWritable = false; + if (!HasIcon()) + goto InvalidProperty; + outDataSize = sizeof(CFURLRef); + break; + + case kAudioUnitProperty_ParameterClumpName: + outDataSize = sizeof(AudioUnitParameterNameInfo ); + outWritable = false; + break; + +#endif // !CA_NO_AU_UI_FEATURES + + case 'lrst' : // kAudioUnitProperty_LastRenderedSampleTime + outDataSize = sizeof(Float64); + outWritable = false; + break; + + case kAudioUnitProperty_NickName: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + outDataSize = sizeof(CFStringRef); + outWritable = true; + break; + + default: + result = GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable); + validateElement = false; + break; + } + + if (result == noErr && validateElement) { + ca_require(GetElement(inScope, inElement) != NULL, InvalidElement); + } + + return result; +InvalidProperty: + return kAudioUnitErr_InvalidProperty; +InvalidScope: + return kAudioUnitErr_InvalidScope; +InvalidElement: + return kAudioUnitErr_InvalidElement; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::DispatchGetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData) +{ + // NOTE: We're currently only called from AUBase::ComponentEntryDispatch, which + // calls DispatchGetPropertyInfo first, which performs validation of the scope/element, + // and ensures that the outData buffer is non-null and large enough. + OSStatus result = noErr; + + switch (inID) { + case kAudioUnitProperty_StreamFormat: + *(CAStreamBasicDescription *)outData = GetStreamFormat(inScope, inElement); + break; + + case kAudioUnitProperty_SampleRate: + *(Float64 *)outData = GetStreamFormat(inScope, inElement).mSampleRate; + break; + + case kAudioUnitProperty_ParameterList: + { + UInt32 nparams = 0; + result = GetParameterList(inScope, (AudioUnitParameterID *)outData, nparams); + } + break; + + case kAudioUnitProperty_ParameterInfo: + result = GetParameterInfo(inScope, inElement, *(AudioUnitParameterInfo *)outData); + break; + + case kAudioUnitProperty_ParameterHistoryInfo: + { + AudioUnitParameterHistoryInfo* info = (AudioUnitParameterHistoryInfo*)outData; + result = GetParameterHistoryInfo(inScope, inElement, info->updatesPerSecond, info->historyDurationInSeconds); + } + break; + + case kAudioUnitProperty_ClassInfo: + { + *(CFPropertyListRef *)outData = NULL; + result = SaveState((CFPropertyListRef *)outData); + } + break; + + case kAudioUnitProperty_FactoryPresets: + { + *(CFArrayRef *)outData = NULL; + result = GetPresets ((CFArrayRef *)outData); + } + break; + + case kAudioUnitProperty_PresentPreset: +#if !CA_USE_AUDIO_PLUGIN_ONLY +#ifndef __LP64__ + case kAudioUnitProperty_CurrentPreset: +#endif +#endif + { + *(AUPreset *)outData = mCurrentPreset; + + // retain current string (as client owns a reference to it and will release it) + if (inID == kAudioUnitProperty_PresentPreset && mCurrentPreset.presetName) + CFRetain (mCurrentPreset.presetName); + + result = noErr; + } + break; + + case kAudioUnitProperty_ElementName: + { + AUElement * element = GetElement(inScope, inElement); + if (element->HasName()) { + *(CFStringRef *)outData = element->GetName(); + CFRetain (element->GetName()); + result = noErr; + } else + result = kAudioUnitErr_InvalidPropertyValue; + } + break; + + case kAudioUnitProperty_ElementCount: + *(UInt32 *)outData = GetScope(inScope).GetNumberOfElements(); + break; + + case kAudioUnitProperty_Latency: + *(Float64 *)outData = GetLatency(); + break; + + case kAudioUnitProperty_TailTime: + if (SupportsTail()) + *(Float64 *)outData = GetTailTime(); + else + result = kAudioUnitErr_InvalidProperty; + break; + + case kAudioUnitProperty_MaximumFramesPerSlice: + *(UInt32 *)outData = mMaxFramesPerSlice; + break; + + case kAudioUnitProperty_LastRenderError: + *(OSStatus *)outData = mLastRenderError; + mLastRenderError = 0; + break; + + case kAudioUnitProperty_SupportedNumChannels: + { + const AUChannelInfo* infoPtr = NULL; + UInt32 num = SupportedNumChannels (&infoPtr); + if(num != 0 && infoPtr != NULL) + memcpy (outData, infoPtr, num * sizeof (AUChannelInfo)); + } + break; + + case kAudioUnitProperty_SupportedChannelLayoutTags: + { + AudioChannelLayoutTag* ptr = outData ? static_cast<AudioChannelLayoutTag*>(outData) : NULL; + UInt32 numLayouts = GetChannelLayoutTags (inScope, inElement, ptr); + if (numLayouts == 0) + result = kAudioUnitErr_InvalidProperty; + } + break; + + case kAudioUnitProperty_AudioChannelLayout: + { + AudioChannelLayout* ptr = outData ? static_cast<AudioChannelLayout*>(outData) : NULL; + Boolean writable; + UInt32 dataSize = GetAudioChannelLayout(inScope, inElement, ptr, writable); + if (!dataSize) { + result = kAudioUnitErr_InvalidProperty; + } + break; + } + +#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || TARGET_OS_IPHONE + case kAudioUnitProperty_ShouldAllocateBuffer: + { + AUIOElement * element = GetIOElement(inScope, inElement); + *(UInt32*)outData = element->WillAllocateBuffer(); + break; + } +#endif + + case kAudioUnitProperty_ParameterValueStrings: + result = GetParameterValueStrings(inScope, inElement, (CFArrayRef *)outData); + break; + +#if !CA_USE_AUDIO_PLUGIN_ONLY + case kAudioUnitProperty_FastDispatch: + if (!IsCMgrObject()) result = kAudioUnitErr_InvalidProperty; + else { + switch (inElement) { + case kAudioUnitGetParameterSelect: + *(AudioUnitGetParameterProc *)outData = (AudioUnitGetParameterProc)CMgr_AudioUnitBaseGetParameter; + break; + case kAudioUnitSetParameterSelect: + *(AudioUnitSetParameterProc *)outData = (AudioUnitSetParameterProc)CMgr_AudioUnitBaseSetParameter; + break; + case kAudioUnitRenderSelect: + if (AudioUnitAPIVersion() > 1) + *(AudioUnitRenderProc *)outData = (AudioUnitRenderProc)CMgr_AudioUnitBaseRender; + else result = kAudioUnitErr_InvalidElement; + break; + default: + result = GetProperty(inID, inScope, inElement, outData); + break; + } + } + break; + + case kAudioUnitProperty_GetUIComponentList: + GetUIComponentDescs ((ComponentDescription*)outData); + break; +#endif + +#if !CA_NO_AU_HOST_CALLBACKS + case kAudioUnitProperty_HostCallbacks: + memcpy(outData, &mHostCallbackInfo, sizeof(mHostCallbackInfo)); + break; +#endif +#if !CA_NO_AU_UI_FEATURES + case kAudioUnitProperty_IconLocation: + { + CFURLRef iconLocation = CopyIconLocation(); + if (iconLocation) { + *(CFURLRef*)outData = iconLocation; + } else + result = kAudioUnitErr_InvalidProperty; + } + break; + + case kAudioUnitProperty_ContextName: + *(CFStringRef *)outData = mContextName; + if (mContextName) { + CFRetain(mContextName); + // retain CFString (if exists) since client will be responsible for its release + result = noErr; + } else { + result = kAudioUnitErr_InvalidPropertyValue; + } + break; + + case kAudioUnitProperty_ParameterClumpName: + { + AudioUnitParameterNameInfo * ioClumpInfo = (AudioUnitParameterNameInfo*) outData; + if (ioClumpInfo->inID == kAudioUnitClumpID_System) // this ID value is reserved + result = kAudioUnitErr_InvalidPropertyValue; + else + { + result = CopyClumpName(inScope, ioClumpInfo->inID, ioClumpInfo->inDesiredLength, &ioClumpInfo->outName); + + // this is provided for compatbility with existing implementations that don't know + // about this new mechanism + if (result == kAudioUnitErr_InvalidProperty) + result = GetProperty (inID, inScope, inElement, outData); + } + } + break; + +#endif // !CA_NO_AU_UI_FEATURES + + case 'lrst' : // kAudioUnitProperty_LastRenderedSampleTime + *(Float64*)outData = mCurrentRenderTime.mSampleTime; + break; + + case kAudioUnitProperty_NickName: + // Ownership follows Core Foundation's 'Copy Rule' + if (mNickName) CFRetain(mNickName); + *(CFStringRef*)outData = mNickName; + break; + + default: + result = GetProperty(inID, inScope, inElement, outData); + break; + } + return result; +} + + +//_____________________________________________________________________________ +// +OSStatus AUBase::DispatchSetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize) +{ + OSStatus result = noErr; + + switch (inID) { + case kAudioUnitProperty_MakeConnection: + ca_require(inDataSize >= sizeof(AudioUnitConnection), InvalidPropertyValue); + { + AudioUnitConnection &connection = *(AudioUnitConnection *)inData; + result = SetConnection(connection); + } + break; + + + case kAudioUnitProperty_SetRenderCallback: + { + ca_require(inDataSize >= sizeof(AURenderCallbackStruct), InvalidPropertyValue); + ca_require(AudioUnitAPIVersion() > 1, InvalidProperty); + AURenderCallbackStruct &callback = *(AURenderCallbackStruct*)inData; + result = SetInputCallback(kAudioUnitProperty_SetRenderCallback, inElement, callback.inputProc, callback.inputProcRefCon); + } + break; + + case kAudioUnitProperty_ElementCount: + ca_require(inDataSize == sizeof(UInt32), InvalidPropertyValue); + ca_require(BusCountWritable(inScope), NotWritable); + result = SetBusCount(inScope, *(UInt32*)inData); + if (result == noErr) { + PropertyChanged(inID, inScope, inElement); + } + break; + + case kAudioUnitProperty_MaximumFramesPerSlice: + ca_require(inDataSize == sizeof(UInt32), InvalidPropertyValue); + result = CanSetMaxFrames(); + if (result) return result; + SetMaxFramesPerSlice(*(UInt32 *)inData); + break; + + case kAudioUnitProperty_StreamFormat: + { + if (inDataSize < 36) goto InvalidPropertyValue; + ca_require(GetElement(inScope, inElement) != NULL, InvalidElement); + + CAStreamBasicDescription newDesc; + // now we're going to be ultra conservative! because of discrepancies between + // sizes of this struct based on aligment padding inconsistencies + memset (&newDesc, 0, sizeof(newDesc)); + memcpy (&newDesc, inData, 36); + + ca_require(ValidFormat(inScope, inElement, newDesc), InvalidFormat); + + const CAStreamBasicDescription curDesc = GetStreamFormat(inScope, inElement); + + if ( !curDesc.IsEqual(newDesc, false) ) { + ca_require(IsStreamFormatWritable(inScope, inElement), NotWritable); + result = ChangeStreamFormat(inScope, inElement, curDesc, newDesc); + } + } + break; + + case kAudioUnitProperty_SampleRate: + { + ca_require(inDataSize == sizeof(Float64), InvalidPropertyValue); + ca_require(GetElement(inScope, inElement) != NULL, InvalidElement); + + const CAStreamBasicDescription curDesc = GetStreamFormat(inScope, inElement); + CAStreamBasicDescription newDesc = curDesc; + newDesc.mSampleRate = *(Float64 *)inData; + + ca_require(ValidFormat(inScope, inElement, newDesc), InvalidFormat); + + if ( !curDesc.IsEqual(newDesc, false) ) { + ca_require(IsStreamFormatWritable(inScope, inElement), NotWritable); + result = ChangeStreamFormat(inScope, inElement, curDesc, newDesc); + } + } + break; + + case kAudioUnitProperty_AudioChannelLayout: + { + const AudioChannelLayout *layout = static_cast<const AudioChannelLayout *>(inData); + size_t headerSize = sizeof(AudioChannelLayout) - sizeof(AudioChannelDescription); + + ca_require(inDataSize >= headerSize + layout->mNumberChannelDescriptions * sizeof(AudioChannelDescription), InvalidPropertyValue); + result = SetAudioChannelLayout(inScope, inElement, layout); + if (result == noErr) + PropertyChanged(inID, inScope, inElement); + break; + } + + case kAudioUnitProperty_ClassInfo: + ca_require(inDataSize == sizeof(CFPropertyListRef *), InvalidPropertyValue); + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + result = RestoreState(*(CFPropertyListRef *)inData); + break; + + case kAudioUnitProperty_PresentPreset: +#if !CA_USE_AUDIO_PLUGIN_ONLY +#ifndef __LP64__ + case kAudioUnitProperty_CurrentPreset: +#endif +#endif + { + ca_require(inDataSize == sizeof(AUPreset), InvalidPropertyValue); + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + AUPreset & newPreset = *(AUPreset *)inData; + + if (newPreset.presetNumber >= 0) + { + result = NewFactoryPresetSet(newPreset); + // NewFactoryPresetSet SHOULD call SetAFactoryPreset if the preset is valid + // from its own list of preset number->name + if (!result) + PropertyChanged(inID, inScope, inElement); + } + else if (newPreset.presetName) + { + result = NewCustomPresetSet(newPreset); + if (!result) + PropertyChanged(inID, inScope, inElement); + } + else + result = kAudioUnitErr_InvalidPropertyValue; + } + break; + + case kAudioUnitProperty_ElementName: + { + ca_require(GetElement(inScope, inElement) != NULL, InvalidElement); + ca_require(inDataSize == sizeof(CFStringRef), InvalidPropertyValue); + AUElement * element = GetScope(inScope).GetElement (inElement); + element->SetName (*(CFStringRef *)inData); + PropertyChanged(inID, inScope, inElement); + } + break; + +#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || TARGET_OS_IPHONE + case kAudioUnitProperty_ShouldAllocateBuffer: + { + ca_require((inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output), InvalidScope); + ca_require(GetElement(inScope, inElement) != NULL, InvalidElement); + ca_require(inDataSize == sizeof(UInt32), InvalidPropertyValue); + ca_require(!IsInitialized(), Initialized); + + AUIOElement * element = GetIOElement(inScope, inElement); + element->SetWillAllocateBuffer(*(UInt32 *)inData != 0); + } + break; +#endif + +#if !CA_NO_AU_HOST_CALLBACKS + case kAudioUnitProperty_HostCallbacks: + { + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + UInt32 availSize = std::min(inDataSize, (UInt32)sizeof(HostCallbackInfo)); + bool hasChanged = !memcmp (&mHostCallbackInfo, inData, availSize); + memset (&mHostCallbackInfo, 0, sizeof (mHostCallbackInfo)); + memcpy (&mHostCallbackInfo, inData, availSize); + if (hasChanged) + PropertyChanged(inID, inScope, inElement); + break; + } +#endif +#if !CA_NO_AU_UI_FEATURES + case kAudioUnitProperty_SetExternalBuffer: + ca_require(inDataSize >= sizeof(AudioUnitExternalBuffer), InvalidPropertyValue); + ca_require(IsInitialized(), Uninitialized); + { + AudioUnitExternalBuffer &buf = *(AudioUnitExternalBuffer*)inData; + if (intptr_t(buf.buffer) & 0x0F) result = kAudio_ParamError; + else if (inScope == kAudioUnitScope_Input) { + AUInputElement *input = GetInput(inElement); + input->UseExternalBuffer(buf); + } else { + AUOutputElement *output = GetOutput(inElement); + output->UseExternalBuffer(buf); + } + } + break; + + case kAudioUnitProperty_ContextName: + { + ca_require(inDataSize == sizeof(CFStringRef), InvalidPropertyValue); + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + CFStringRef inStr = *(CFStringRef *)inData; + if (mContextName) CFRelease(mContextName); + if (inStr) CFRetain(inStr); + mContextName = inStr; + PropertyChanged(inID, inScope, inElement); + } + break; + +#endif // !CA_NO_AU_UI_FEATURES + + case kAudioUnitProperty_NickName: + { + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inDataSize == sizeof(CFStringRef), InvalidPropertyValue); + CFStringRef inStr = *(CFStringRef *)inData; + if (mNickName) CFRelease(mNickName); + if (inStr) CFRetain(inStr); + mNickName = inStr; + PropertyChanged(inID, inScope, inElement); + break; + } + + default: + result = SetProperty(inID, inScope, inElement, inData, inDataSize); + if (result == noErr) + PropertyChanged(inID, inScope, inElement); + + break; + } + return result; +NotWritable: + return kAudioUnitErr_PropertyNotWritable; +InvalidFormat: + return kAudioUnitErr_FormatNotSupported; +#if !CA_NO_AU_UI_FEATURES +Uninitialized: + return kAudioUnitErr_Uninitialized; +#endif +#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || CA_USE_AUDIO_PLUGIN_ONLY +Initialized: + return kAudioUnitErr_Initialized; +#endif +InvalidScope: + return kAudioUnitErr_InvalidScope; +InvalidProperty: + return kAudioUnitErr_InvalidProperty; +InvalidPropertyValue: + return kAudioUnitErr_InvalidPropertyValue; +InvalidElement: + return kAudioUnitErr_InvalidElement; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::DispatchRemovePropertyValue (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement) +{ + OSStatus result = noErr; + switch (inID) + { + case kAudioUnitProperty_AudioChannelLayout: + { + result = RemoveAudioChannelLayout(inScope, inElement); + if (result == noErr) + PropertyChanged(inID, inScope, inElement); + break; + } + +#if !CA_NO_AU_HOST_CALLBACKS + case kAudioUnitProperty_HostCallbacks: + { + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + bool hasValue = false; + void* ptr = &mHostCallbackInfo; + for (unsigned int i = 0; i < sizeof (HostCallbackInfo); ++i) { + if (static_cast<char*>(ptr)[i]) { + hasValue = true; + break; + } + } + if (hasValue) { + memset (&mHostCallbackInfo, 0, sizeof (HostCallbackInfo)); + PropertyChanged(inID, inScope, inElement); + } + break; + } +#endif +#if !CA_NO_AU_UI_FEATURES + case kAudioUnitProperty_ContextName: + if (mContextName) CFRelease(mContextName); + mContextName = NULL; + result = noErr; + break; + +#endif // !CA_NO_AU_UI_FEATURES + + case kAudioUnitProperty_NickName: + { + if(inScope == kAudioUnitScope_Global) { + if (mNickName) CFRelease(mNickName); + mNickName = NULL; + PropertyChanged(inID, inScope, inElement); + } else { + result = kAudioUnitErr_InvalidScope; + } + break; + } + + default: + result = RemovePropertyValue (inID, inScope, inElement); + break; + } + + return result; +#if !CA_NO_AU_UI_FEATURES || !CA_NO_AU_HOST_CALLBACKS +InvalidScope: + return kAudioUnitErr_InvalidScope; +#endif +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::GetPropertyInfo( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable) +{ + return kAudioUnitErr_InvalidProperty; +} + + +//_____________________________________________________________________________ +// +OSStatus AUBase::GetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData) +{ + return kAudioUnitErr_InvalidProperty; +} + + +//_____________________________________________________________________________ +// +OSStatus AUBase::SetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize) +{ + return kAudioUnitErr_InvalidProperty; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::RemovePropertyValue ( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement) +{ + return kAudioUnitErr_InvalidPropertyValue; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::AddPropertyListener( AudioUnitPropertyID inID, + AudioUnitPropertyListenerProc inProc, + void * inProcRefCon) +{ + PropertyListener pl; + + pl.propertyID = inID; + pl.listenerProc = inProc; + pl.listenerRefCon = inProcRefCon; + + if (mPropertyListeners.empty()) + mPropertyListeners.reserve(32); + mPropertyListeners.push_back(pl); + + return noErr; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::RemovePropertyListener( AudioUnitPropertyID inID, + AudioUnitPropertyListenerProc inProc, + void * inProcRefCon, + bool refConSpecified) +{ + // iterate in reverse so that it's safe to erase in the middle of the vector + for (int i = (int)mPropertyListeners.size(); --i >=0; ) { + PropertyListeners::iterator it = mPropertyListeners.begin() + i; + if ((*it).propertyID == inID && (*it).listenerProc == inProc && (!refConSpecified || (*it).listenerRefCon == inProcRefCon)) + mPropertyListeners.erase(it); + } + return noErr; +} + +//_____________________________________________________________________________ +// +void AUBase::PropertyChanged( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement) +{ + for (PropertyListeners::iterator it = mPropertyListeners.begin(); it != mPropertyListeners.end(); ++it) + if ((*it).propertyID == inID) + ((*it).listenerProc)((*it).listenerRefCon, mComponentInstance, inID, inScope, inElement); +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::SetRenderNotification( AURenderCallback inProc, + void * inRefCon) +{ + if (inProc == NULL) + return kAudio_ParamError; + + mRenderCallbacksTouched = true; + mRenderCallbacks.deferred_add(RenderCallback(inProc, inRefCon)); + // this will do nothing if it's already in the list + return noErr; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::RemoveRenderNotification( AURenderCallback inProc, + void * inRefCon) +{ + mRenderCallbacks.deferred_remove(RenderCallback(inProc, inRefCon)); + return noErr; // error? +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::GetParameter( AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + AudioUnitParameterValue & outValue) +{ + AUElement *elem = SafeGetElement(inScope, inElement); + outValue = elem->GetParameter(inID); + return noErr; +} + + +//_____________________________________________________________________________ +// +OSStatus AUBase::SetParameter( AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + AudioUnitParameterValue inValue, + UInt32 inBufferOffsetInFrames) +{ + AUElement *elem = SafeGetElement(inScope, inElement); + elem->SetParameter(inID, inValue); + return noErr; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::ScheduleParameter ( const AudioUnitParameterEvent *inParameterEvent, + UInt32 inNumEvents) +{ + bool canScheduleParameters = CanScheduleParameters(); + + for (UInt32 i = 0; i < inNumEvents; ++i) + { + if (inParameterEvent[i].eventType == kParameterEvent_Immediate) + { + SetParameter (inParameterEvent[i].parameter, + inParameterEvent[i].scope, + inParameterEvent[i].element, + inParameterEvent[i].eventValues.immediate.value, + inParameterEvent[i].eventValues.immediate.bufferOffset); + } + if (canScheduleParameters) { + mParamList.push_back (inParameterEvent[i]); + } + } + + return noErr; +} + +// ____________________________________________________________________________ +// +static bool SortParameterEventList(const AudioUnitParameterEvent &ev1, const AudioUnitParameterEvent &ev2 ) +{ + int offset1 = ev1.eventType == kParameterEvent_Immediate ? ev1.eventValues.immediate.bufferOffset : ev1.eventValues.ramp.startBufferOffset; + int offset2 = ev2.eventType == kParameterEvent_Immediate ? ev2.eventValues.immediate.bufferOffset : ev2.eventValues.ramp.startBufferOffset; + + if(offset1 < offset2) return true; + return false; +} + + +// ____________________________________________________________________________ +// +OSStatus AUBase::ProcessForScheduledParams( ParameterEventList &inParamList, + UInt32 inFramesToProcess, + void *inUserData ) +{ + OSStatus result = noErr; + + int totalFramesToProcess = inFramesToProcess; + + int framesRemaining = totalFramesToProcess; + + unsigned int currentStartFrame = 0; // start of the whole buffer + + + + // sort the ParameterEventList by startBufferOffset + std::sort(inParamList.begin(), inParamList.end(), SortParameterEventList); + + ParameterEventList::iterator iter = inParamList.begin(); + + + while(framesRemaining > 0 ) + { + // first of all, go through the ramped automation events and find out where the next + // division of our whole buffer will be + + int currentEndFrame = totalFramesToProcess; // start out assuming we'll process all the way to + // the end of the buffer + + iter = inParamList.begin(); + + // find the next break point + while(iter != inParamList.end() ) + { + AudioUnitParameterEvent &event = *iter; + + int offset = event.eventType == kParameterEvent_Immediate ? event.eventValues.immediate.bufferOffset : event.eventValues.ramp.startBufferOffset; + + if(offset > (int)currentStartFrame && offset < currentEndFrame ) + { + currentEndFrame = offset; + break; + } + + // consider ramp end to be a possible choice (there may be gaps in the supplied ramp events) + if(event.eventType == kParameterEvent_Ramped ) + { + offset = event.eventValues.ramp.startBufferOffset + event.eventValues.ramp.durationInFrames; + + if(offset > (int)currentStartFrame && offset < currentEndFrame ) + { + currentEndFrame = offset; + } + } + + iter++; + } + + int framesThisTime = currentEndFrame - currentStartFrame; + + // next, setup the parameter maps to be current for the ramp parameters active during + // this time segment... + + for(ParameterEventList::iterator iter2 = inParamList.begin(); iter2 != inParamList.end(); iter2++ ) + { + AudioUnitParameterEvent &event = *iter2; + + bool eventFallsInSlice; + + + if(event.eventType == kParameterEvent_Ramped) + eventFallsInSlice = event.eventValues.ramp.startBufferOffset < currentEndFrame + && event.eventValues.ramp.startBufferOffset + event.eventValues.ramp.durationInFrames > currentStartFrame; + else /* kParameterEvent_Immediate */ + // actually, for the same parameter, there may be future immediate events which override this one, + // but it's OK since the event list is sorted in time order, we're guaranteed to end up with the current one + eventFallsInSlice = event.eventValues.immediate.bufferOffset <= currentStartFrame; + + if(eventFallsInSlice) + { + AUElement *element = GetElement(event.scope, event.element ); + + if(element) element->SetScheduledEvent( event.parameter, + event, + currentStartFrame, + currentEndFrame - currentStartFrame ); + } + } + + + + // Finally, actually do the processing for this slice..... + + result = ProcessScheduledSlice( inUserData, + currentStartFrame, + framesThisTime, + inFramesToProcess ); + + if(result != noErr) break; + + framesRemaining -= framesThisTime; + currentStartFrame = currentEndFrame; // now start from where we left off last time + } + + return result; +} + +//_____________________________________________________________________________ +// +void AUBase::SetWantsRenderThreadID (bool inFlag) +{ + if (inFlag == mWantsRenderThreadID) + return; + + mWantsRenderThreadID = inFlag; + if (!mWantsRenderThreadID) + mRenderThreadID = NULL; +} + +//_____________________________________________________________________________ +// + +//_____________________________________________________________________________ +// +OSStatus AUBase::DoRender( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inBusNumber, + UInt32 inFramesToProcess, + AudioBufferList & ioData) +{ + OSStatus theError; + RenderCallbackList::iterator rcit; + + AUTRACE(kCATrace_AUBaseRenderStart, mComponentInstance, (uintptr_t)this, inBusNumber, inFramesToProcess, (uintptr_t)ioData.mBuffers[0].mData); + DISABLE_DENORMALS + + try { + ca_require(IsInitialized(), Uninitialized); + ca_require(mAudioUnitAPIVersion >= 2, ParamErr); + if (inFramesToProcess > mMaxFramesPerSlice) { + static time_t lastTimeMessagePrinted = 0; + time_t now = time(NULL); + if (now != lastTimeMessagePrinted) { + lastTimeMessagePrinted = now; + syslog(LOG_ERR, "kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=%u, mMaxFramesPerSlice=%u", (unsigned)inFramesToProcess, (unsigned)mMaxFramesPerSlice); + DebugMessageN4("%s:%d inFramesToProcess=%u, mMaxFramesPerSlice=%u; TooManyFrames", __FILE__, __LINE__, (unsigned)inFramesToProcess, (unsigned)mMaxFramesPerSlice); + } + goto TooManyFrames; + } + ca_require (!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(), ParamErr); + + AUOutputElement *output = GetOutput(inBusNumber); // will throw if non-existant + if (output->GetStreamFormat().NumberChannelStreams() != ioData.mNumberBuffers) { + DebugMessageN4("%s:%d ioData.mNumberBuffers=%u, output->GetStreamFormat().NumberChannelStreams()=%u; kAudio_ParamError", + __FILE__, __LINE__, (unsigned)ioData.mNumberBuffers, (unsigned)output->GetStreamFormat().NumberChannelStreams()); + goto ParamErr; + } + + unsigned expectedBufferByteSize = inFramesToProcess * output->GetStreamFormat().mBytesPerFrame; + for (unsigned ibuf = 0; ibuf < ioData.mNumberBuffers; ++ibuf) { + AudioBuffer &buf = ioData.mBuffers[ibuf]; + if (buf.mData != NULL) { + // only care about the size if the buffer is non-null + if (buf.mDataByteSize < expectedBufferByteSize) { + // if the buffer is too small, we cannot render safely. kAudio_ParamError. + DebugMessageN7("%s:%d %u frames, %u bytes/frame, expected %u-byte buffer; ioData.mBuffers[%u].mDataByteSize=%u; kAudio_ParamError", + __FILE__, __LINE__, (unsigned)inFramesToProcess, (unsigned)output->GetStreamFormat().mBytesPerFrame, expectedBufferByteSize, ibuf, (unsigned)buf.mDataByteSize); + goto ParamErr; + } + // Some clients incorrectly pass bigger buffers than expectedBufferByteSize. + // We will generally set the buffer size at the end of rendering, before we return. + // However we should ensure that no one, DURING rendering, READS a + // potentially incorrect size. This can lead to doing too much work, or + // reading past the end of an input buffer into unmapped memory. + buf.mDataByteSize = expectedBufferByteSize; + } + } + + if (WantsRenderThreadID()) + { + #if TARGET_OS_MAC + mRenderThreadID = pthread_self(); + #elif TARGET_OS_WIN32 + mRenderThreadID = GetCurrentThreadId(); + #endif + } + + AudioUnitRenderActionFlags flags; + if (mRenderCallbacksTouched) { + mRenderCallbacks.update(); + flags = ioActionFlags | kAudioUnitRenderAction_PreRender; + for (rcit = mRenderCallbacks.begin(); rcit != mRenderCallbacks.end(); ++rcit) { + RenderCallback &rc = *rcit; + AUTRACE(kCATrace_AUBaseRenderCallbackStart, mComponentInstance, (intptr_t)this, (intptr_t)rc.mRenderNotify, 1, 0); + (*(AURenderCallback)rc.mRenderNotify)(rc.mRenderNotifyRefCon, + &flags, + &inTimeStamp, inBusNumber, inFramesToProcess, &ioData); + AUTRACE(kCATrace_AUBaseRenderCallbackEnd, mComponentInstance, (intptr_t)this, (intptr_t)rc.mRenderNotify, 1, 0); + } + } + + theError = DoRenderBus(ioActionFlags, inTimeStamp, inBusNumber, output, inFramesToProcess, ioData); + + if (mRenderCallbacksTouched) { + flags = ioActionFlags | kAudioUnitRenderAction_PostRender; + + if (SetRenderError (theError)) { + flags |= kAudioUnitRenderAction_PostRenderError; + } + + for (rcit = mRenderCallbacks.begin(); rcit != mRenderCallbacks.end(); ++rcit) { + RenderCallback &rc = *rcit; + AUTRACE(kCATrace_AUBaseRenderCallbackStart, mComponentInstance, (intptr_t)this, (intptr_t)rc.mRenderNotify, 2, 0); + (*(AURenderCallback)rc.mRenderNotify)(rc.mRenderNotifyRefCon, + &flags, + &inTimeStamp, inBusNumber, inFramesToProcess, &ioData); + AUTRACE(kCATrace_AUBaseRenderCallbackEnd, mComponentInstance, (intptr_t)this, (intptr_t)rc.mRenderNotify, 2, 0); + } + } + + // The vector's being emptied + // because these events should only apply to this Render cycle, so anything + // left over is from a preceding cycle and should be dumped. New scheduled + // parameters must be scheduled from the next pre-render callback. + if (!mParamList.empty()) + mParamList.clear(); + + } + catch (OSStatus err) { + theError = err; + goto errexit; + } + catch (...) { + theError = -1; + goto errexit; + } +done: + RESTORE_DENORMALS + AUTRACE(kCATrace_AUBaseRenderEnd, mComponentInstance, (intptr_t)this, theError, ioActionFlags, CATrace::ablData(ioData)); + + return theError; + +Uninitialized: theError = kAudioUnitErr_Uninitialized; goto errexit; +ParamErr: theError = kAudio_ParamError; goto errexit; +TooManyFrames: theError = kAudioUnitErr_TooManyFramesToProcess; goto errexit; +errexit: + DebugMessageN2 (" from %s, render err: %d", GetLoggingString(), (int)theError); + SetRenderError(theError); + goto done; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::DoProcess ( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inFramesToProcess, + AudioBufferList & ioData) +{ + OSStatus theError; + AUTRACE(kCATrace_AUBaseRenderStart, mComponentInstance, (intptr_t)this, -1, inFramesToProcess, 0); + DISABLE_DENORMALS + + try { + + if (!(ioActionFlags & (1 << 9)/*kAudioUnitRenderAction_DoNotCheckRenderArgs*/)) { + ca_require(IsInitialized(), Uninitialized); + ca_require(inFramesToProcess <= mMaxFramesPerSlice, TooManyFrames); + ca_require(!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(), ParamErr); + + AUInputElement *input = GetInput(0); // will throw if non-existant + if (input->GetStreamFormat().NumberChannelStreams() != ioData.mNumberBuffers) { + DebugMessageN4("%s:%d ioData.mNumberBuffers=%u, input->GetStreamFormat().NumberChannelStreams()=%u; kAudio_ParamError", + __FILE__, __LINE__, (unsigned)ioData.mNumberBuffers, (unsigned)input->GetStreamFormat().NumberChannelStreams()); + goto ParamErr; + } + + unsigned expectedBufferByteSize = inFramesToProcess * input->GetStreamFormat().mBytesPerFrame; + for (unsigned ibuf = 0; ibuf < ioData.mNumberBuffers; ++ibuf) { + AudioBuffer &buf = ioData.mBuffers[ibuf]; + if (buf.mData != NULL) { + // only care about the size if the buffer is non-null + if (buf.mDataByteSize < expectedBufferByteSize) { + // if the buffer is too small, we cannot render safely. kAudio_ParamError. + DebugMessageN7("%s:%d %u frames, %u bytes/frame, expected %u-byte buffer; ioData.mBuffers[%u].mDataByteSize=%u; kAudio_ParamError", + __FILE__, __LINE__, (unsigned)inFramesToProcess, (unsigned)input->GetStreamFormat().mBytesPerFrame, expectedBufferByteSize, ibuf, (unsigned)buf.mDataByteSize); + goto ParamErr; + } + // Some clients incorrectly pass bigger buffers than expectedBufferByteSize. + // We will generally set the buffer size at the end of rendering, before we return. + // However we should ensure that no one, DURING rendering, READS a + // potentially incorrect size. This can lead to doing too much work, or + // reading past the end of an input buffer into unmapped memory. + buf.mDataByteSize = expectedBufferByteSize; + } + } + } + + if (WantsRenderThreadID()) + { + #if TARGET_OS_MAC + mRenderThreadID = pthread_self(); + #elif TARGET_OS_WIN32 + mRenderThreadID = GetCurrentThreadId(); + #endif + } + + if (NeedsToRender (inTimeStamp)) { + theError = ProcessBufferLists (ioActionFlags, ioData, ioData, inFramesToProcess); + } else + theError = noErr; + + } + catch (OSStatus err) { + theError = err; + goto errexit; + } + catch (...) { + theError = -1; + goto errexit; + } +done: + RESTORE_DENORMALS + AUTRACE(kCATrace_AUBaseRenderEnd, mComponentInstance, (intptr_t)this, theError, ioActionFlags, CATrace::ablData(ioData)); + + return theError; + +Uninitialized: theError = kAudioUnitErr_Uninitialized; goto errexit; +ParamErr: theError = kAudio_ParamError; goto errexit; +TooManyFrames: theError = kAudioUnitErr_TooManyFramesToProcess; goto errexit; +errexit: + DebugMessageN2 (" from %s, process err: %d", GetLoggingString(), (int)theError); + SetRenderError(theError); + goto done; +} + +OSStatus AUBase::DoProcessMultiple ( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inFramesToProcess, + UInt32 inNumberInputBufferLists, + const AudioBufferList ** inInputBufferLists, + UInt32 inNumberOutputBufferLists, + AudioBufferList ** ioOutputBufferLists) +{ + OSStatus theError; + DISABLE_DENORMALS + + try { + + if (!(ioActionFlags & (1 << 9)/*kAudioUnitRenderAction_DoNotCheckRenderArgs*/)) { + ca_require(IsInitialized(), Uninitialized); + ca_require(inFramesToProcess <= mMaxFramesPerSlice, TooManyFrames); + ca_require (!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(), ParamErr); + + for (unsigned ibl = 0; ibl < inNumberInputBufferLists; ++ibl) { + if (inInputBufferLists[ibl] != NULL) { + AUInputElement *input = GetInput(ibl); // will throw if non-existant + unsigned expectedBufferByteSize = inFramesToProcess * input->GetStreamFormat().mBytesPerFrame; + + if (input->GetStreamFormat().NumberChannelStreams() != inInputBufferLists[ibl]->mNumberBuffers) { + DebugMessageN5("%s:%d inInputBufferLists[%u]->mNumberBuffers=%u, input->GetStreamFormat().NumberChannelStreams()=%u; kAudio_ParamError", + __FILE__, __LINE__, ibl, (unsigned)inInputBufferLists[ibl]->mNumberBuffers, (unsigned)input->GetStreamFormat().NumberChannelStreams()); + goto ParamErr; + } + + for (unsigned ibuf = 0; ibuf < inInputBufferLists[ibl]->mNumberBuffers; ++ibuf) { + const AudioBuffer &buf = inInputBufferLists[ibl]->mBuffers[ibuf]; + if (buf.mData != NULL) { + if (buf.mDataByteSize < expectedBufferByteSize) { + // the buffer is too small + DebugMessageN8("%s:%d %u frames, %u bytes/frame, expected %u-byte buffer; inInputBufferLists[%u].mBuffers[%u].mDataByteSize=%u; kAudio_ParamError", + __FILE__, __LINE__, (unsigned)inFramesToProcess, (unsigned)input->GetStreamFormat().mBytesPerFrame, expectedBufferByteSize, ibl, ibuf, (unsigned)buf.mDataByteSize); + goto ParamErr; + } + } else { + // the buffer must exist + goto ParamErr; + } + } + } else { + // skip NULL input audio buffer list + } + } + + for (unsigned obl = 0; obl < inNumberOutputBufferLists; ++obl) { + if (ioOutputBufferLists[obl] != NULL) { + AUOutputElement *output = GetOutput(obl); // will throw if non-existant + unsigned expectedBufferByteSize = inFramesToProcess * output->GetStreamFormat().mBytesPerFrame; + + if (output->GetStreamFormat().NumberChannelStreams() != ioOutputBufferLists[obl]->mNumberBuffers) { + DebugMessageN5("%s:%d ioOutputBufferLists[%u]->mNumberBuffers=%u, output->GetStreamFormat().NumberChannelStreams()=%u; kAudio_ParamError", + __FILE__, __LINE__, obl, (unsigned)ioOutputBufferLists[obl]->mNumberBuffers, (unsigned)output->GetStreamFormat().NumberChannelStreams()); + goto ParamErr; + } + + for (unsigned obuf = 0; obuf < ioOutputBufferLists[obl]->mNumberBuffers; ++obuf) { + AudioBuffer &buf = ioOutputBufferLists[obl]->mBuffers[obuf]; + if (buf.mData != NULL) { + // only care about the size if the buffer is non-null + if (buf.mDataByteSize < expectedBufferByteSize) { + // if the buffer is too small, we cannot render safely. kAudio_ParamError. + DebugMessageN8("%s:%d %u frames, %u bytes/frame, expected %u-byte buffer; ioOutputBufferLists[%u]->mBuffers[%u].mDataByteSize=%u; kAudio_ParamError", + __FILE__, __LINE__, (unsigned)inFramesToProcess, (unsigned)output->GetStreamFormat().mBytesPerFrame, expectedBufferByteSize, obl, obuf, (unsigned)buf.mDataByteSize); + goto ParamErr; + } + // Some clients incorrectly pass bigger buffers than expectedBufferByteSize. + // We will generally set the buffer size at the end of rendering, before we return. + // However we should ensure that no one, DURING rendering, READS a + // potentially incorrect size. This can lead to doing too much work, or + // reading past the end of an input buffer into unmapped memory. + buf.mDataByteSize = expectedBufferByteSize; + } + } + } else { + // skip NULL output audio buffer list + } + } + } + + if (WantsRenderThreadID()) + { +#if TARGET_OS_MAC + mRenderThreadID = pthread_self(); +#elif TARGET_OS_WIN32 + mRenderThreadID = GetCurrentThreadId(); +#endif + } + + if (NeedsToRender (inTimeStamp)) { + theError = ProcessMultipleBufferLists (ioActionFlags, inFramesToProcess, inNumberInputBufferLists, inInputBufferLists, inNumberOutputBufferLists, ioOutputBufferLists); + } else + theError = noErr; + } + catch (OSStatus err) { + theError = err; + goto errexit; + } + catch (...) { + theError = -1; + goto errexit; + } +done: + RESTORE_DENORMALS + + return theError; + +Uninitialized: theError = kAudioUnitErr_Uninitialized; goto errexit; +ParamErr: theError = kAudio_ParamError; goto errexit; +TooManyFrames: theError = kAudioUnitErr_TooManyFramesToProcess; goto errexit; +errexit: + DebugMessageN2 (" from %s, processmultiple err: %d", GetLoggingString(), (int)theError); + SetRenderError(theError); + goto done; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::SetInputCallback( UInt32 inPropertyID, + AudioUnitElement inElement, + AURenderCallback inProc, + void * inRefCon) +{ + AUInputElement *input = GetInput(inElement); // may throw + + input->SetInputCallback(inProc, inRefCon); + PropertyChanged(inPropertyID, kAudioUnitScope_Input, inElement); + + return noErr; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::SetConnection( const AudioUnitConnection & inConnection) +{ + + OSStatus err; + AUInputElement *input = GetInput(inConnection.destInputNumber); // may throw + + if (inConnection.sourceAudioUnit) { + // connecting, not disconnecting + CAStreamBasicDescription sourceDesc; + UInt32 size = sizeof(CAStreamBasicDescription); + ca_require_noerr(err = AudioUnitGetProperty( + inConnection.sourceAudioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + inConnection.sourceOutputNumber, + &sourceDesc, + &size), errexit); + ca_require_noerr(err = DispatchSetProperty (kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, inConnection.destInputNumber, + &sourceDesc, sizeof(CAStreamBasicDescription)), errexit); + } + input->SetConnection(inConnection); + + PropertyChanged(kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, inConnection.destInputNumber); + return noErr; + +errexit: + return err; +} + +//_____________________________________________________________________________ +// +UInt32 AUBase::SupportedNumChannels ( const AUChannelInfo** outInfo) +{ + return 0; +} + +//_____________________________________________________________________________ +// +bool AUBase::ValidFormat( AudioUnitScope inScope, + AudioUnitElement inElement, + const CAStreamBasicDescription & inNewFormat) +{ + return FormatIsCanonical(inNewFormat); +} + +//_____________________________________________________________________________ +// +bool AUBase::IsStreamFormatWritable( AudioUnitScope scope, + AudioUnitElement element) +{ + switch (scope) { + case kAudioUnitScope_Input: + { + AUInputElement *input = GetInput(element); + if (input->HasConnection()) return false; // can't write format when input comes from connection + } + // ... fall ... + case kAudioUnitScope_Output: + return StreamFormatWritable(scope, element); + +//#warning "aliasing of global scope format should be pushed to subclasses" + case kAudioUnitScope_Global: + return StreamFormatWritable(kAudioUnitScope_Output, 0); + } + return false; +} + +//_____________________________________________________________________________ +// +const CAStreamBasicDescription & + AUBase::GetStreamFormat( AudioUnitScope inScope, + AudioUnitElement inElement) +{ +//#warning "aliasing of global scope format should be pushed to subclasses" + AUIOElement *element; + + switch (inScope) { + case kAudioUnitScope_Input: + element = Inputs().GetIOElement(inElement); + break; + case kAudioUnitScope_Output: + element = Outputs().GetIOElement(inElement); + break; + case kAudioUnitScope_Global: // global stream description is an alias for that of output 0 + element = Outputs().GetIOElement(0); + break; + default: + COMPONENT_THROW(kAudioUnitErr_InvalidScope); + } + return element->GetStreamFormat(); +} + +OSStatus AUBase::SetBusCount( AudioUnitScope inScope, + UInt32 inCount) +{ + if (IsInitialized()) + return kAudioUnitErr_Initialized; + + GetScope(inScope).SetNumberOfElements(inCount); + return noErr; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::ChangeStreamFormat( AudioUnitScope inScope, + AudioUnitElement inElement, + const CAStreamBasicDescription & inPrevFormat, + const CAStreamBasicDescription & inNewFormat) +{ +//#warning "aliasing of global scope format should be pushed to subclasses" + AUIOElement *element; + + switch (inScope) { + case kAudioUnitScope_Input: + element = Inputs().GetIOElement(inElement); + break; + case kAudioUnitScope_Output: + element = Outputs().GetIOElement(inElement); + break; + case kAudioUnitScope_Global: + element = Outputs().GetIOElement(0); + break; + default: + COMPONENT_THROW(kAudioUnitErr_InvalidScope); + } + element->SetStreamFormat(inNewFormat); + PropertyChanged(kAudioUnitProperty_StreamFormat, inScope, inElement); + return noErr; +} + +UInt32 AUBase::GetChannelLayoutTags( AudioUnitScope inScope, + AudioUnitElement inElement, + AudioChannelLayoutTag * outLayoutTags) +{ + return GetIOElement(inScope, inElement)->GetChannelLayoutTags(outLayoutTags); +} + +UInt32 AUBase::GetAudioChannelLayout( AudioUnitScope scope, + AudioUnitElement element, + AudioChannelLayout * outLayoutPtr, + Boolean & outWritable) +{ + AUIOElement * el = GetIOElement(scope, element); + return el->GetAudioChannelLayout(outLayoutPtr, outWritable); +} + +OSStatus AUBase::RemoveAudioChannelLayout( AudioUnitScope inScope, + AudioUnitElement inElement) +{ + OSStatus result = noErr; + AUIOElement * el = GetIOElement(inScope, inElement); + Boolean writable; + if (el->GetAudioChannelLayout(NULL, writable)) { + result = el->RemoveAudioChannelLayout(); + } + return result; +} + +OSStatus AUBase::SetAudioChannelLayout( AudioUnitScope inScope, + AudioUnitElement inElement, + const AudioChannelLayout * inLayout) +{ + AUIOElement* ioEl = GetIOElement (inScope, inElement); + + // the num channels of the layout HAS TO MATCH the current channels of the Element's stream format + UInt32 currentChannels = ioEl->GetStreamFormat().NumberChannels(); + UInt32 numChannelsInLayout = CAAudioChannelLayout::NumberChannels(*inLayout); + if (currentChannels != numChannelsInLayout) + return kAudioUnitErr_InvalidPropertyValue; + + UInt32 numLayouts = GetChannelLayoutTags (inScope, inElement, NULL); + if (numLayouts == 0) + return kAudioUnitErr_InvalidProperty; + AudioChannelLayoutTag *tags = (AudioChannelLayoutTag *)CA_malloc (numLayouts * sizeof (AudioChannelLayoutTag)); + GetChannelLayoutTags (inScope, inElement, tags); + bool foundTag = false; + for (unsigned int i = 0; i < numLayouts; ++i) { + if (tags[i] == inLayout->mChannelLayoutTag || tags[i] == kAudioChannelLayoutTag_UseChannelDescriptions) { + foundTag = true; + break; + } + } + free(tags); + + if (foundTag == false) + return kAudioUnitErr_InvalidPropertyValue; + + return ioEl->SetAudioChannelLayout(*inLayout); +} + +static void AddNumToDictionary (CFMutableDictionaryRef dict, CFStringRef key, SInt32 value) +{ + CFNumberRef num = CFNumberCreate (NULL, kCFNumberSInt32Type, &value); + CFDictionarySetValue (dict, key, num); + CFRelease (num); +} + +#define kCurrentSavedStateVersion 0 + +OSStatus AUBase::SaveState( CFPropertyListRef * outData) +{ + AudioComponentDescription desc = GetComponentDescription(); + + CFMutableDictionaryRef dict = CFDictionaryCreateMutable (NULL, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + +// first step -> save the version to the data ref + SInt32 value = kCurrentSavedStateVersion; + AddNumToDictionary (dict, kVersionString, value); + +// second step -> save the component type, subtype, manu to the data ref + value = desc.componentType; + AddNumToDictionary (dict, kTypeString, value); + + value = desc.componentSubType; + AddNumToDictionary (dict, kSubtypeString, value); + + value = desc.componentManufacturer; + AddNumToDictionary (dict, kManufacturerString, value); + +// fourth step -> save the state of all parameters on all scopes and elements + CFMutableDataRef data = CFDataCreateMutable(NULL, 0); + for (AudioUnitScope iscope = 0; iscope < 3; ++iscope) { + AUScope &scope = GetScope(iscope); + scope.SaveState (data); + } + + SaveExtendedScopes(data); + +// save all this in the data section of the dictionary + CFDictionarySetValue(dict, kDataString, data); + CFRelease (data); + +//OK - now we're going to do some properties +//save the preset name... + CFDictionarySetValue (dict, kNameString, mCurrentPreset.presetName); + +// Does the unit support the RenderQuality property - if so, save it... + value = 0; + OSStatus result = DispatchGetProperty (kAudioUnitProperty_RenderQuality, + kAudioUnitScope_Global, + 0, + &value); + + if (result == noErr) { + AddNumToDictionary (dict, kRenderQualityString, value); + } + +// Does the unit support the CPULoad Quality property - if so, save it... + Float32 cpuLoad; + result = DispatchGetProperty (6/*kAudioUnitProperty_CPULoad*/, + kAudioUnitScope_Global, + 0, + &cpuLoad); + + if (result == noErr) { + CFNumberRef num = CFNumberCreate (NULL, kCFNumberFloatType, &cpuLoad); + CFDictionarySetValue (dict, kCPULoadString, num); + CFRelease (num); + } + +// Do we have any element names for any of our scopes? + // first check to see if we have any names... + bool foundName = false; + for (AudioUnitScope i = 0; i < kNumScopes; ++i) { + foundName = GetScope (i).HasElementWithName(); + if (foundName) + break; + } + // OK - we found a name away we go... + if (foundName) { + CFMutableDictionaryRef nameDict = CFDictionaryCreateMutable (NULL, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + for (AudioUnitScope i = 0; i < kNumScopes; ++i) { + GetScope (i).AddElementNamesToDict (nameDict); + } + + CFDictionarySetValue (dict, kElementNameString, nameDict); + CFRelease (nameDict); + } + +// we're done!!! + *outData = dict; + + return noErr; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::RestoreState( CFPropertyListRef plist) +{ + if (CFGetTypeID(plist) != CFDictionaryGetTypeID()) return kAudioUnitErr_InvalidPropertyValue; + + AudioComponentDescription desc = GetComponentDescription(); + + CFDictionaryRef dict = static_cast<CFDictionaryRef>(plist); + +// zeroeth step - make sure the Part key is NOT present, as this method is used +// to restore the GLOBAL state of the dictionary + if (CFDictionaryContainsKey (dict, kPartString)) + return kAudioUnitErr_InvalidPropertyValue; + +// first step -> check the saved version in the data ref +// at this point we're only dealing with version==0 + CFNumberRef cfnum = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue (dict, kVersionString)); + if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue; + SInt32 value; + CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value); + if (value != kCurrentSavedStateVersion) return kAudioUnitErr_InvalidPropertyValue; + +// second step -> check that this data belongs to this kind of audio unit +// by checking the component subtype and manuID +// We're not checking the type, since there may be different versions (effect, format-converter, offline) +// of essentially the same AU + cfnum = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue (dict, kSubtypeString)); + if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue; + CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value); + if (UInt32(value) != desc.componentSubType) return kAudioUnitErr_InvalidPropertyValue; + + cfnum = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue (dict, kManufacturerString)); + if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue; + CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value); + if (UInt32(value) != desc.componentManufacturer) return kAudioUnitErr_InvalidPropertyValue; + +// fourth step -> restore the state of all of the parameters for each scope and element + CFDataRef data = reinterpret_cast<CFDataRef>(CFDictionaryGetValue (dict, kDataString)); + if (data != NULL) + { + const UInt8 *p, *pend; + + p = CFDataGetBytePtr(data); + pend = p + CFDataGetLength(data); + + // we have a zero length data, which may just mean there were no parameters to save! + // if (p >= pend) return noErr; + + while (p < pend) { + UInt32 scopeIdx = CFSwapInt32BigToHost(*(UInt32 *)p); + p += sizeof(UInt32); + + AUScope &scope = GetScope(scopeIdx); + p = scope.RestoreState(p); + } + } + +//OK - now we're going to do some properties +//restore the preset name... + CFStringRef name = reinterpret_cast<CFStringRef>(CFDictionaryGetValue (dict, kNameString)); + if (mCurrentPreset.presetName) CFRelease (mCurrentPreset.presetName); + if (name) + { + mCurrentPreset.presetName = name; + mCurrentPreset.presetNumber = -1; + } + else { // no name entry make the default one + mCurrentPreset.presetName = kUntitledString; + mCurrentPreset.presetNumber = -1; + } + + CFRetain (mCurrentPreset.presetName); +#if !CA_USE_AUDIO_PLUGIN_ONLY +#ifndef __LP64__ + PropertyChanged(kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global, 0); +#endif +#endif + PropertyChanged(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0); + +// Does the dict contain render quality information? + if (CFDictionaryGetValueIfPresent (dict, kRenderQualityString, reinterpret_cast<const void**>(&cfnum))) + { + CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value); + DispatchSetProperty (kAudioUnitProperty_RenderQuality, + kAudioUnitScope_Global, + 0, + &value, + sizeof(value)); + } + +// Does the unit support the CPULoad Quality property - if so, save it... + if (CFDictionaryGetValueIfPresent (dict, kCPULoadString, reinterpret_cast<const void**>(&cfnum))) + { + Float32 floatValue; + CFNumberGetValue (cfnum, kCFNumberFloatType, &floatValue); + DispatchSetProperty (6/*kAudioUnitProperty_CPULoad*/, + kAudioUnitScope_Global, + 0, + &floatValue, + sizeof(floatValue)); + } + +// Do we have any element names for any of our scopes? + CFDictionaryRef nameDict; + if (CFDictionaryGetValueIfPresent (dict, kElementNameString, reinterpret_cast<const void**>(&nameDict))) + { + char string[64]; + for (int i = 0; i < kNumScopes; ++i) + { + snprintf (string, sizeof(string), "%d", i); + CFStringRef key = CFStringCreateWithCString (NULL, string, kCFStringEncodingASCII); + CFDictionaryRef elementDict; + if (CFDictionaryGetValueIfPresent (nameDict, key, reinterpret_cast<const void**>(&elementDict))) + { + bool didAddElements = GetScope (i).RestoreElementNames (elementDict); + if (didAddElements) + PropertyChanged (kAudioUnitProperty_ElementCount, i, 0); + } + CFRelease (key); + } + } + + return noErr; +} + +OSStatus AUBase::GetPresets ( CFArrayRef * outData) const +{ + return kAudioUnitErr_InvalidProperty; +} + +OSStatus AUBase::NewFactoryPresetSet (const AUPreset & inNewFactoryPreset) +{ + return kAudioUnitErr_InvalidProperty; +} + +OSStatus AUBase::NewCustomPresetSet (const AUPreset & inNewCustomPreset) +{ + CFRelease (mCurrentPreset.presetName); + mCurrentPreset = inNewCustomPreset; + CFRetain (mCurrentPreset.presetName); + return noErr; +} + + // set the default preset for the unit -> the number of the preset MUST be >= 0 + // and the name should be valid, or the preset WON'T take +bool AUBase::SetAFactoryPresetAsCurrent (const AUPreset & inPreset) +{ + if (inPreset.presetNumber < 0 || inPreset.presetName == NULL) return false; + CFRelease (mCurrentPreset.presetName); + mCurrentPreset = inPreset; + CFRetain (mCurrentPreset.presetName); + return true; +} + +#if !CA_USE_AUDIO_PLUGIN_ONLY +int AUBase::GetNumCustomUIComponents () +{ + return 0; +} + +void AUBase::GetUIComponentDescs (ComponentDescription* inDescArray) {} +#endif + +bool AUBase::HasIcon () +{ +#if !CA_NO_AU_UI_FEATURES + CFURLRef url = CopyIconLocation(); + if (url) { + CFRelease (url); + return true; + } +#endif + return false; +} + +CFURLRef AUBase::CopyIconLocation () +{ + return NULL; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::GetParameterList( AudioUnitScope inScope, + AudioUnitParameterID * outParameterList, + UInt32 & outNumParameters) +{ + AUScope &scope = GetScope(inScope); + AUElement *elementWithMostParameters = NULL; + UInt32 maxNumParams = 0; + + int nElems = scope.GetNumberOfElements(); + for (int ielem = 0; ielem < nElems; ++ielem) { + AUElement *element = scope.GetElement(ielem); + UInt32 nParams = element->GetNumberOfParameters(); + if (nParams > maxNumParams) { + maxNumParams = nParams; + elementWithMostParameters = element; + } + } + + if (outParameterList != NULL && elementWithMostParameters != NULL) + elementWithMostParameters->GetParameterList(outParameterList); + + outNumParameters = maxNumParams; + return noErr; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::GetParameterInfo( AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + AudioUnitParameterInfo &outParameterInfo ) +{ + return kAudioUnitErr_InvalidParameter; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::GetParameterValueStrings(AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + CFArrayRef * outStrings) +{ + return kAudioUnitErr_InvalidProperty; +} + +//_____________________________________________________________________________ +// +OSStatus AUBase::GetParameterHistoryInfo( AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + Float32 & outUpdatesPerSecond, + Float32 & outHistoryDurationInSeconds) +{ + return kAudioUnitErr_InvalidProperty; +} + + +//_____________________________________________________________________________ +// +OSStatus AUBase::CopyClumpName( AudioUnitScope inScope, + UInt32 inClumpID, + UInt32 inDesiredNameLength, + CFStringRef * outClumpName) +{ + return kAudioUnitErr_InvalidProperty; +} + +//_____________________________________________________________________________ +// +void AUBase::SetNumberOfElements( AudioUnitScope inScope, + UInt32 numElements) +{ + if (inScope == kAudioUnitScope_Global && numElements != 1) + COMPONENT_THROW(kAudioUnitErr_InvalidScope); + + GetScope(inScope).SetNumberOfElements(numElements); +} + +//_____________________________________________________________________________ +// +AUElement * AUBase::CreateElement( AudioUnitScope scope, + AudioUnitElement element) +{ + switch (scope) { + case kAudioUnitScope_Global: + return new AUElement(this); + case kAudioUnitScope_Input: + return new AUInputElement(this); + case kAudioUnitScope_Output: + return new AUOutputElement(this); +#if !CA_BASIC_AU_FEATURES + case kAudioUnitScope_Group: + return new AUElement(this); + case kAudioUnitScope_Part: + return new AUElement(this); +#endif + } + COMPONENT_THROW(kAudioUnitErr_InvalidScope); + + return NULL; // get rid of compiler warning +} + +//_____________________________________________________________________________ +// +bool AUBase::FormatIsCanonical( const CAStreamBasicDescription &f) +{ + return (f.mFormatID == kAudioFormatLinearPCM + && f.mFramesPerPacket == 1 + && f.mBytesPerPacket == f.mBytesPerFrame +// && f.mChannelsPerFrame >= 0 -- this is always true since it's unsigned + // so far, it's a valid PCM format +#if CA_PREFER_FIXED_POINT + && (f.mFormatFlags & kLinearPCMFormatFlagIsFloat) == 0 + && (((f.mFormatFlags & kLinearPCMFormatFlagsSampleFractionMask) >> kLinearPCMFormatFlagsSampleFractionShift) == kAudioUnitSampleFractionBits) +#else + && (f.mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0 +#endif + && ((f.mChannelsPerFrame == 1) || ((f.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0) == (mAudioUnitAPIVersion == 1)) +#if TARGET_RT_BIG_ENDIAN + && (f.mFormatFlags & kLinearPCMFormatFlagIsBigEndian) != 0 +#else + && (f.mFormatFlags & kLinearPCMFormatFlagIsBigEndian) == 0 +#endif + && f.mBitsPerChannel == 8 * sizeof(AudioUnitSampleType) + && f.mBytesPerFrame == f.NumberInterleavedChannels() * sizeof(AudioUnitSampleType) + ); +} + +//_____________________________________________________________________________ +// +void AUBase::MakeCanonicalFormat( CAStreamBasicDescription & f, + int nChannels) +{ + f.SetAUCanonical(nChannels, mAudioUnitAPIVersion < 2); // interleaved for v1, non for v2 + f.mSampleRate = 0.0; +} + +const Float64 AUBase::kNoLastRenderedSampleTime = -1.; + +#include "AUBaseHelper.h" + +char* AUBase::GetLoggingString () const +{ + if (mLogString) return mLogString; + + AudioComponentDescription desc = GetComponentDescription(); + + const size_t logStringSize = 256; + const_cast<AUBase*>(this)->mLogString = new char[logStringSize]; + char str[24]; + char str1[24]; + char str2[24]; + snprintf (const_cast<AUBase*>(this)->mLogString, logStringSize, "AU (%p): %s %s %s", + GetComponentInstance(), + CAStringForOSType(desc.componentType, str, sizeof(str)), + CAStringForOSType(desc.componentSubType, str1, sizeof(str1)), + CAStringForOSType(desc.componentManufacturer, str2, sizeof(str2))); + + return mLogString; +} + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUBase.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUBase.h new file mode 100644 index 0000000000..0c78221cc8 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUBase.h @@ -0,0 +1,1048 @@ +/* + File: AUBase.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUBase_h__ +#define __AUBase_h__ + +#include <TargetConditionals.h> + +#if TARGET_OS_MAC + #include <pthread.h> +#elif TARGET_OS_WIN32 + #include <windows.h> +#else + #error Unsupported Operating System +#endif + +#include <vector> + +#include "AUScopeElement.h" +#include "AUInputElement.h" +#include "AUOutputElement.h" +#include "AUBuffer.h" +#include "CAMath.h" +#include "CAThreadSafeList.h" +#include "CAVectorUnit.h" +#include "CAMutex.h" +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include <AudioUnit/AudioUnit.h> + #if !CA_BASIC_AU_FEATURES + #include <AudioUnit/MusicDevice.h> + #endif +#else + #include "AudioUnit.h" + #if !CA_BASIC_AU_FEATURES + #include "MusicDevice.h" + #endif +#endif + +#ifndef AUTRACE + #define AUTRACE(code, obj, a, b, c, d) +#endif + +#include "AUPlugInDispatch.h" + + + +// ________________________________________________________________________ +// These are to be moved to the public AudioUnit headers + +#define kAUDefaultSampleRate 44100.0 +#if !TARGET_OS_WIN32 +#define kAUDefaultMaxFramesPerSlice 1156 +//this allows enough default frames for a 512 dest 44K and SRC from 96K +// add a padding of 4 frames for any altivec rounding +#else +#define kAUDefaultMaxFramesPerSlice 2048 +#endif + +// ________________________________________________________________________ + +/*! @class AUBase */ +class AUBase : public ComponentBase { +public: + + /*! @ctor AUBase */ + AUBase( AudioComponentInstance inInstance, + UInt32 numInputElements, + UInt32 numOutputElements, + UInt32 numGroupElements = 0); + /*! @dtor AUBase */ + virtual ~AUBase(); + + /*! @method PostConstructor */ + virtual void PostConstructor() { CreateElements(); } + + /*! @method PreDestructor */ + virtual void PreDestructor(); + + /*! @method CreateElements */ + void CreateElements(); + // Called immediately after construction, when virtual methods work. + // Or, a subclass may call this in order to have access to elements + // in its constructor. + + /*! @method CreateExtendedElements */ + 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. + + /*! @method DoInitialize */ + OSStatus DoInitialize(); + // this implements the entry point and makes sure that initialization + // is only attempted exactly once... + + /*! @method Initialize */ + virtual OSStatus Initialize(); + // ... so that overrides to this method can assume that they will only + // be called exactly once. + + /*! @method IsInitialized */ + bool IsInitialized() const { return mInitialized; } + /*! @method HasBegunInitializing */ + bool HasBegunInitializing() const { return mHasBegunInitializing; } + + /*! @method DoCleanup */ + void DoCleanup(); + // same pattern as with Initialize + + /*! @method Cleanup */ + virtual void Cleanup(); + + /*! @method Reset */ + 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) + + /*! @method DispatchGetPropertyInfo */ + OSStatus DispatchGetPropertyInfo(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable); + + /*! @method DispatchGetProperty */ + OSStatus DispatchGetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData); + + /*! @method DispatchSetProperty */ + OSStatus DispatchSetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize); + + OSStatus DispatchRemovePropertyValue( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement); + + /*! @method GetPropertyInfo */ + virtual OSStatus GetPropertyInfo( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable); + + /*! @method GetProperty */ + virtual OSStatus GetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData); + + /*! @method SetProperty */ + virtual OSStatus SetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize); + + /*! @method ClearPropertyUsage */ + virtual OSStatus RemovePropertyValue ( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement); + + /*! @method AddPropertyListener */ + virtual OSStatus AddPropertyListener( AudioUnitPropertyID inID, + AudioUnitPropertyListenerProc inProc, + void * inProcRefCon); + + /*! @method RemovePropertyListener */ + virtual OSStatus RemovePropertyListener( AudioUnitPropertyID inID, + AudioUnitPropertyListenerProc inProc, + void * inProcRefCon, + bool refConSpecified); + + /*! @method SetRenderNotification */ + virtual OSStatus SetRenderNotification( AURenderCallback inProc, + void * inRefCon); + + /*! @method RemoveRenderNotification */ + virtual OSStatus RemoveRenderNotification( + AURenderCallback inProc, + void * inRefCon); + + /*! @method GetParameter */ + virtual OSStatus GetParameter( AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + AudioUnitParameterValue & outValue); + + /*! @method SetParameter */ + virtual OSStatus SetParameter( AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + AudioUnitParameterValue inValue, + UInt32 inBufferOffsetInFrames); + + /*! @method CanScheduleParams */ + virtual bool CanScheduleParameters() const = 0; + + /*! @method ScheduleParameter */ + virtual OSStatus ScheduleParameter ( const AudioUnitParameterEvent *inParameterEvent, + UInt32 inNumEvents); + + + /*! @method DoRender */ + OSStatus DoRender( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList & ioData); + + + /*! @method Process */ + OSStatus DoProcess ( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inFramesToProcess, + AudioBufferList & ioData); + + /*! @method ProcessMultiple */ + OSStatus DoProcessMultiple ( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inFramesToProcess, + UInt32 inNumberInputBufferLists, + const AudioBufferList ** inInputBufferLists, + UInt32 inNumberOutputBufferLists, + AudioBufferList ** ioOutputBufferLists); + + /*! @method ProcessBufferLists */ + virtual OSStatus ProcessBufferLists( AudioUnitRenderActionFlags & ioActionFlags, + const AudioBufferList & inBuffer, + AudioBufferList & outBuffer, + UInt32 inFramesToProcess ) + { + return kAudio_UnimplementedError; + } + + /*! @method ProcessMultipleBufferLists */ + virtual OSStatus ProcessMultipleBufferLists( AudioUnitRenderActionFlags & ioActionFlags, + UInt32 inFramesToProcess, + UInt32 inNumberInputBufferLists, + const AudioBufferList ** inInputBufferLists, + UInt32 inNumberOutputBufferLists, + AudioBufferList ** ioOutputBufferLists) + { + return kAudio_UnimplementedError; + } + + /*! @method ComplexRender */ + 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. + /*! @method RenderBus */ + 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. + /*! @method Render */ + virtual OSStatus Render( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inNumberFrames) + { + return noErr; + } + + +#pragma mark - +#pragma mark Property Dispatch + + static const Float64 kNoLastRenderedSampleTime; + + // ________________________________________________________________________ + // These are generated from DispatchGetProperty/DispatchGetPropertyInfo/DispatchSetProperty + + /*! @method BusCountWritable */ + virtual bool BusCountWritable( AudioUnitScope inScope) + { + return false; + } + virtual OSStatus SetBusCount( AudioUnitScope inScope, + UInt32 inCount); + + /*! @method SetConnection */ + virtual OSStatus SetConnection( const AudioUnitConnection & inConnection); + + /*! @method SetInputCallback */ + virtual OSStatus SetInputCallback( UInt32 inPropertyID, + AudioUnitElement inElement, + AURenderCallback inProc, + void * inRefCon); + + /*! @method GetParameterList */ + virtual OSStatus GetParameterList( AudioUnitScope inScope, + AudioUnitParameterID * outParameterList, + UInt32 & outNumParameters); + // outParameterList may be a null pointer + + /*! @method GetParameterInfo */ + virtual OSStatus GetParameterInfo( AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + AudioUnitParameterInfo & outParameterInfo); + + virtual OSStatus GetParameterHistoryInfo(AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + Float32 & outUpdatesPerSecond, + Float32 & outHistoryDurationInSeconds); + + /*! @method SaveState */ + virtual OSStatus SaveState( CFPropertyListRef * outData); + + /*! @method SaveExtendedScopes */ + virtual void SaveExtendedScopes( CFMutableDataRef outData) {}; + + /*! @method RestoreState */ + virtual OSStatus RestoreState( CFPropertyListRef inData); + + /*! @method GetParameterValueStrings */ + virtual OSStatus GetParameterValueStrings(AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + CFArrayRef * outStrings); + + /*! @method CopyClumpName */ + virtual OSStatus CopyClumpName( AudioUnitScope inScope, + UInt32 inClumpID, + UInt32 inDesiredNameLength, + CFStringRef * outClumpName); + + /*! @method GetPresets */ + 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 WON'T take + /*! @method SetAFactoryPresetAsCurrent */ + bool SetAFactoryPresetAsCurrent (const AUPreset & inPreset); + + // Called when someone 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 + /*! @method NewFactoryPresetSet */ + virtual OSStatus NewFactoryPresetSet (const AUPreset & inNewFactoryPreset); + + /*! @method NewCustomPresetSet */ + virtual OSStatus NewCustomPresetSet (const AUPreset & inNewCustomPreset); + +#if !CA_USE_AUDIO_PLUGIN_ONLY + /*! @method GetNumCustomUIComponents */ + virtual int GetNumCustomUIComponents (); + + /*! @method GetUIComponentDescs */ + virtual void GetUIComponentDescs (ComponentDescription* inDescArray); +#endif + + /*! @method CopyIconLocation */ + virtual CFURLRef CopyIconLocation (); + + // default is no latency, and unimplemented tail time + /*! @method GetLatency */ + virtual Float64 GetLatency() {return 0.0;} + /*! @method GetTailTime */ + virtual Float64 GetTailTime() {return 0;} + /*! @method SupportsRampAndTail */ + virtual bool SupportsTail () { return false; } + + /*! @method IsStreamFormatWritable */ + bool IsStreamFormatWritable( AudioUnitScope scope, + AudioUnitElement element); + + /*! @method StreamFormatWritable */ + virtual bool StreamFormatWritable( AudioUnitScope scope, + AudioUnitElement element) = 0; + // scope will always be input or output + + // 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... + /*! @method SupportedNumChannels */ + virtual UInt32 SupportedNumChannels ( const AUChannelInfo** outInfo); + + /*! @method ValidFormat */ + virtual bool ValidFormat( AudioUnitScope inScope, + AudioUnitElement inElement, + const CAStreamBasicDescription & inNewFormat); + // Will only be called after StreamFormatWritable + // has succeeded. + // Default implementation requires canonical format: + // 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. + + + /*! @method FormatIsCanonical */ + bool FormatIsCanonical( const CAStreamBasicDescription &format); + + /*! @method MakeCanonicalFormat */ + void MakeCanonicalFormat( CAStreamBasicDescription & outDesc, + int numChannels = 2); + + /*! @method GetStreamFormat */ + virtual const CAStreamBasicDescription & + GetStreamFormat( AudioUnitScope inScope, + AudioUnitElement inElement); + + /*! @method ChangeStreamFormat */ + virtual OSStatus ChangeStreamFormat( AudioUnitScope inScope, + AudioUnitElement inElement, + const CAStreamBasicDescription & inPrevFormat, + const CAStreamBasicDescription & inNewFormat); + // Will only be called after StreamFormatWritable + // and ValidFormat have succeeded. + + // ________________________________________________________________________ + +#if !CA_USE_AUDIO_PLUGIN_ONLY + /*! @method ComponentEntryDispatch */ + static OSStatus ComponentEntryDispatch( ComponentParameters * params, + AUBase * This); +#endif + + // ________________________________________________________________________ + // Methods useful for subclasses + + /*! @method GetScope */ + AUScope & GetScope( AudioUnitScope inScope) + { + if (inScope >= kNumScopes) { + AUScope * scope = GetScopeExtended(inScope); + if (!scope) COMPONENT_THROW(kAudioUnitErr_InvalidScope); + return *scope; + } + return mScopes[inScope]; + } + + /*! @method GetScopeExtended */ + virtual AUScope * GetScopeExtended (AudioUnitScope inScope) { return NULL; } + + /*! @method GlobalScope */ + AUScope & GlobalScope() { return mScopes[kAudioUnitScope_Global]; } + /*! @method Inputs */ + AUScope & Inputs() { return mScopes[kAudioUnitScope_Input]; } + /*! @method Outputs */ + AUScope & Outputs() { return mScopes[kAudioUnitScope_Output]; } +#if !CA_BASIC_AU_FEATURES + /*! @method Groups */ + AUScope & Groups() { return mScopes[kAudioUnitScope_Group]; } +#endif + /*! @method Globals */ + AUElement * Globals() { return mScopes[kAudioUnitScope_Global].GetElement(0); } + + /*! @method SetNumberOfElements */ + void SetNumberOfElements( AudioUnitScope inScope, + UInt32 numElements); + + /*! @method GetElement */ + AUElement * GetElement( AudioUnitScope inScope, + AudioUnitElement inElement) + { + return GetScope(inScope).GetElement(inElement); + } + + /*! @method GetIOElement */ + AUIOElement * GetIOElement( AudioUnitScope inScope, + AudioUnitElement inElement) + { + return GetScope(inScope).GetIOElement(inElement); + } + + /*! @method SafeGetElement */ + AUElement * SafeGetElement( AudioUnitScope inScope, + AudioUnitElement inElement) + { + return GetScope(inScope).SafeGetElement(inElement); + } + + /*! @method GetInput */ + AUInputElement * GetInput( AudioUnitElement inElement) + { + return static_cast<AUInputElement *>(Inputs().SafeGetElement(inElement)); + } + + /*! @method GetOutput */ + AUOutputElement * GetOutput( AudioUnitElement inElement) + { + return static_cast<AUOutputElement *>(Outputs().SafeGetElement(inElement)); + } + +#if !CA_BASIC_AU_FEATURES + /*! @method GetGroup */ + AUElement * GetGroup( AudioUnitElement inElement) + { + return Groups().SafeGetElement(inElement); + } +#endif + + /*! @method PullInput */ + OSStatus PullInput( UInt32 inBusNumber, + AudioUnitRenderActionFlags &ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inNumberFrames) + { + AUInputElement *input = GetInput(inBusNumber); // throws if error + return input->PullInput(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames); + } + + /*! @method GetMaxFramesPerSlice */ + UInt32 GetMaxFramesPerSlice() const { return mMaxFramesPerSlice; } + /*! @method UsesFixedBlockSize */ + bool UsesFixedBlockSize() const { return mUsesFixedBlockSize; } + /*! @method SetUsesFixedBlockSize */ + void SetUsesFixedBlockSize(bool inUsesFixedBlockSize) { mUsesFixedBlockSize = inUsesFixedBlockSize; } + + /*! @method GetVectorUnitType */ + static SInt32 GetVectorUnitType() { return sVectorUnitType; } + /*! @method HasVectorUnit */ + static bool HasVectorUnit() { return sVectorUnitType > 0; } + /*! @method HasAltivec */ + static bool HasAltivec() { return sVectorUnitType == kVecAltivec; } + /*! @method HasSSE2 */ + static bool HasSSE2() { return sVectorUnitType >= kVecSSE2; } + /*! @method HasSSE3 */ + static bool HasSSE3() { return sVectorUnitType >= kVecSSE3; } + + /*! @method AudioUnitAPIVersion */ + UInt8 AudioUnitAPIVersion() const { return mAudioUnitAPIVersion; } + + /*! @method IsRenderThread */ + bool InRenderThread () const + { +#if TARGET_OS_MAC + return (mRenderThreadID ? pthread_equal (mRenderThreadID, pthread_self()) : false); +#elif TARGET_OS_WIN32 + return (mRenderThreadID ? mRenderThreadID == GetCurrentThreadId() : false); +#endif + } + + /*! @method HasInput */ + bool HasInput( AudioUnitElement inElement) { + AUInputElement *in = static_cast<AUInputElement *>(Inputs().GetElement(inElement)); + return in != NULL && in->IsActive(); + } + // says whether an input is connected or has a callback + + /*! @method PropertyChanged */ + virtual void PropertyChanged( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement); + +#if !CA_NO_AU_UI_FEATURES + // 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. + + /*! @method CallHostBeatAndTempo */ + OSStatus CallHostBeatAndTempo (Float64 *outCurrentBeat, + Float64 *outCurrentTempo) + { + return (mHostCallbackInfo.beatAndTempoProc + ? (*mHostCallbackInfo.beatAndTempoProc) (mHostCallbackInfo.hostUserData, + outCurrentBeat, + outCurrentTempo) + : -1); + } + + /*! @method CallHostMusicalTimeLocation */ + OSStatus CallHostMusicalTimeLocation (UInt32 *outDeltaSampleOffsetToNextBeat, + Float32 *outTimeSig_Numerator, + UInt32 *outTimeSig_Denominator, + Float64 *outCurrentMeasureDownBeat) + { + return (mHostCallbackInfo.musicalTimeLocationProc + ? (*mHostCallbackInfo.musicalTimeLocationProc) (mHostCallbackInfo.hostUserData, + outDeltaSampleOffsetToNextBeat, + outTimeSig_Numerator, + outTimeSig_Denominator, + outCurrentMeasureDownBeat) + : -1); + } + + /*! @method CallHostTransportState */ + OSStatus CallHostTransportState (Boolean *outIsPlaying, + Boolean *outTransportStateChanged, + Float64 *outCurrentSampleInTimeLine, + Boolean *outIsCycling, + Float64 *outCycleStartBeat, + Float64 *outCycleEndBeat) + { + return (mHostCallbackInfo.transportStateProc + ? (*mHostCallbackInfo.transportStateProc) (mHostCallbackInfo.hostUserData, + outIsPlaying, + outTransportStateChanged, + outCurrentSampleInTimeLine, + outIsCycling, + outCycleStartBeat, + outCycleEndBeat) + : -1); + } +#endif + + char* GetLoggingString () const; + + CAMutex* GetMutex() { return mAUMutex; } + + // ________________________________________________________________________ + /*! @method CreateElement */ + virtual AUElement * CreateElement( AudioUnitScope scope, + AudioUnitElement element); + +#pragma mark - +#pragma mark AU Output Base Dispatch + // ________________________________________________________________________ + // ________________________________________________________________________ + // ________________________________________________________________________ + // output unit methods + /*! @method Start */ + virtual OSStatus Start() { return kAudio_UnimplementedError; } + /*! @method Stop */ + virtual OSStatus Stop() { return kAudio_UnimplementedError; } + +#if !CA_BASIC_AU_FEATURES +#pragma mark - +#pragma mark AU Music Base Dispatch + +#if !TARGET_OS_IPHONE +// these methods are deprecated, so we don't include them except for compatability + /*! @method PrepareInstrument */ + virtual OSStatus PrepareInstrument(MusicDeviceInstrumentID inInstrument) { return kAudio_UnimplementedError; } + + /*! @method PrepareInstrument */ + virtual OSStatus ReleaseInstrument(MusicDeviceInstrumentID inInstrument) { return kAudio_UnimplementedError; } +#endif + + // ________________________________________________________________________ + // ________________________________________________________________________ + // ________________________________________________________________________ + // music device/music effect methods -- incomplete + /*! @method MIDIEvent */ + virtual OSStatus MIDIEvent( UInt32 inStatus, + UInt32 inData1, + UInt32 inData2, + UInt32 inOffsetSampleFrame) { return kAudio_UnimplementedError; } + + /*! @method SysEx */ + virtual OSStatus SysEx( const UInt8 * inData, + UInt32 inLength) { return kAudio_UnimplementedError;} + + /*! @method StartNote */ + virtual OSStatus StartNote( MusicDeviceInstrumentID inInstrument, + MusicDeviceGroupID inGroupID, + NoteInstanceID * outNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams) { return kAudio_UnimplementedError; } + + /*! @method StopNote */ + virtual OSStatus StopNote( MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame) { return kAudio_UnimplementedError; } +#endif + + // ________________________________________________________________________ + // ________________________________________________________________________ + // ________________________________________________________________________ + +protected: +#pragma mark - +#pragma mark Implementation methods + + /*! @method ReallocateBuffers */ + virtual void ReallocateBuffers(); + // needs to be called when mMaxFramesPerSlice changes + virtual void DeallocateIOBuffers(); + + /*! @method FillInParameterName */ + static void FillInParameterName (AudioUnitParameterInfo& ioInfo, CFStringRef inName, bool inShouldRelease) + { + ioInfo.cfNameString = inName; + ioInfo.flags |= kAudioUnitParameterFlag_HasCFNameString; + if (inShouldRelease) + ioInfo.flags |= kAudioUnitParameterFlag_CFNameRelease; + CFStringGetCString (inName, ioInfo.name, offsetof (AudioUnitParameterInfo, clumpID), kCFStringEncodingUTF8); + } + + static void HasClump (AudioUnitParameterInfo& ioInfo, UInt32 inClumpID) + { + ioInfo.clumpID = inClumpID; + ioInfo.flags |= kAudioUnitParameterFlag_HasClump; + } + + /*! @method SetMaxFramesPerSlice */ + virtual void SetMaxFramesPerSlice(UInt32 nFrames); + + /*! @method CanSetMaxFrames */ + virtual OSStatus CanSetMaxFrames() const; + + /*! @method WantsRenderThreadID */ + bool WantsRenderThreadID () const { return mWantsRenderThreadID; } + + /*! @method SetWantsRenderThreadID */ + void SetWantsRenderThreadID (bool inFlag); + + /*! @method SetRenderError */ + OSStatus SetRenderError (OSStatus inErr) + { + if (inErr && mLastRenderError == 0) { + mLastRenderError = inErr; + PropertyChanged(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0); + } + return inErr; + } + +private: + /*! @method DoRenderBus */ + // 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 == NULL || (theOutput->WillAllocateBuffer() && Outputs().GetNumberOfElements() > 1)) + // will render into cache buffer + theOutput->PrepareBuffer(inNumberFrames); + else + // will render into caller's buffer + theOutput->SetBufferList(ioData); + OSStatus result = RenderBus(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames); + if (result == noErr) { + if (ioData.mBuffers[0].mData == NULL) { + theOutput->CopyBufferListTo(ioData); + AUTRACE(kCATrace_AUBaseDoRenderBus, mComponentInstance, inNumberFrames, (intptr_t)theOutput->GetBufferList().mBuffers[0].mData, 0, *(UInt32 *)ioData.mBuffers[0].mData); + } else { + theOutput->CopyBufferContentsTo(ioData); + AUTRACE(kCATrace_AUBaseDoRenderBus, mComponentInstance, inNumberFrames, (intptr_t)theOutput->GetBufferList().mBuffers[0].mData, (intptr_t)ioData.mBuffers[0].mData, *(UInt32 *)ioData.mBuffers[0].mData); + theOutput->InvalidateBufferList(); + } + } + return result; + } + + /*! @method HasIcon */ + bool HasIcon (); + + /*! @method ResetRenderTime */ + void ResetRenderTime () + { + memset (&mCurrentRenderTime, 0, sizeof(mCurrentRenderTime)); + mCurrentRenderTime.mSampleTime = kNoLastRenderedSampleTime; + } + +protected: + /*! @method GetAudioChannelLayout */ + virtual UInt32 GetChannelLayoutTags( AudioUnitScope scope, + AudioUnitElement element, + AudioChannelLayoutTag * outLayoutTags); + + /*! @method GetAudioChannelLayout */ + virtual UInt32 GetAudioChannelLayout( AudioUnitScope scope, + AudioUnitElement element, + AudioChannelLayout * outLayoutPtr, + Boolean & outWritable); + + /*! @method SetAudioChannelLayout */ + virtual OSStatus SetAudioChannelLayout( AudioUnitScope scope, + AudioUnitElement element, + const AudioChannelLayout * inLayout); + + /*! @method RemoveAudioChannelLayout */ + virtual OSStatus RemoveAudioChannelLayout(AudioUnitScope scope, AudioUnitElement element); + + /*! @method NeedsToRender */ + bool NeedsToRender( const AudioTimeStamp & inTimeStamp) + { + bool needsToRender = fnotequal(inTimeStamp.mSampleTime, mCurrentRenderTime.mSampleTime); + if (needsToRender) // only copy this if we need to render + mCurrentRenderTime = inTimeStamp; + return needsToRender; + } + + // Scheduled parameter implementation: + + typedef std::vector<AudioUnitParameterEvent> ParameterEventList; + + // 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 ) {return noErr;}; // default impl does nothing... + + + /*! @method CurrentRenderTime */ + const AudioTimeStamp & CurrentRenderTime () const { return mCurrentRenderTime; } + + // ________________________________________________________________________ + // Private data members to discourage hacking in subclasses +private: + struct RenderCallback { + RenderCallback(AURenderCallback proc, void *ref) : + mRenderNotify(proc), + mRenderNotifyRefCon(ref) + { } + + AURenderCallback mRenderNotify; + void * mRenderNotifyRefCon; + + bool operator == (const RenderCallback &other) { + return this->mRenderNotify == other.mRenderNotify && + this->mRenderNotifyRefCon == other.mRenderNotifyRefCon; + } + }; + typedef TThreadSafeList<RenderCallback> RenderCallbackList; + +#if !CA_BASIC_AU_FEATURES + enum { kNumScopes = 4 }; +#else + enum { kNumScopes = 3 }; +#endif + + /*! @var mElementsCreated */ + bool mElementsCreated; +protected: + /*! @var mInitialized */ + bool mInitialized; + /*! @var mHasBegunInitializing */ + bool mHasBegunInitializing; +private: + /*! @var mAudioUnitAPIVersion */ + UInt8 mAudioUnitAPIVersion; + + /*! @var mInitNumInputEls */ + const UInt32 mInitNumInputEls; + /*! @var mInitNumOutputEls */ + const UInt32 mInitNumOutputEls; +#if !CA_BASIC_AU_FEATURES + /*! @var mInitNumGroupEls */ + const UInt32 mInitNumGroupEls; +#endif + /*! @var mScopes */ + AUScope mScopes[kNumScopes]; + + /*! @var mRenderCallbacks */ + RenderCallbackList mRenderCallbacks; + bool mRenderCallbacksTouched; + + /*! @var mRenderThreadID */ +#if TARGET_OS_MAC + pthread_t mRenderThreadID; +#elif TARGET_OS_WIN32 + UInt32 mRenderThreadID; +#endif + + /*! @var mWantsRenderThreadID */ + bool mWantsRenderThreadID; + + /*! @var mCurrentRenderTime */ + AudioTimeStamp mCurrentRenderTime; + + /*! @var mMaxFramesPerSlice */ + UInt32 mMaxFramesPerSlice; + + /*! @var mLastRenderError */ + OSStatus mLastRenderError; + /*! @var mCurrentPreset */ + AUPreset mCurrentPreset; + +protected: + /*! @var mUsesFixedBlockSize */ + bool mUsesFixedBlockSize; + + struct PropertyListener { + AudioUnitPropertyID propertyID; + AudioUnitPropertyListenerProc listenerProc; + void * listenerRefCon; + }; + typedef std::vector<PropertyListener> PropertyListeners; + + /*! @var mParamList */ + ParameterEventList mParamList; + /*! @var mPropertyListeners */ + PropertyListeners mPropertyListeners; + + /*! @var mBuffersAllocated */ + bool mBuffersAllocated; + + /*! @var mLogString */ + // if this is NOT null, it will contain identifying info about this AU. + char* mLogString; + + /*! @var mNickName */ + CFStringRef mNickName; + + /*! @var mAUMutex */ + CAMutex * mAUMutex; + +private: + /*! @var sVectorUnitType */ + static SInt32 sVectorUnitType; + +#if !CA_NO_AU_HOST_CALLBACKS +protected: + /*! @var mHostCallbackInfo */ + HostCallbackInfo mHostCallbackInfo; + +#endif +#if !CA_NO_AU_UI_FEATURES +protected: + /*! @var mContextInfo */ + CFStringRef mContextName; +#endif +}; + +inline OSStatus AUInputElement::PullInputWithBufferList( + AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + AudioUnitElement inElement, + UInt32 nFrames, + AudioBufferList * inBufferList) +{ + OSStatus theResult; + + if (HasConnection()) { + // only support connections for V2 audio units +#if !CA_USE_AUDIO_PLUGIN_ONLY + if (mConnRenderProc != NULL) + theResult = reinterpret_cast<AudioUnitRenderProc>(mConnRenderProc)( + mConnInstanceStorage, &ioActionFlags, &inTimeStamp, mConnection.sourceOutputNumber, nFrames, inBufferList); + else +#endif + theResult = AudioUnitRender( + mConnection.sourceAudioUnit, &ioActionFlags, &inTimeStamp, mConnection.sourceOutputNumber, nFrames, inBufferList); + } else { + // kFromCallback: + theResult = (mInputProc)( + mInputProcRefCon, &ioActionFlags, &inTimeStamp, inElement, nFrames, inBufferList); + } + + if (mInputType == kNoInput) // defense: the guy upstream could have disconnected + // it's a horrible thing to do, but may happen! + return kAudioUnitErr_NoConnection; + + + return theResult; +} + +#endif // __AUBase_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUDispatch.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUDispatch.cpp new file mode 100644 index 0000000000..1f0f01c7d5 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUDispatch.cpp @@ -0,0 +1,438 @@ +/* + File: AUDispatch.cpp + Abstract: AUDispatch.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUBase.h" +#include "CAXException.h" +#include "AUDispatch.h" + + + +#if TARGET_OS_MAC + #if __LP64__ + // comp instance, parameters in forward order + #define PARAM(_typ, _name, _index, _nparams) \ + _typ _name = *(_typ *)¶ms->params[_index + 1]; + #else + // parameters in reverse order, then comp instance + #define PARAM(_typ, _name, _index, _nparams) \ + _typ _name = *(_typ *)¶ms->params[_nparams - 1 - _index]; + #endif +#elif TARGET_OS_WIN32 + // (no comp instance), parameters in forward order + #define PARAM(_typ, _name, _index, _nparams) \ + _typ _name = *(_typ *)¶ms->params[_index]; +#endif + + +OSStatus AUBase::ComponentEntryDispatch(ComponentParameters *params, AUBase *This) +{ + if (This == NULL) return kAudio_ParamError; + + OSStatus result = noErr; + + switch (params->what) { + case kComponentCanDoSelect: + switch (GetSelectorForCanDo(params)) { + // any selectors + case kAudioUnitInitializeSelect: + case kAudioUnitUninitializeSelect: + case kAudioUnitGetPropertyInfoSelect: + case kAudioUnitGetPropertySelect: + case kAudioUnitSetPropertySelect: + case kAudioUnitAddPropertyListenerSelect: +#if (!__LP64__) + case kAudioUnitRemovePropertyListenerSelect: +#endif + case kAudioUnitGetParameterSelect: + case kAudioUnitSetParameterSelect: + case kAudioUnitResetSelect: + result = 1; + break; + // v1 selectors + + // v2 selectors + case kAudioUnitRemovePropertyListenerWithUserDataSelect: + case kAudioUnitAddRenderNotifySelect: + case kAudioUnitRemoveRenderNotifySelect: + case kAudioUnitScheduleParametersSelect: + case kAudioUnitRenderSelect: + result = (This->AudioUnitAPIVersion() > 1); + break; + + default: + return ComponentBase::ComponentEntryDispatch(params, This); + } + break; + + case kAudioUnitInitializeSelect: + { + CAMutex::Locker lock2(This->GetMutex()); + result = This->DoInitialize(); + } + break; + + case kAudioUnitUninitializeSelect: + { + CAMutex::Locker lock2(This->GetMutex()); + This->DoCleanup(); + result = noErr; + } + break; + + case kAudioUnitGetPropertyInfoSelect: + { + CAMutex::Locker lock(This->GetMutex()); + PARAM(AudioUnitPropertyID, pinID, 0, 5); + PARAM(AudioUnitScope, pinScope, 1, 5); + PARAM(AudioUnitElement, pinElement, 2, 5); + PARAM(UInt32 *, poutDataSize, 3, 5); + PARAM(Boolean *, poutWritable, 4, 5); + + // pass our own copies so that we assume responsibility for testing + // the caller's pointers against null and our C++ classes can + // always assume they're non-null + UInt32 dataSize; + Boolean writable; + + result = This->DispatchGetPropertyInfo(pinID, pinScope, pinElement, dataSize, writable); + if (poutDataSize != NULL) + *poutDataSize = dataSize; + if (poutWritable != NULL) + *poutWritable = writable; + } + break; + + case kAudioUnitGetPropertySelect: + { + CAMutex::Locker lock(This->GetMutex()); + PARAM(AudioUnitPropertyID, pinID, 0, 5); + PARAM(AudioUnitScope, pinScope, 1, 5); + PARAM(AudioUnitElement, pinElement, 2, 5); + PARAM(void *, poutData, 3, 5); + PARAM(UInt32 *, pioDataSize, 4, 5); + + UInt32 actualPropertySize, clientBufferSize; + Boolean writable; + char *tempBuffer; + void *destBuffer; + + if (pioDataSize == NULL) { + ca_debug_string("AudioUnitGetProperty: null size pointer"); + result = kAudio_ParamError; + goto finishGetProperty; + } + if (poutData == NULL) { + UInt32 dataSize; + + result = This->DispatchGetPropertyInfo(pinID, pinScope, pinElement, dataSize, writable); + *pioDataSize = dataSize; + goto finishGetProperty; + } + + clientBufferSize = *pioDataSize; + if (clientBufferSize == 0) + { + ca_debug_string("AudioUnitGetProperty: *ioDataSize == 0 on entry"); + // $$$ or should we allow this as a shortcut for finding the size? + result = kAudio_ParamError; + goto finishGetProperty; + } + + result = This->DispatchGetPropertyInfo(pinID, pinScope, pinElement, + actualPropertySize, writable); + if (result) + goto finishGetProperty; + + if (clientBufferSize < actualPropertySize) + { + tempBuffer = new char[actualPropertySize]; + destBuffer = tempBuffer; + } else { + tempBuffer = NULL; + destBuffer = poutData; + } + + result = This->DispatchGetProperty(pinID, pinScope, pinElement, destBuffer); + + if (result == noErr) { + if (clientBufferSize < actualPropertySize && tempBuffer != NULL) + { + memcpy(poutData, tempBuffer, clientBufferSize); + delete[] tempBuffer; + // pioDataSize remains correct, the number of bytes we wrote + } else + *pioDataSize = actualPropertySize; + } else + *pioDataSize = 0; + + finishGetProperty: + ; + + } + break; + + case kAudioUnitSetPropertySelect: + { + CAMutex::Locker lock(This->GetMutex()); + PARAM(AudioUnitPropertyID, pinID, 0, 5); + PARAM(AudioUnitScope, pinScope, 1, 5); + PARAM(AudioUnitElement, pinElement, 2, 5); + PARAM(const void *, pinData, 3, 5); + PARAM(UInt32, pinDataSize, 4, 5); + + if (pinData && pinDataSize) + result = This->DispatchSetProperty(pinID, pinScope, pinElement, pinData, pinDataSize); + else { + if (pinData == NULL && pinDataSize == 0) { + result = This->DispatchRemovePropertyValue (pinID, pinScope, pinElement); + } else { + if (pinData == NULL) { + ca_debug_string("AudioUnitSetProperty: inData == NULL"); + result = kAudio_ParamError; + goto finishSetProperty; + } + + if (pinDataSize == 0) { + ca_debug_string("AudioUnitSetProperty: inDataSize == 0"); + result = kAudio_ParamError; + goto finishSetProperty; + } + } + } + finishSetProperty: + ; + + } + break; + + case kAudioUnitAddPropertyListenerSelect: + { + CAMutex::Locker lock(This->GetMutex()); + PARAM(AudioUnitPropertyID, pinID, 0, 3); + PARAM(AudioUnitPropertyListenerProc, pinProc, 1, 3); + PARAM(void *, pinProcRefCon, 2, 3); + result = This->AddPropertyListener(pinID, pinProc, pinProcRefCon); + } + break; + +#if (!__LP64__) + case kAudioUnitRemovePropertyListenerSelect: + { + CAMutex::Locker lock(This->GetMutex()); + PARAM(AudioUnitPropertyID, pinID, 0, 2); + PARAM(AudioUnitPropertyListenerProc, pinProc, 1, 2); + result = This->RemovePropertyListener(pinID, pinProc, NULL, false); + } + break; +#endif + + case kAudioUnitRemovePropertyListenerWithUserDataSelect: + { + CAMutex::Locker lock(This->GetMutex()); + PARAM(AudioUnitPropertyID, pinID, 0, 3); + PARAM(AudioUnitPropertyListenerProc, pinProc, 1, 3); + PARAM(void *, pinProcRefCon, 2, 3); + result = This->RemovePropertyListener(pinID, pinProc, pinProcRefCon, true); + } + break; + + case kAudioUnitAddRenderNotifySelect: + { + CAMutex::Locker lock(This->GetMutex()); + PARAM(AURenderCallback, pinProc, 0, 2); + PARAM(void *, pinProcRefCon, 1, 2); + result = This->SetRenderNotification (pinProc, pinProcRefCon); + } + break; + + case kAudioUnitRemoveRenderNotifySelect: + { + CAMutex::Locker lock(This->GetMutex()); + PARAM(AURenderCallback, pinProc, 0, 2); + PARAM(void *, pinProcRefCon, 1, 2); + result = This->RemoveRenderNotification (pinProc, pinProcRefCon); + } + break; + + case kAudioUnitGetParameterSelect: + { + CAMutex::Locker lock(This->GetMutex()); + PARAM(AudioUnitParameterID, pinID, 0, 4); + PARAM(AudioUnitScope, pinScope, 1, 4); + PARAM(AudioUnitElement, pinElement, 2, 4); + PARAM(AudioUnitParameterValue *, poutValue, 3, 4); + result = (poutValue == NULL ? kAudio_ParamError : This->GetParameter(pinID, pinScope, pinElement, *poutValue)); + } + break; + + case kAudioUnitSetParameterSelect: + { + CAMutex::Locker lock(This->GetMutex()); // is this realtime or no??? + PARAM(AudioUnitParameterID, pinID, 0, 5); + PARAM(AudioUnitScope, pinScope, 1, 5); + PARAM(AudioUnitElement, pinElement, 2, 5); + PARAM(AudioUnitParameterValue, pinValue, 3, 5); + PARAM(UInt32, pinBufferOffsetInFrames, 4, 5); + result = This->SetParameter(pinID, pinScope, pinElement, pinValue, pinBufferOffsetInFrames); + } + break; + + case kAudioUnitScheduleParametersSelect: + { + CAMutex::Locker lock(This->GetMutex()); // is this realtime or no??? + if (This->AudioUnitAPIVersion() > 1) + { + PARAM(AudioUnitParameterEvent *, pinParameterEvent, 0, 2); + PARAM(UInt32, pinNumParamEvents, 1, 2); + result = This->ScheduleParameter (pinParameterEvent, pinNumParamEvents); + } else + result = badComponentSelector; + } + break; + + + case kAudioUnitRenderSelect: + { + // realtime; no lock + { + PARAM(AudioUnitRenderActionFlags *, pinActionFlags, 0, 5); + PARAM(const AudioTimeStamp *, pinTimeStamp, 1, 5); + PARAM(UInt32, pinOutputBusNumber, 2, 5); + PARAM(UInt32, pinNumberFrames, 3, 5); + PARAM(AudioBufferList *, pioData, 4, 5); + AudioUnitRenderActionFlags tempFlags; + + if (pinTimeStamp == NULL || pioData == NULL) + result = kAudio_ParamError; + else { + if (pinActionFlags == NULL) { + tempFlags = 0; + pinActionFlags = &tempFlags; + } + result = This->DoRender(*pinActionFlags, *pinTimeStamp, pinOutputBusNumber, pinNumberFrames, *pioData); + } + } + } + break; + + case kAudioUnitResetSelect: + { + CAMutex::Locker lock(This->GetMutex()); + PARAM(AudioUnitScope, pinScope, 0, 2); + PARAM(AudioUnitElement, pinElement, 1, 2); + This->ResetRenderTime(); + result = This->Reset(pinScope, pinElement); + } + break; + + default: + result = ComponentBase::ComponentEntryDispatch(params, This); + break; + } + + return result; +} + +// Fast dispatch entry points -- these need to replicate all error-checking logic from above + +OSStatus CMgr_AudioUnitBaseGetParameter( AUBase * This, + AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + float *outValue) +{ + OSStatus result = AUBase::noErr; + + try { + if (This == NULL || outValue == NULL) return kAudio_ParamError; + result = This->GetParameter(inID, inScope, inElement, *outValue); + } + COMPONENT_CATCH + + return result; +} + +OSStatus CMgr_AudioUnitBaseSetParameter( AUBase * This, + AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + float inValue, + UInt32 inBufferOffset) +{ + OSStatus result = AUBase::noErr; + + try { + if (This == NULL) return kAudio_ParamError; + result = This->SetParameter(inID, inScope, inElement, inValue, inBufferOffset); + } + COMPONENT_CATCH + + return result; +} + +OSStatus CMgr_AudioUnitBaseRender( AUBase * This, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList * ioData) +{ + if (inTimeStamp == NULL || ioData == NULL) return kAudio_ParamError; + + OSStatus result = AUBase::noErr; + AudioUnitRenderActionFlags tempFlags; + + try { + if (ioActionFlags == NULL) { + tempFlags = 0; + ioActionFlags = &tempFlags; + } + result = This->DoRender(*ioActionFlags, *inTimeStamp, inBusNumber, inNumberFrames, *ioData); + } + COMPONENT_CATCH + + return result; +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUDispatch.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUDispatch.h new file mode 100644 index 0000000000..5acd96250a --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUDispatch.h @@ -0,0 +1,82 @@ +/* + File: AUDispatch.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUDispatch_h__ +#define __AUDispatch_h__ + + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include <AudioUnit/AudioUnit.h> +#else + #include "AudioUnit.h" +#endif + +#if !CA_USE_AUDIO_PLUGIN_ONLY +/*! @function AudioUnitBaseGetParameter */ +OSStatus CMgr_AudioUnitBaseGetParameter( AUBase * This, + AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + float * outValue); + +/*! @function AudioUnitBaseSetParameter */ +OSStatus CMgr_AudioUnitBaseSetParameter( AUBase * This, + AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + float inValue, + UInt32 inBufferOffset); + +/*! @function AudioUnitBaseRender */ +OSStatus CMgr_AudioUnitBaseRender( AUBase * This, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList * ioData); +#endif + +#endif // __AUDispatch_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUInputElement.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUInputElement.cpp new file mode 100644 index 0000000000..2e148e8ba7 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUInputElement.cpp @@ -0,0 +1,151 @@ +/* + File: AUInputElement.cpp + Abstract: AUInputElement.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUBase.h" + +inline bool HasGoodBufferPointers(const AudioBufferList &abl, UInt32 nBytes) +{ + const AudioBuffer *buf = abl.mBuffers; + for (UInt32 i = abl.mNumberBuffers; i--;++buf) { + if (buf->mData == NULL || buf->mDataByteSize < nBytes) + return false; + } + return true; +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// AUInputElement::AUInputElement +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +AUInputElement::AUInputElement(AUBase *audioUnit) : + AUIOElement(audioUnit), + mInputType(kNoInput) +{ +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// AUInputElement::SetConnection +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +void AUInputElement::SetConnection(const AudioUnitConnection &conn) +{ + if (conn.sourceAudioUnit == 0) { + Disconnect(); + return; + } + + mInputType = kFromConnection; + mConnection = conn; + AllocateBuffer(); + + mConnInstanceStorage = NULL; + +#if !CA_USE_AUDIO_PLUGIN_ONLY + mConnRenderProc = NULL; + UInt32 size = sizeof(AudioUnitRenderProc); + OSStatus result = AudioUnitGetProperty( conn.sourceAudioUnit, + kAudioUnitProperty_FastDispatch, + kAudioUnitScope_Global, + kAudioUnitRenderSelect, + &mConnRenderProc, + &size); + if (result == noErr) + mConnInstanceStorage = CMgr_GetComponentInstanceStorage (conn.sourceAudioUnit); + else + mConnRenderProc = NULL; +#endif +} + +void AUInputElement::Disconnect() +{ + mInputType = kNoInput; + mIOBuffer.Deallocate(); +} + + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// AUInputElement::SetInputCallback +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +void AUInputElement::SetInputCallback(AURenderCallback proc, void *refCon) +{ + if (proc == NULL) + Disconnect(); + else { + mInputType = kFromCallback; + mInputProc = proc; + mInputProcRefCon = refCon; + AllocateBuffer(); + } +} + +OSStatus AUInputElement::SetStreamFormat(const CAStreamBasicDescription &fmt) +{ + OSStatus err = AUIOElement::SetStreamFormat(fmt); + if (err == AUBase::noErr) + AllocateBuffer(); + return err; +} + +OSStatus AUInputElement::PullInput( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + AudioUnitElement inElement, + UInt32 nFrames) +{ + if (!IsActive()) + return kAudioUnitErr_NoConnection; + + AudioBufferList *pullBuffer; + + if (HasConnection() || !WillAllocateBuffer()) + pullBuffer = &mIOBuffer.PrepareNullBuffer(mStreamFormat, nFrames); + else + pullBuffer = &mIOBuffer.PrepareBuffer(mStreamFormat, nFrames); + + return PullInputWithBufferList (ioActionFlags, inTimeStamp, inElement, nFrames, pullBuffer); +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUInputElement.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUInputElement.h new file mode 100644 index 0000000000..891e4c60b7 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUInputElement.h @@ -0,0 +1,119 @@ +/* + File: AUInputElement.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUInput_h__ +#define __AUInput_h__ + +#include "AUScopeElement.h" +#include "AUBuffer.h" + +/*! @class AUInputElement */ +class AUInputElement : public AUIOElement { +public: + + /*! @ctor AUInputElement */ + AUInputElement(AUBase *audioUnit); + /*! @dtor ~AUInputElement */ + virtual ~AUInputElement() { } + + // AUElement override + /*! @method SetStreamFormat */ + virtual OSStatus SetStreamFormat(const CAStreamBasicDescription &desc); + /*! @method NeedsBufferSpace */ + virtual bool NeedsBufferSpace() const { return IsCallback(); } + + /*! @method SetConnection */ + void SetConnection(const AudioUnitConnection &conn); + /*! @method SetInputCallback */ + void SetInputCallback(AURenderCallback proc, void *refCon); + + /*! @method IsActive */ + bool IsActive() const { return mInputType != kNoInput; } + /*! @method IsCallback */ + bool IsCallback() const { return mInputType == kFromCallback; } + /*! @method HasConnection */ + bool HasConnection() const { return mInputType == kFromConnection; } + + /*! @method PullInput */ + OSStatus PullInput( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + AudioUnitElement inElement, + UInt32 inNumberFrames); + + /*! @method PullInputWithBufferList */ + OSStatus PullInputWithBufferList( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + AudioUnitElement inElement, + UInt32 nFrames, + AudioBufferList * inBufferList); +protected: + /*! @method Disconnect */ + void Disconnect(); + + enum EInputType { kNoInput, kFromConnection, kFromCallback }; + + /*! @var mInputType */ + EInputType mInputType; + + // if from callback: + /*! @var mInputProc */ + AURenderCallback mInputProc; + /*! @var mInputProcRefCon */ + void * mInputProcRefCon; + + // if from connection: + /*! @var mConnection */ + AudioUnitConnection mConnection; +#if !CA_USE_AUDIO_PLUGIN_ONLY + /*! @var mConnRenderProc */ + AudioUnitRenderProc mConnRenderProc; +#endif + /*! @var mConnInstanceStorage */ + void * mConnInstanceStorage; // for the input component +}; + + +#endif // __AUInput_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUOutputElement.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUOutputElement.cpp new file mode 100644 index 0000000000..5eb34a1c20 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUOutputElement.cpp @@ -0,0 +1,62 @@ +/* + File: AUOutputElement.cpp + Abstract: AUOutputElement.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUOutputElement.h" +#include "AUBase.h" + +AUOutputElement::AUOutputElement(AUBase *audioUnit) : + AUIOElement(audioUnit) +{ + AllocateBuffer(); +} + +OSStatus AUOutputElement::SetStreamFormat(const CAStreamBasicDescription &desc) +{ + OSStatus result = AUIOElement::SetStreamFormat(desc); // inherited + if (result == AUBase::noErr) + AllocateBuffer(); + return result; +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUOutputElement.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUOutputElement.h new file mode 100644 index 0000000000..3e6a938fff --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUOutputElement.h @@ -0,0 +1,66 @@ +/* + File: AUOutputElement.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUOutput_h__ +#define __AUOutput_h__ + +#include "AUScopeElement.h" +#include "AUBuffer.h" + + /*! @class AUOutputElement */ +class AUOutputElement : public AUIOElement { +public: + /*! @ctor AUOutputElement */ + AUOutputElement(AUBase *audioUnit); + + // AUElement override + /*! @method SetStreamFormat */ + virtual OSStatus SetStreamFormat(const CAStreamBasicDescription &desc); + /*! @method NeedsBufferSpace */ + virtual bool NeedsBufferSpace() const { return true; } +}; + +#endif // __AUOutput_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUPlugInDispatch.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUPlugInDispatch.cpp new file mode 100644 index 0000000000..3bab198369 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUPlugInDispatch.cpp @@ -0,0 +1,669 @@ +/* + File: AUPlugInDispatch.cpp + Abstract: AUPlugInDispatch.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUPlugInDispatch.h" +#include "CAXException.h" +#include "ComponentBase.h" +#include "AUBase.h" + +#define ACPI ((AudioComponentPlugInInstance *)self) +#define AUI ((AUBase *)&ACPI->mInstanceStorage) + +#define AUI_LOCK CAMutex::Locker auLock(AUI->GetMutex()); + +// ------------------------------------------------------------------------------------------------ +static OSStatus AUMethodInitialize(void *self) +{ + OSStatus result = noErr; + try { + AUI_LOCK + result = AUI->DoInitialize(); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodUninitialize(void *self) +{ + OSStatus result = noErr; + try { + AUI_LOCK + AUI->DoCleanup(); + } + COMPONENT_CATCH + 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. + Boolean writable = false; + + AUI_LOCK + result = AUI->DispatchGetPropertyInfo(prop, scope, elem, dataSize, writable); + if (outDataSize != NULL) + *outDataSize = dataSize; + if (outWritable != NULL) + *outWritable = writable; + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodGetProperty(void *self, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void *outData, UInt32 *ioDataSize) +{ + OSStatus result = noErr; + try { + UInt32 actualPropertySize, clientBufferSize; + Boolean writable; + char *tempBuffer; + void *destBuffer; + + AUI_LOCK + if (ioDataSize == NULL) { + ca_debug_string("AudioUnitGetProperty: null size pointer"); + result = kAudio_ParamError; + goto finishGetProperty; + } + if (outData == NULL) { + UInt32 dataSize; + + result = AUI->DispatchGetPropertyInfo(inID, inScope, inElement, dataSize, writable); + *ioDataSize = dataSize; + goto finishGetProperty; + } + + clientBufferSize = *ioDataSize; + if (clientBufferSize == 0) + { + ca_debug_string("AudioUnitGetProperty: *ioDataSize == 0 on entry"); + // $$$ or should we allow this as a shortcut for finding the size? + result = kAudio_ParamError; + goto finishGetProperty; + } + + result = AUI->DispatchGetPropertyInfo(inID, inScope, inElement, actualPropertySize, writable); + if (result != noErr) + goto finishGetProperty; + + if (clientBufferSize < actualPropertySize) + { + tempBuffer = new char[actualPropertySize]; + destBuffer = tempBuffer; + } else { + tempBuffer = NULL; + destBuffer = outData; + } + + result = AUI->DispatchGetProperty(inID, inScope, inElement, destBuffer); + + if (result == noErr) { + if (clientBufferSize < actualPropertySize && tempBuffer != NULL) + { + memcpy(outData, tempBuffer, clientBufferSize); + delete[] tempBuffer; + // ioDataSize remains correct, the number of bytes we wrote + } else + *ioDataSize = actualPropertySize; + } else + *ioDataSize = 0; + } + COMPONENT_CATCH +finishGetProperty: + return result; +} + +static OSStatus AUMethodSetProperty(void *self, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void *inData, UInt32 inDataSize) +{ + OSStatus result = noErr; + try { + AUI_LOCK + if (inData && inDataSize) + result = AUI->DispatchSetProperty(inID, inScope, inElement, inData, inDataSize); + else { + if (inData == NULL && inDataSize == 0) { + result = AUI->DispatchRemovePropertyValue(inID, inScope, inElement); + } else { + if (inData == NULL) { + ca_debug_string("AudioUnitSetProperty: inData == NULL"); + result = kAudio_ParamError; + goto finishSetProperty; + } + + if (inDataSize == 0) { + ca_debug_string("AudioUnitSetProperty: inDataSize == 0"); + result = kAudio_ParamError; + goto finishSetProperty; + } + } + } + } + COMPONENT_CATCH +finishSetProperty: + return result; +} + +static OSStatus AUMethodAddPropertyListener(void *self, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void *userData) +{ + OSStatus result = noErr; + try { + AUI_LOCK + result = AUI->AddPropertyListener(prop, proc, userData); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodRemovePropertyListener(void *self, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc) +{ + OSStatus result = noErr; + try { + AUI_LOCK + result = AUI->RemovePropertyListener(prop, proc, NULL, false); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodRemovePropertyListenerWithUserData(void *self, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void *userData) +{ + OSStatus result = noErr; + try { + AUI_LOCK + result = AUI->RemovePropertyListener(prop, proc, userData, true); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodAddRenderNotify(void *self, AURenderCallback proc, void *userData) +{ + OSStatus result = noErr; + try { + AUI_LOCK + result = AUI->SetRenderNotification(proc, userData); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodRemoveRenderNotify(void *self, AURenderCallback proc, void *userData) +{ + OSStatus result = noErr; + try { + AUI_LOCK + result = AUI->RemoveRenderNotification(proc, userData); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodGetParameter(void *self, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement elem, AudioUnitParameterValue *value) +{ + OSStatus result = noErr; + try { + AUI_LOCK + result = (value == NULL ? kAudio_ParamError : AUI->GetParameter(param, scope, elem, *value)); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodSetParameter(void *self, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement elem, AudioUnitParameterValue value, UInt32 bufferOffset) +{ + OSStatus result = noErr; + try { + // this is a (potentially) realtime method; no lock + result = AUI->SetParameter(param, scope, elem, value, bufferOffset); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodScheduleParameters(void *self, const AudioUnitParameterEvent *events, UInt32 numEvents) +{ + OSStatus result = noErr; + try { + // this is a (potentially) realtime method; no lock + result = AUI->ScheduleParameter(events, numEvents); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodRender(void *self, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + OSStatus result = noErr; + +#if !TARGET_OS_IPHONE + try { +#endif + // this is a processing method; no lock + AudioUnitRenderActionFlags tempFlags; + + if (inTimeStamp == NULL || ioData == NULL) + result = kAudio_ParamError; + else { + if (ioActionFlags == NULL) { + tempFlags = 0; + ioActionFlags = &tempFlags; + } + result = AUI->DoRender(*ioActionFlags, *inTimeStamp, inOutputBusNumber, inNumberFrames, *ioData); + } + +#if !TARGET_OS_IPHONE + } + COMPONENT_CATCH +#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 !TARGET_OS_IPHONE + try { +#endif + // this is a processing method; no lock + AudioUnitRenderActionFlags tempFlags; + + if (inTimeStamp == NULL || ioData == NULL) + result = kAudio_ParamError; + else { + if (ioActionFlags == NULL) { + tempFlags = 0; + ioActionFlags = &tempFlags; + } + result = AUI->ComplexRender(*ioActionFlags, *inTimeStamp, inOutputBusNumber, inNumberOfPackets, outNumberOfPackets, outPacketDescriptions, *ioData, outMetadata, outMetadataByteSize); + } + +#if !TARGET_OS_IPHONE + } + COMPONENT_CATCH +#endif + + return result; +} + +static OSStatus AUMethodReset(void *self, AudioUnitScope scope, AudioUnitElement elem) +{ + OSStatus result = noErr; + try { + AUI_LOCK + result = AUI->Reset(scope, elem); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodProcess (void *self, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData) +{ + OSStatus result = noErr; + +#if !TARGET_OS_IPHONE + try { +#endif + // this is a processing method; no lock + bool doParamCheck = true; + + AudioUnitRenderActionFlags tempFlags; + + if (ioActionFlags == NULL) { + tempFlags = 0; + ioActionFlags = &tempFlags; + } else { + if (*ioActionFlags & (1 << 9)/*kAudioUnitRenderAction_DoNotCheckRenderArgs*/) + doParamCheck = false; + } + + if (doParamCheck && (inTimeStamp == NULL || ioData == NULL)) + result = kAudio_ParamError; + else { + result = AUI->DoProcess(*ioActionFlags, *inTimeStamp, inNumberFrames, *ioData); + } + +#if !TARGET_OS_IPHONE + } + COMPONENT_CATCH +#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 !TARGET_OS_IPHONE + try { +#endif + // this is a processing method; no lock + bool doParamCheck = true; + + AudioUnitRenderActionFlags tempFlags; + + if (ioActionFlags == NULL) { + tempFlags = 0; + ioActionFlags = &tempFlags; + } else { + if (*ioActionFlags & (1 << 9)/*kAudioUnitRenderAction_DoNotCheckRenderArgs*/) + doParamCheck = false; + } + + if (doParamCheck && (inTimeStamp == NULL || inInputBufferLists == NULL || ioOutputBufferLists == NULL)) + result = kAudio_ParamError; + else { + result = AUI->DoProcessMultiple(*ioActionFlags, *inTimeStamp, inNumberFrames, inNumberInputBufferLists, inInputBufferLists, inNumberOutputBufferLists, ioOutputBufferLists); + } + +#if !TARGET_OS_IPHONE + } + COMPONENT_CATCH +#endif + + return result; +} +// ------------------------------------------------------------------------------------------------ + +static OSStatus AUMethodStart(void *self) +{ + OSStatus result = noErr; + try { + AUI_LOCK + result = AUI->Start(); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodStop(void *self) +{ + OSStatus result = noErr; + try { + AUI_LOCK + result = AUI->Stop(); + } + COMPONENT_CATCH + return result; +} + +// ------------------------------------------------------------------------------------------------ + +#if !CA_BASIC_AU_FEATURES +// 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 = AUI->MIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame); + } + COMPONENT_CATCH + 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 = AUI->SysEx(inData, inLength); + } + COMPONENT_CATCH + return result; +} + +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 == NULL || outNoteInstanceID == NULL) + result = kAudio_ParamError; + else + result = AUI->StartNote(inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, *inParams); + } + COMPONENT_CATCH + 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 = AUI->StopNote(inGroupID, inNoteInstanceID, inOffsetSampleFrame); + } + COMPONENT_CATCH + return result; +} + +#if !TARGET_OS_IPHONE +static OSStatus AUMethodPrepareInstrument (void *self, MusicDeviceInstrumentID inInstrument) +{ + OSStatus result = noErr; + try { + // this is a potential render-time method; no lock + result = AUI->PrepareInstrument(inInstrument); + } + COMPONENT_CATCH + return result; +} + +static OSStatus AUMethodReleaseInstrument (void *self, MusicDeviceInstrumentID inInstrument) +{ + OSStatus result = noErr; + try { + // this is a potential render-time method; no lock + result = AUI->ReleaseInstrument(inInstrument); + } + COMPONENT_CATCH + return result; +} +#endif // TARGET_OS_IPHONE +#endif // CA_BASIC_AU_FEATURES + + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#pragma mark - +#pragma mark Lookup Methods + +AudioComponentMethod AUBaseLookup::Lookup (SInt16 selector) +{ + switch (selector) { + case kAudioUnitInitializeSelect: return (AudioComponentMethod)AUMethodInitialize; + case kAudioUnitUninitializeSelect: return (AudioComponentMethod)AUMethodUninitialize; + case kAudioUnitGetPropertyInfoSelect: return (AudioComponentMethod)AUMethodGetPropertyInfo; + case kAudioUnitGetPropertySelect: return (AudioComponentMethod)AUMethodGetProperty; + case kAudioUnitSetPropertySelect: return (AudioComponentMethod)AUMethodSetProperty; + case kAudioUnitAddPropertyListenerSelect:return (AudioComponentMethod)AUMethodAddPropertyListener; + case kAudioUnitRemovePropertyListenerSelect: + return (AudioComponentMethod)AUMethodRemovePropertyListener; + case kAudioUnitRemovePropertyListenerWithUserDataSelect: + return (AudioComponentMethod)AUMethodRemovePropertyListenerWithUserData; + case kAudioUnitAddRenderNotifySelect: return (AudioComponentMethod)AUMethodAddRenderNotify; + case kAudioUnitRemoveRenderNotifySelect:return (AudioComponentMethod)AUMethodRemoveRenderNotify; + case kAudioUnitGetParameterSelect: return (AudioComponentMethod)AUMethodGetParameter; + case kAudioUnitSetParameterSelect: return (AudioComponentMethod)AUMethodSetParameter; + case kAudioUnitScheduleParametersSelect:return (AudioComponentMethod)AUMethodScheduleParameters; + case kAudioUnitRenderSelect: return (AudioComponentMethod)AUMethodRender; + case kAudioUnitResetSelect: return (AudioComponentMethod)AUMethodReset; + default: + break; + } + return NULL; +} + +AudioComponentMethod AUOutputLookup::Lookup (SInt16 selector) +{ + AudioComponentMethod method = AUBaseLookup::Lookup(selector); + if (method) return method; + + switch (selector) { + case kAudioOutputUnitStartSelect: return (AudioComponentMethod)AUMethodStart; + case kAudioOutputUnitStopSelect: return (AudioComponentMethod)AUMethodStop; + default: + break; + } + return NULL; +} + +AudioComponentMethod AUComplexOutputLookup::Lookup (SInt16 selector) +{ + AudioComponentMethod method = AUBaseLookup::Lookup(selector); + if (method) return method; + + method = AUOutputLookup::Lookup(selector); + if (method) return method; + + if (selector == kAudioUnitComplexRenderSelect) + return (AudioComponentMethod)AUMethodComplexRender; + return NULL; +} + +AudioComponentMethod AUBaseProcessLookup::Lookup (SInt16 selector) +{ + AudioComponentMethod method = AUBaseLookup::Lookup(selector); + if (method) return method; + + if (selector == kAudioUnitProcessSelect) + return (AudioComponentMethod)AUMethodProcess; + + return NULL; +} + +AudioComponentMethod AUBaseProcessMultipleLookup::Lookup (SInt16 selector) +{ + AudioComponentMethod method = AUBaseLookup::Lookup(selector); + if (method) return method; + + if (selector == kAudioUnitProcessMultipleSelect) + return (AudioComponentMethod)AUMethodProcessMultiple; + + return NULL; +} + +AudioComponentMethod AUBaseProcessAndMultipleLookup::Lookup (SInt16 selector) +{ + AudioComponentMethod method = AUBaseLookup::Lookup(selector); + if (method) return method; + + method = AUBaseProcessMultipleLookup::Lookup(selector); + if (method) return method; + + method = AUBaseProcessLookup::Lookup(selector); + if (method) return method; + + return NULL; +} + +#if !CA_BASIC_AU_FEATURES +inline AudioComponentMethod MIDI_Lookup (SInt16 selector) +{ + switch (selector) { + case kMusicDeviceMIDIEventSelect: return (AudioComponentMethod)AUMethodMIDIEvent; + case kMusicDeviceSysExSelect: return (AudioComponentMethod)AUMethodSysEx; + default: + break; + } + return NULL; +} + +AudioComponentMethod AUMIDILookup::Lookup (SInt16 selector) +{ + AudioComponentMethod method = AUBaseLookup::Lookup(selector); + if (method) return method; + + return MIDI_Lookup(selector); +} + +AudioComponentMethod AUMIDIProcessLookup::Lookup (SInt16 selector) +{ + AudioComponentMethod method = AUBaseProcessLookup::Lookup(selector); + if (method) return method; + + return MIDI_Lookup(selector); +} + +AudioComponentMethod AUMusicLookup::Lookup (SInt16 selector) +{ + AudioComponentMethod method = AUBaseLookup::Lookup(selector); + if (method) return method; + + switch (selector) { + case kMusicDeviceStartNoteSelect: return (AudioComponentMethod)AUMethodStartNote; + case kMusicDeviceStopNoteSelect: return (AudioComponentMethod)AUMethodStopNote; +#if !TARGET_OS_IPHONE + case kMusicDevicePrepareInstrumentSelect: return (AudioComponentMethod)AUMethodPrepareInstrument; + case kMusicDeviceReleaseInstrumentSelect: return (AudioComponentMethod)AUMethodReleaseInstrument; +#endif + default: + break; + } + return MIDI_Lookup (selector); +} + +AudioComponentMethod AUAuxBaseLookup::Lookup (SInt16 selector) +{ + switch (selector) { + case kAudioUnitGetPropertyInfoSelect: return (AudioComponentMethod)AUMethodGetPropertyInfo; + case kAudioUnitGetPropertySelect: return (AudioComponentMethod)AUMethodGetProperty; + case kAudioUnitSetPropertySelect: return (AudioComponentMethod)AUMethodSetProperty; + + case kAudioUnitGetParameterSelect: return (AudioComponentMethod)AUMethodGetParameter; + case kAudioUnitSetParameterSelect: return (AudioComponentMethod)AUMethodSetParameter; + + default: + break; + } + return NULL; +} +#endif + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUPlugInDispatch.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUPlugInDispatch.h new file mode 100644 index 0000000000..6ebea75fd6 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUPlugInDispatch.h @@ -0,0 +1,144 @@ +/* + File: AUPlugInDispatch.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUPlugInBase_h__ +#define __AUPlugInBase_h__ + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include <AudioUnit/AudioComponent.h> + #if !CA_BASIC_AU_FEATURES + #include <AudioUnit/MusicDevice.h> + #endif +#else + #include "AudioComponent.h" + #include "MusicDevice.h" +#endif + +#include "ComponentBase.h" + +struct AUBaseLookup { + static AudioComponentMethod Lookup (SInt16 selector); +}; +template <class Implementor> +class AUBaseFactory : public APFactory<AUBaseLookup, Implementor> +{ +}; + +struct AUOutputLookup { + static AudioComponentMethod Lookup (SInt16 selector); +}; +template <class Implementor> +class AUOutputBaseFactory : public APFactory<AUOutputLookup, Implementor> +{ +}; + +struct AUComplexOutputLookup { + static AudioComponentMethod Lookup (SInt16 selector); +}; +template <class Implementor> +class AUOutputComplexBaseFactory : public APFactory<AUComplexOutputLookup, Implementor> +{ +}; + +struct AUBaseProcessLookup { + static AudioComponentMethod Lookup (SInt16 selector); +}; +template <class Implementor> +class AUBaseProcessFactory : public APFactory<AUBaseProcessLookup, Implementor> +{ +}; + +struct AUBaseProcessMultipleLookup { + static AudioComponentMethod Lookup (SInt16 selector); +}; +template <class Implementor> +class AUBaseProcessMultipleFactory : public APFactory<AUBaseProcessMultipleLookup, Implementor> +{ +}; + +struct AUBaseProcessAndMultipleLookup { + static AudioComponentMethod Lookup (SInt16 selector); +}; +template <class Implementor> +class AUBaseProcessAndMultipleFactory : public APFactory<AUBaseProcessAndMultipleLookup, Implementor> +{ +}; + +#if !CA_BASIC_AU_FEATURES +struct AUMIDILookup { + static AudioComponentMethod Lookup (SInt16 selector); +}; +template <class Implementor> +class AUMIDIEffectFactory : public APFactory<AUMIDILookup, Implementor> +{ +}; + +struct AUMIDIProcessLookup { + static AudioComponentMethod Lookup (SInt16 selector); +}; +template <class Implementor> +class AUMIDIProcessFactory : public APFactory<AUMIDIProcessLookup, Implementor> +{ +}; + +struct AUMusicLookup { + static AudioComponentMethod Lookup (SInt16 selector); +}; +template <class Implementor> +class AUMusicDeviceFactory : public APFactory<AUMusicLookup, Implementor> +{ +}; + +struct AUAuxBaseLookup { + static AudioComponentMethod Lookup (SInt16 selector); +}; +template <class Implementor> +class AUAuxBaseFactory : public APFactory<AUAuxBaseLookup, Implementor> +{ +}; +#endif // CA_BASIC_AU_FEATURES + +#endif // __AUPlugInBase_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUResources.r b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUResources.r new file mode 100644 index 0000000000..55040fc396 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUResources.r @@ -0,0 +1,140 @@ +/* + File: AUResources.r + Abstract: AUResources.r + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// AUResources.r +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +/* sample macro definitions -- all of these symbols must be defined +#define RES_ID kHALOutputResID +#define COMP_TYPE kAudioUnitComponentType +#define COMP_SUBTYPE kAudioUnitOutputSubType +#define COMP_MANUF kAudioUnitAudioHardwareOutputSubSubType +#define VERSION 0x00010000 +#define NAME "AudioHALOutput" +#define DESCRIPTION "Audio hardware output AudioUnit" +#define ENTRY_POINT "AUHALEntry" +*/ +#define UseExtendedThingResource 1 + +#include <CoreServices/CoreServices.r> + +// this is a define used to indicate that a component has no static data that would mean +// that no more than one instance could be open at a time - never been true for AUs +#ifndef cmpThreadSafeOnMac +#define cmpThreadSafeOnMac 0x10000000 +#endif + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +resource 'STR ' (RES_ID, purgeable) { + NAME +}; + +resource 'STR ' (RES_ID + 1, purgeable) { + DESCRIPTION +}; + +resource 'dlle' (RES_ID) { + ENTRY_POINT +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +resource 'thng' (RES_ID, NAME) { + COMP_TYPE, + COMP_SUBTYPE, + COMP_MANUF, + 0, 0, 0, 0, // no 68K + 'STR ', RES_ID, + 'STR ', RES_ID + 1, + 0, 0, /* icon */ + VERSION, + componentHasMultiplePlatforms | componentDoAutoVersion, + 0, + { + #if defined(ppc_YES) + cmpThreadSafeOnMac, + 'dlle', RES_ID, platformPowerPCNativeEntryPoint + #define NeedLeadingComma 1 + #endif + #if defined(ppc64_YES) + #if defined(NeedLeadingComma) + , + #endif + cmpThreadSafeOnMac, + 'dlle', RES_ID, platformPowerPC64NativeEntryPoint + #define NeedLeadingComma 1 + #endif + #if defined(i386_YES) + #if defined(NeedLeadingComma) + , + #endif + cmpThreadSafeOnMac, + 'dlle', RES_ID, platformIA32NativeEntryPoint + #define NeedLeadingComma 1 + #endif + #if defined(x86_64_YES) + #if defined(NeedLeadingComma) + , + #endif + cmpThreadSafeOnMac, + 'dlle', RES_ID, 8 + #define NeedLeadingComma 1 + #endif + } +}; + +#undef RES_ID +#undef COMP_TYPE +#undef COMP_SUBTYPE +#undef COMP_MANUF +#undef VERSION +#undef NAME +#undef DESCRIPTION +#undef ENTRY_POINT +#undef NeedLeadingComma diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUScopeElement.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUScopeElement.cpp new file mode 100644 index 0000000000..24bd18e434 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUScopeElement.cpp @@ -0,0 +1,565 @@ +/* + File: AUScopeElement.cpp + Abstract: AUScopeElement.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUScopeElement.h" +#include "AUBase.h" + +//_____________________________________________________________________________ +// +// By default, parameterIDs may be arbitrarily spaced, and an STL 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(int inNumberOfParameters) +{ + mIndexedParameters.resize (inNumberOfParameters); + mUseIndexedParameters = true; +} + +//_____________________________________________________________________________ +// +// Helper method. +// returns the ParameterMapEvent object associated with the paramID +// +inline ParameterMapEvent& AUElement::GetParamEvent(AudioUnitParameterID paramID) +{ + ParameterMapEvent *event; + + if(mUseIndexedParameters) + { + if(paramID >= mIndexedParameters.size() ) + COMPONENT_THROW(kAudioUnitErr_InvalidParameter); + + event = &mIndexedParameters[paramID]; + } + else + { + ParameterMap::iterator i = mParameters.find(paramID); + if (i == mParameters.end()) + COMPONENT_THROW(kAudioUnitErr_InvalidParameter); + + event = &(*i).second; + } + + return *event; +} + +//_____________________________________________________________________________ +// +// Helper method. +// returns whether the specified paramID is known to the element +// +bool AUElement::HasParameterID (AudioUnitParameterID paramID) const +{ + if(mUseIndexedParameters) + { + if(paramID >= mIndexedParameters.size() ) + return false; + + return true; + } + + ParameterMap::const_iterator i = mParameters.find(paramID); + if (i == mParameters.end()) + return false; + + return true; +} + +//_____________________________________________________________________________ +// +// caller assumes that this is actually an immediate parameter +// +AudioUnitParameterValue AUElement::GetParameter(AudioUnitParameterID paramID) +{ + ParameterMapEvent &event = GetParamEvent(paramID); + + return event.GetValue(); +} + + +//_____________________________________________________________________________ +// +void AUElement::GetRampSliceStartEnd( AudioUnitParameterID paramID, + AudioUnitParameterValue & outStartValue, + AudioUnitParameterValue & outEndValue, + AudioUnitParameterValue & outValuePerFrameDelta ) + +{ + ParameterMapEvent &event = GetParamEvent(paramID); + + // works even if the value is constant (immediate parameter value) + event.GetRampSliceStartEnd(outStartValue, outEndValue, outValuePerFrameDelta ); +} + +//_____________________________________________________________________________ +// +AudioUnitParameterValue AUElement::GetEndValue( AudioUnitParameterID paramID) + +{ + ParameterMapEvent &event = GetParamEvent(paramID); + + // works even if the value is constant (immediate parameter value) + return event.GetEndValue(); +} + +//_____________________________________________________________________________ +// +void AUElement::SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue inValue, bool okWhenInitialized) +{ + if(mUseIndexedParameters) + { + ParameterMapEvent &event = GetParamEvent(paramID); + event.SetValue(inValue); + } + else + { + ParameterMap::iterator 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. + // COMPONENT_THROW(kAudioUnitErr_InvalidParameter); +#if DEBUG + fprintf(stderr, "WARNING: %s SetParameter for undefined param ID %d while initialized. Ignoring..\n", + mAudioUnit->GetLoggingString(), (int)paramID); +#endif + } else { + // create new entry in map for the paramID (only happens first time) + ParameterMapEvent event(inValue); + mParameters[paramID] = event; + } + } + else + { + // paramID already exists in map so simply change its value + ParameterMapEvent &event = (*i).second; + event.SetValue(inValue); + } + } +} + +//_____________________________________________________________________________ +// +void AUElement::SetScheduledEvent( AudioUnitParameterID paramID, + const AudioUnitParameterEvent &inEvent, + UInt32 inSliceOffsetInBuffer, + UInt32 inSliceDurationFrames, + bool okWhenInitialized ) +{ + if(mUseIndexedParameters) + { + ParameterMapEvent &event = GetParamEvent(paramID); + event.SetScheduledEvent(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames ); + } + else + { + ParameterMap::iterator 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. + // COMPONENT_THROW(kAudioUnitErr_InvalidParameter); +#if DEBUG + fprintf(stderr, "WARNING: %s SetScheduledEvent for undefined param ID %d while initialized. Ignoring..\n", + mAudioUnit->GetLoggingString(), (int)paramID); +#endif + } else { + // create new entry in map for the paramID (only happens first time) + ParameterMapEvent event(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames); + mParameters[paramID] = event; + } + } + else + { + // paramID already exists in map so simply change its value + ParameterMapEvent &event = (*i).second; + + event.SetScheduledEvent(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames ); + } + } +} + + + +//_____________________________________________________________________________ +// +void AUElement::GetParameterList(AudioUnitParameterID *outList) +{ + if(mUseIndexedParameters) + { + UInt32 nparams = static_cast<UInt32>(mIndexedParameters.size()); + for (UInt32 i = 0; i < nparams; i++ ) + *outList++ = (AudioUnitParameterID)i; + } + else + { + for (ParameterMap::iterator i = mParameters.begin(); i != mParameters.end(); ++i) + *outList++ = (*i).first; + } +} + +//_____________________________________________________________________________ +// +void AUElement::SaveState(CFMutableDataRef data) +{ + if(mUseIndexedParameters) + { + UInt32 nparams = static_cast<UInt32>(mIndexedParameters.size()); + UInt32 theData = CFSwapInt32HostToBig(nparams); + CFDataAppendBytes(data, (UInt8 *)&theData, sizeof(nparams)); + + for (UInt32 i = 0; i < nparams; i++) + { + struct { + UInt32 paramID; + //CFSwappedFloat32 value; crashes gcc3 PFE + UInt32 value; // really a big-endian float + } entry; + + entry.paramID = CFSwapInt32HostToBig(i); + + AudioUnitParameterValue v = mIndexedParameters[i].GetValue(); + entry.value = CFSwapInt32HostToBig(*(UInt32 *)&v ); + + CFDataAppendBytes(data, (UInt8 *)&entry, sizeof(entry)); + } + } + else + { + UInt32 nparams = CFSwapInt32HostToBig(static_cast<uint32_t>(mParameters.size())); + CFDataAppendBytes(data, (UInt8 *)&nparams, sizeof(nparams)); + + for (ParameterMap::iterator i = mParameters.begin(); i != mParameters.end(); ++i) { + struct { + UInt32 paramID; + //CFSwappedFloat32 value; crashes gcc3 PFE + UInt32 value; // really a big-endian float + } entry; + + entry.paramID = CFSwapInt32HostToBig((*i).first); + + AudioUnitParameterValue v = (*i).second.GetValue(); + entry.value = CFSwapInt32HostToBig(*(UInt32 *)&v ); + + CFDataAppendBytes(data, (UInt8 *)&entry, sizeof(entry)); + } + } +} + +//_____________________________________________________________________________ +// +const UInt8 * AUElement::RestoreState(const UInt8 *state) +{ + union FloatInt32 { UInt32 i; AudioUnitParameterValue f; }; + const UInt8 *p = state; + UInt32 nparams = CFSwapInt32BigToHost(*(UInt32 *)p); + p += sizeof(UInt32); + + for (UInt32 i = 0; i < nparams; ++i) { + struct { + AudioUnitParameterID paramID; + AudioUnitParameterValue value; + } entry; + + entry.paramID = CFSwapInt32BigToHost(*(UInt32 *)p); + p += sizeof(UInt32); + FloatInt32 temp; + temp.i = CFSwapInt32BigToHost(*(UInt32 *)p); + entry.value = temp.f; + p += sizeof(AudioUnitParameterValue); + + SetParameter(entry.paramID, entry.value); + } + return p; +} + +//_____________________________________________________________________________ +// +void AUElement::SetName (CFStringRef inName) +{ + if (mElementName) CFRelease (mElementName); + mElementName = inName; + if (mElementName) CFRetain (mElementName); +} + + +//_____________________________________________________________________________ +// +AUIOElement::AUIOElement(AUBase *audioUnit) : + AUElement(audioUnit), + mWillAllocate (true) +{ + mStreamFormat.SetAUCanonical(2, // stereo + audioUnit->AudioUnitAPIVersion() == 1); + // interleaved if API version 1, deinterleaved if version 2 + mStreamFormat.mSampleRate = kAUDefaultSampleRate; +} + +//_____________________________________________________________________________ +// +OSStatus AUIOElement::SetStreamFormat(const CAStreamBasicDescription &desc) +{ + mStreamFormat = desc; + return AUBase::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(); + +// printf ("will allocate: %d\n", (int)((mWillAllocate && NeedsBufferSpace()) ? framesToAllocate : 0)); + + mIOBuffer.Allocate(mStreamFormat, (mWillAllocate && NeedsBufferSpace()) ? framesToAllocate : 0); + } +} + +//_____________________________________________________________________________ +// +void AUIOElement::DeallocateBuffer() +{ + mIOBuffer.Deallocate(); +} + +//_____________________________________________________________________________ +// +// AudioChannelLayout support + +// outLayoutTagsPtr WILL be NULL if called to find out how many +// layouts that Audio Unit will report +// return 0 (ie. NO channel layouts) if the AU doesn't require channel layout knowledge +UInt32 AUIOElement::GetChannelLayoutTags (AudioChannelLayoutTag *outLayoutTagsPtr) +{ + return 0; +} + +// As the AudioChannelLayout can be a variable length structure +// (though in most cases it won't be!!!) +// The size of the ACL is always returned by the method +// if outMapPtr is NOT-NULL, then AU should copy into this pointer (outMapPtr) the current ACL that it has in use. +// the AU should also return whether the property is writable (that is the client can provide any arbitrary ACL that the audio unit will then honour) +// or if the property is read only - which is the generally preferred mode. +// If the AU doesn't require an AudioChannelLayout, then just return 0. +UInt32 AUIOElement::GetAudioChannelLayout (AudioChannelLayout *outMapPtr, + Boolean &outWritable) +{ + return 0; +} + +// 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 &inData) +{ + return kAudioUnitErr_InvalidProperty; +} + +// 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 () +{ + return kAudioUnitErr_InvalidPropertyValue; +} + + +//_____________________________________________________________________________ +// +AUScope::~AUScope() +{ + for (ElementVector::iterator it = mElements.begin(); it != mElements.end(); ++it) + delete *it; +} + +//_____________________________________________________________________________ +// +void AUScope::SetNumberOfElements(UInt32 numElements) +{ + if (mDelegate) + return mDelegate->SetNumberOfElements(numElements); + + if (numElements > mElements.size()) { + mElements.reserve(numElements); + while (numElements > mElements.size()) { + AUElement *elem = mCreator->CreateElement(GetScope(), static_cast<UInt32>(mElements.size())); + mElements.push_back(elem); + } + } else + while (numElements < mElements.size()) { + AUElement *elem = mElements.back(); + mElements.pop_back(); + delete elem; + } +} + +//_____________________________________________________________________________ +// +bool AUScope::HasElementWithName () const +{ + for (UInt32 i = 0; i < GetNumberOfElements(); ++i) { + AUElement * el = const_cast<AUScope*>(this)->GetElement (i); + if (el && el->HasName()) { + return true; + } + } + return false; +} + +//_____________________________________________________________________________ +// + +void AUScope::AddElementNamesToDict (CFMutableDictionaryRef & inNameDict) +{ + if (HasElementWithName()) + { + static char string[32]; + CFMutableDictionaryRef elementDict = CFDictionaryCreateMutable (NULL, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFStringRef str; + for (UInt32 i = 0; i < GetNumberOfElements(); ++i) { + AUElement * el = GetElement (i); + if (el && el->HasName()) { + snprintf (string, sizeof(string), "%d", int(i)); + str = CFStringCreateWithCString (NULL, string, kCFStringEncodingASCII); + CFDictionarySetValue (elementDict, str, el->GetName()); + CFRelease (str); + } + } + + snprintf (string, sizeof(string), "%d", int(mScope)); + str = CFStringCreateWithCString (NULL, string, kCFStringEncodingASCII); + CFDictionarySetValue (inNameDict, str, elementDict); + CFRelease (str); + CFRelease (elementDict); + } +} + +//_____________________________________________________________________________ +// +bool AUScope::RestoreElementNames (CFDictionaryRef& inNameDict) +{ + static char string[32]; + + //first we have to see if we have enough elements + bool didAddElements = false; + unsigned int maxElNum = GetNumberOfElements(); + + int dictSize = static_cast<int>(CFDictionaryGetCount(inNameDict)); + CFStringRef * keys = (CFStringRef*)CA_malloc (dictSize * sizeof (CFStringRef)); + CFDictionaryGetKeysAndValues (inNameDict, reinterpret_cast<const void**>(keys), NULL); + for (int i = 0; i < dictSize; i++) + { + unsigned int intKey = 0; + CFStringGetCString (keys[i], string, 32, kCFStringEncodingASCII); + int result = sscanf (string, "%u", &intKey); + // check if sscanf succeeded and element index is less than max elements. + if (result && UInt32(intKey) < maxElNum) + { + CFStringRef elName = reinterpret_cast<CFStringRef>(CFDictionaryGetValue (inNameDict, keys[i])); + AUElement* element = GetElement (intKey); + if (element) + element->SetName (elName); + } + } + free (keys); + + return didAddElements; +} + +void AUScope::SaveState(CFMutableDataRef data) +{ + AudioUnitElement nElems = GetNumberOfElements(); + for (AudioUnitElement ielem = 0; ielem < nElems; ++ielem) { + AUElement *element = GetElement(ielem); + UInt32 nparams = element->GetNumberOfParameters(); + if (nparams > 0) { + struct { + UInt32 scope; + UInt32 element; + } hdr; + + hdr.scope = CFSwapInt32HostToBig(GetScope()); + hdr.element = CFSwapInt32HostToBig(ielem); + CFDataAppendBytes(data, (UInt8 *)&hdr, sizeof(hdr)); + + element->SaveState(data); + } + } +} + +const UInt8 * AUScope::RestoreState(const UInt8 *state) +{ + const UInt8 *p = state; + UInt32 elementIdx = CFSwapInt32BigToHost(*(UInt32 *)p); p += sizeof(UInt32); + AUElement *element = GetElement(elementIdx); + if (!element) { + struct { + AudioUnitParameterID paramID; + AudioUnitParameterValue value; + } entry; + UInt32 nparams = CFSwapInt32BigToHost(*(UInt32 *)p); + p += sizeof(UInt32); + + p += nparams * sizeof(entry); + } else + p = element->RestoreState(p); + + return p; +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUScopeElement.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUScopeElement.h new file mode 100644 index 0000000000..47ebe2f338 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/AUScopeElement.h @@ -0,0 +1,553 @@ +/* + File: AUScopeElement.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUScopeElement_h__ +#define __AUScopeElement_h__ + +#include <map> +#include <vector> + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include <AudioUnit/AudioUnit.h> +#else + #include <AudioUnit.h> +#endif +#include "ComponentBase.h" +#include "AUBuffer.h" + + +class AUBase; + +// ____________________________________________________________________________ +// +// represents a parameter's value (either constant or ramped) +/*! @class ParameterMapEvent */ +class ParameterMapEvent +{ +public: +/*! @ctor ParameterMapEvent */ + ParameterMapEvent() + : mEventType(kParameterEvent_Immediate), mBufferOffset(0), mDurationInFrames(0), mValue1(0.0f), mValue2(0.0f), mSliceDurationFrames(0) + {} + +/*! @ctor ParameterMapEvent */ + ParameterMapEvent(AudioUnitParameterValue inValue) + : mEventType(kParameterEvent_Immediate), mBufferOffset(0), mDurationInFrames(0), mValue1(inValue), mValue2(inValue), mSliceDurationFrames(0) + {} + + // constructor for scheduled event +/*! @ctor ParameterMapEvent */ + ParameterMapEvent( const AudioUnitParameterEvent &inEvent, + UInt32 inSliceOffsetInBuffer, + UInt32 inSliceDurationFrames ) + { + SetScheduledEvent(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames ); + }; + +/*! @method SetScheduledEvent */ + void SetScheduledEvent( const AudioUnitParameterEvent &inEvent, + UInt32 inSliceOffsetInBuffer, + UInt32 inSliceDurationFrames ) + { + mEventType = inEvent.eventType; + mSliceDurationFrames = inSliceDurationFrames; + + if(mEventType == kParameterEvent_Immediate ) + { + // constant immediate value for the whole slice + mValue1 = inEvent.eventValues.immediate.value; + mValue2 = mValue1; + mDurationInFrames = inSliceDurationFrames; + mBufferOffset = 0; + } + else + { + mDurationInFrames = inEvent.eventValues.ramp.durationInFrames; + mBufferOffset = inEvent.eventValues.ramp.startBufferOffset - inSliceOffsetInBuffer; // shift over for this slice + mValue1 = inEvent.eventValues.ramp.startValue; + mValue2 = inEvent.eventValues.ramp.endValue; + } + }; + + + +/*! @method GetEventType */ + AUParameterEventType GetEventType() const {return mEventType;}; + +/*! @method GetValue */ + AudioUnitParameterValue GetValue() const {return mValue1;}; // only valid if immediate event type +/*! @method GetEndValue */ + AudioUnitParameterValue GetEndValue() const {return mValue2;}; // only valid if immediate event type +/*! @method SetValue */ + void SetValue(AudioUnitParameterValue inValue) + { + mEventType = kParameterEvent_Immediate; + mValue1 = inValue; + mValue2 = inValue; + } + + // interpolates the start and end values corresponding to the current processing slice + // most ramp parameter implementations will want to use this method + // the start value will correspond to the start of the slice + // the end value will correspond to the end of the slice +/*! @method GetRampSliceStartEnd */ + void GetRampSliceStartEnd( AudioUnitParameterValue & outStartValue, + AudioUnitParameterValue & outEndValue, + AudioUnitParameterValue & outValuePerFrameDelta ) + { + if (mEventType == kParameterEvent_Ramped) { + outValuePerFrameDelta = (mValue2 - mValue1) / mDurationInFrames; + + outStartValue = mValue1 + outValuePerFrameDelta * (-mBufferOffset); // corresponds to frame 0 of this slice + outEndValue = outStartValue + outValuePerFrameDelta * mSliceDurationFrames; + } else { + outValuePerFrameDelta = 0; + outStartValue = outEndValue = mValue1; + } + }; + + // Some ramp parameter implementations will want to interpret the ramp using their + // own interpolation method (perhaps non-linear) + // This method gives the raw ramp information, relative to this processing slice + // for the client to interpret as desired +/*! @method GetRampInfo */ + void GetRampInfo( SInt32 & outBufferOffset, + UInt32 & outDurationInFrames, + AudioUnitParameterValue & outStartValue, + AudioUnitParameterValue & outEndValue ) + { + outBufferOffset = mBufferOffset; + outDurationInFrames = mDurationInFrames; + outStartValue = mValue1; + outEndValue = mValue2; + }; + +#if DEBUG + void Print() + { + printf("ParameterEvent @ %p\n", this); + printf(" mEventType = %d\n", (int)mEventType); + printf(" mBufferOffset = %d\n", (int)mBufferOffset); + printf(" mDurationInFrames = %d\n", (int)mDurationInFrames); + printf(" mSliceDurationFrames = %d\n", (int)mSliceDurationFrames); + printf(" mValue1 = %.5f\n", mValue1); + printf(" mValue2 = %.5f\n", mValue2); + } +#endif + +private: + AUParameterEventType mEventType; + + SInt32 mBufferOffset; // ramp start offset relative to start of this slice (may be negative) + UInt32 mDurationInFrames; // total duration of ramp parameter + AudioUnitParameterValue mValue1; // value if immediate : startValue if ramp + AudioUnitParameterValue mValue2; // endValue (only used for ramp) + + UInt32 mSliceDurationFrames; // duration of this processing slice +}; + + + +// ____________________________________________________________________________ +// +class AUIOElement; + +/*! @class AUElement */ +class AUElement { +public: +/*! @ctor AUElement */ + AUElement(AUBase *audioUnit) : mAudioUnit(audioUnit), + mUseIndexedParameters(false), mElementName(0) { } + +/*! @dtor ~AUElement */ + virtual ~AUElement() { if (mElementName) CFRelease (mElementName); } + +/*! @method GetNumberOfParameters */ + virtual UInt32 GetNumberOfParameters() + { + if(mUseIndexedParameters) return static_cast<UInt32>(mIndexedParameters.size()); else return static_cast<UInt32>(mParameters.size()); + } +/*! @method GetParameterList */ + virtual void GetParameterList(AudioUnitParameterID *outList); +/*! @method HasParameterID */ + bool HasParameterID (AudioUnitParameterID paramID) const; + +/*! @method GetParameter */ + AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID); +/*! @method SetParameter */ + 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. + + // interpolates the start and end values corresponding to the current processing slice + // most ramp parameter implementations will want to use this method +/*! @method GetRampSliceStartEnd */ + void GetRampSliceStartEnd( AudioUnitParameterID paramID, + AudioUnitParameterValue & outStartValue, + AudioUnitParameterValue & outEndValue, + AudioUnitParameterValue & outValuePerFrameDelta ); + +/*! @method GetEndValue */ + AudioUnitParameterValue GetEndValue( AudioUnitParameterID paramID); + +/*! @method SetRampParameter */ + void SetScheduledEvent( AudioUnitParameterID paramID, + const AudioUnitParameterEvent &inEvent, + UInt32 inSliceOffsetInBuffer, + UInt32 inSliceDurationFrames, + 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. + + +/*! @method GetAudioUnit */ + AUBase * GetAudioUnit() const { return mAudioUnit; }; + +/*! @method SaveState */ + void SaveState(CFMutableDataRef data); +/*! @method RestoreState */ + const UInt8 * RestoreState(const UInt8 *state); +/*! @method GetName */ + CFStringRef GetName () const { return mElementName; } +/*! @method SetName */ + void SetName (CFStringRef inName); +/*! @method HasName */ + bool HasName () const { return mElementName != 0; } +/*! @method UseIndexedParameters */ + virtual void UseIndexedParameters(int inNumberOfParameters); + +/*! @method AsIOElement*/ + virtual AUIOElement* AsIOElement () { return NULL; } + +protected: + inline ParameterMapEvent& GetParamEvent(AudioUnitParameterID paramID); + +private: + typedef std::map<AudioUnitParameterID, ParameterMapEvent, std::less<AudioUnitParameterID> > ParameterMap; + +/*! @var mAudioUnit */ + AUBase * mAudioUnit; +/*! @var mParameters */ + ParameterMap mParameters; + +/*! @var mUseIndexedParameters */ + bool mUseIndexedParameters; +/*! @var mIndexedParameters */ + std::vector<ParameterMapEvent> mIndexedParameters; + +/*! @var mElementName */ + CFStringRef mElementName; +}; + + + +// ____________________________________________________________________________ +// +/*! @class AUIOElement */ +class AUIOElement : public AUElement { +public: +/*! @ctor AUIOElement */ + AUIOElement(AUBase *audioUnit); + +/*! @method GetStreamFormat */ + const CAStreamBasicDescription &GetStreamFormat() const { return mStreamFormat; } + +/*! @method SetStreamFormat */ + virtual OSStatus SetStreamFormat(const CAStreamBasicDescription &desc); + +/*! @method AllocateBuffer */ + virtual void AllocateBuffer(UInt32 inFramesToAllocate = 0); +/*! @method DeallocateBuffer */ + void DeallocateBuffer(); +/*! @method NeedsBufferSpace */ + virtual bool NeedsBufferSpace() const = 0; + +/*! @method SetWillAllocateBuffer */ + void SetWillAllocateBuffer(bool inFlag) { + mWillAllocate = inFlag; + } +/*! @method WillAllocateBuffer */ + bool WillAllocateBuffer() const { + return mWillAllocate; + } + +/*! @method UseExternalBuffer */ + void UseExternalBuffer(const AudioUnitExternalBuffer &buf) { + mIOBuffer.UseExternalBuffer(mStreamFormat, buf); + } +/*! @method PrepareBuffer */ + AudioBufferList & PrepareBuffer(UInt32 nFrames) { + if (mWillAllocate) + return mIOBuffer.PrepareBuffer(mStreamFormat, nFrames); + throw OSStatus(kAudioUnitErr_InvalidPropertyValue); + } +/*! @method PrepareNullBuffer */ + AudioBufferList & PrepareNullBuffer(UInt32 nFrames) { + return mIOBuffer.PrepareNullBuffer(mStreamFormat, nFrames); + } +/*! @method SetBufferList */ + AudioBufferList & SetBufferList(AudioBufferList &abl) { return mIOBuffer.SetBufferList(abl); } +/*! @method SetBuffer */ + void SetBuffer(UInt32 index, AudioBuffer &ab) { mIOBuffer.SetBuffer(index, ab); } +/*! @method InvalidateBufferList */ + void InvalidateBufferList() { mIOBuffer.InvalidateBufferList(); } + +/*! @method GetBufferList */ + AudioBufferList & GetBufferList() const { return mIOBuffer.GetBufferList(); } + +/*! @method GetChannelData */ + AudioUnitSampleType * GetChannelData(int ch) const { + if (mStreamFormat.IsInterleaved()) + return static_cast<AudioUnitSampleType *>(mIOBuffer.GetBufferList().mBuffers[0].mData) + ch; + else + return static_cast<AudioUnitSampleType *>(mIOBuffer.GetBufferList().mBuffers[ch].mData); + } + Float32 * GetFloat32ChannelData(int ch) const { + if (mStreamFormat.IsInterleaved()) + return static_cast<Float32 *>(mIOBuffer.GetBufferList().mBuffers[0].mData) + ch; + else + return static_cast<Float32 *>(mIOBuffer.GetBufferList().mBuffers[ch].mData); + } + SInt32 * GetSInt32ChannelData(int ch) const { + if (mStreamFormat.IsInterleaved()) + return static_cast<SInt32 *>(mIOBuffer.GetBufferList().mBuffers[0].mData) + ch; + else + return static_cast<SInt32 *>(mIOBuffer.GetBufferList().mBuffers[ch].mData); + } + SInt16 * GetInt16ChannelData(int ch) const { + if (mStreamFormat.IsInterleaved()) + return static_cast<SInt16 *>(mIOBuffer.GetBufferList().mBuffers[0].mData) + ch; + else + return static_cast<SInt16 *>(mIOBuffer.GetBufferList().mBuffers[ch].mData); + } + +/*! @method CopyBufferListTo */ + void CopyBufferListTo(AudioBufferList &abl) const { + mIOBuffer.CopyBufferListTo(abl); + } +/*! @method CopyBufferContentsTo */ + void CopyBufferContentsTo(AudioBufferList &abl) const { + mIOBuffer.CopyBufferContentsTo(abl); + } + +/* UInt32 BytesToFrames(UInt32 nBytes) { return nBytes / mStreamFormat.mBytesPerFrame; } + UInt32 BytesToFrames(AudioBufferList &abl) { + return BytesToFrames(abl.mBuffers[0].mDataByteSize); + } + UInt32 FramesToBytes(UInt32 nFrames) { return nFrames * mStreamFormat.mBytesPerFrame; }*/ + +/*! @method IsInterleaved */ + bool IsInterleaved() const { return mStreamFormat.IsInterleaved(); } +/*! @method NumberChannels */ + UInt32 NumberChannels() const { return mStreamFormat.NumberChannels(); } +/*! @method NumberInterleavedChannels */ + UInt32 NumberInterleavedChannels() const { return mStreamFormat.NumberInterleavedChannels(); } + +/*! @method GetChannelMapTags */ + virtual UInt32 GetChannelLayoutTags (AudioChannelLayoutTag *outLayoutTagsPtr); + +/*! @method GetAudioChannelLayout */ + virtual UInt32 GetAudioChannelLayout (AudioChannelLayout *outMapPtr, Boolean &outWritable); + +/*! @method SetAudioChannelLayout */ + virtual OSStatus SetAudioChannelLayout (const AudioChannelLayout &inData); + +/*! @method RemoveAudioChannelLayout */ + virtual OSStatus RemoveAudioChannelLayout (); + +/*! @method AsIOElement*/ + virtual AUIOElement* AsIOElement () { return this; } + +protected: +/*! @var mStreamFormat */ + CAStreamBasicDescription mStreamFormat; +/*! @var mIOBuffer */ + AUBufferList mIOBuffer; // for input: input proc buffer, only allocated when needed + // for output: output cache, usually allocated early on +/*! @var mWillAllocate */ + bool mWillAllocate; +}; + +// ____________________________________________________________________________ +// +// AUScopeDelegates are a way to get virtual scopes. +/*! @class AUScopeDelegate */ +class AUScopeDelegate { +public: +/*! @ctor AUScopeDelegate */ + AUScopeDelegate() : mCreator(NULL), mScope(0) { } +/*! @dtor ~AUScopeDelegate */ + virtual ~AUScopeDelegate() {} + +/*! @method Initialize */ + void Initialize( AUBase *creator, + AudioUnitScope scope, + UInt32 numElements) + { + mCreator = creator; + mScope = scope; + SetNumberOfElements(numElements); + } + +/*! @method SetNumberOfElements */ + virtual void SetNumberOfElements(UInt32 numElements) = 0; + +/*! @method GetNumberOfElements */ + virtual UInt32 GetNumberOfElements() = 0; + +/*! @method GetElement */ + virtual AUElement * GetElement(UInt32 elementIndex) = 0; + + AUBase * GetCreator() const { return mCreator; } + AudioUnitScope GetScope() const { return mScope; } + + +private: +/*! @var mCreator */ + AUBase * mCreator; +/*! @var mScope */ + AudioUnitScope mScope; +}; + + + +// ____________________________________________________________________________ +// +/*! @class AUScope */ +class AUScope { +public: +/*! @ctor AUScope */ + AUScope() : mCreator(NULL), mScope(0), mDelegate(0) { } +/*! @dtor ~AUScope */ + ~AUScope(); + +/*! @method Initialize */ + void Initialize(AUBase *creator, + AudioUnitScope scope, + UInt32 numElements) + { + mCreator = creator; + mScope = scope; + + if (mDelegate) + return mDelegate->Initialize(creator, scope, numElements); + + SetNumberOfElements(numElements); + } + +/*! @method SetNumberOfElements */ + void SetNumberOfElements(UInt32 numElements); + +/*! @method GetNumberOfElements */ + UInt32 GetNumberOfElements() const + { + if (mDelegate) + return mDelegate->GetNumberOfElements(); + + return static_cast<UInt32>(mElements.size()); + } + +/*! @method GetElement */ + AUElement * GetElement(UInt32 elementIndex) const + { + if (mDelegate) + return mDelegate->GetElement(elementIndex); + + ElementVector::const_iterator i = mElements.begin() + elementIndex; + // catch passing -1 in as the elementIndex - causes a wrap around + return (i >= mElements.end() || i < mElements.begin()) ? NULL : *i; + } + +/*! @method SafeGetElement */ + AUElement * SafeGetElement(UInt32 elementIndex) + { + AUElement *element = GetElement(elementIndex); + if (element == NULL) + COMPONENT_THROW(kAudioUnitErr_InvalidElement); + return element; + } + +/*! @method GetIOElement */ + AUIOElement * GetIOElement(UInt32 elementIndex) const + { + AUElement *element = GetElement(elementIndex); + AUIOElement *ioel = element ? element->AsIOElement () : NULL; + if (!ioel) + COMPONENT_THROW (kAudioUnitErr_InvalidElement); + return ioel; + } + +/*! @method HasElementWithName */ + bool HasElementWithName () const; + +/*! @method AddElementNamesToDict */ + void AddElementNamesToDict (CFMutableDictionaryRef & inNameDict); + + bool RestoreElementNames (CFDictionaryRef& inNameDict); + + AudioUnitScope GetScope() const { return mScope; } + + void SetDelegate(AUScopeDelegate* inDelegate) { mDelegate = inDelegate; } + +/*! @method SaveState */ + void SaveState(CFMutableDataRef data); + +/*! @method RestoreState */ + const UInt8 * RestoreState(const UInt8 *state); + +private: + typedef std::vector<AUElement *> ElementVector; +/*! @var mCreator */ + AUBase * mCreator; +/*! @var mScope */ + AudioUnitScope mScope; +/*! @var mElements */ + ElementVector mElements; +/*! @var mDelegate */ + AUScopeDelegate * mDelegate; +}; + + + +#endif // __AUScopeElement_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/ComponentBase.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/ComponentBase.cpp new file mode 100644 index 0000000000..fc987335dc --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/ComponentBase.cpp @@ -0,0 +1,370 @@ +/* + File: ComponentBase.cpp + Abstract: ComponentBase.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "ComponentBase.h" +#include "CAXException.h" + +#if TARGET_OS_MAC +pthread_mutex_t ComponentInitLocker::sComponentOpenMutex = PTHREAD_MUTEX_INITIALIZER; +pthread_once_t ComponentInitLocker::sOnce = PTHREAD_ONCE_INIT; + +void ComponentInitLocker::InitComponentInitLocker() +{ + // have to do this because OS X lacks PTHREAD_MUTEX_RECURSIVE_INITIALIZER_NP + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&sComponentOpenMutex, &attr); + pthread_mutexattr_destroy(&attr); +} + +#elif TARGET_OS_WIN32 +CAGuard ComponentInitLocker::sComponentOpenGuard("sComponentOpenGuard"); +#endif + +ComponentBase::EInstanceType ComponentBase::sNewInstanceType; + +static OSStatus CB_GetComponentDescription (const AudioComponentInstance inInstance, AudioComponentDescription * outDesc); +#if !CA_USE_AUDIO_PLUGIN_ONLY && !TARGET_OS_WIN32 + static OSStatus CMgr_GetComponentDescription (const AudioComponentInstance inInstance, AudioComponentDescription * outDesc); +#endif + +ComponentBase::ComponentBase(AudioComponentInstance inInstance) + : mComponentInstance(inInstance), + mInstanceType(sNewInstanceType) +{ + GetComponentDescription(); +} + +ComponentBase::~ComponentBase() +{ +} + +void ComponentBase::PostConstructor() +{ +} + +void ComponentBase::PreDestructor() +{ +} + +#define ACPI ((AudioComponentPlugInInstance *)self) +#define ACImp ((ComponentBase *)&ACPI->mInstanceStorage) + +OSStatus ComponentBase::AP_Open(void *self, AudioUnit compInstance) +{ + OSStatus result = noErr; + try { + ComponentInitLocker lock; + + ComponentBase::sNewInstanceType = ComponentBase::kAudioComponentInstance; + ComponentBase *cb = (ComponentBase *)(*ACPI->mConstruct)(&ACPI->mInstanceStorage, compInstance); + cb->PostConstructor(); // allows base class to do additional initialization + // once the derived class is fully constructed + result = noErr; + } + COMPONENT_CATCH + if (result) + delete ACPI; + return result; +} + +OSStatus ComponentBase::AP_Close(void *self) +{ + OSStatus result = noErr; + try { + if (ACImp) { + ACImp->PreDestructor(); + (*ACPI->mDestruct)(&ACPI->mInstanceStorage); + free(self); + } + } + COMPONENT_CATCH + return result; +} + +#if !CA_USE_AUDIO_PLUGIN_ONLY +OSStatus ComponentBase::Version() +{ + return 0x00000001; +} + +OSStatus ComponentBase::ComponentEntryDispatch(ComponentParameters *p, ComponentBase *This) +{ + if (This == NULL) return kAudio_ParamError; + + OSStatus result = noErr; + + switch (p->what) { + case kComponentCloseSelect: + This->PreDestructor(); + delete This; + break; + + case kComponentVersionSelect: + result = This->Version(); + break; + + case kComponentCanDoSelect: + switch (GetSelectorForCanDo(p)) { + case kComponentOpenSelect: + case kComponentCloseSelect: + case kComponentVersionSelect: + case kComponentCanDoSelect: + return 1; + default: + return 0; + } + + default: + result = badComponentSelector; + break; + } + return result; +} + +SInt16 ComponentBase::GetSelectorForCanDo(ComponentParameters *params) +{ + if (params->what != kComponentCanDoSelect) return 0; + + #if TARGET_CPU_X86 + SInt16 sel = params->params[0]; + #elif TARGET_CPU_X86_64 + SInt16 sel = params->params[1]; + #elif TARGET_CPU_PPC + SInt16 sel = (params->params[0] >> 16); + #else + SInt16 sel = params->params[0]; + #endif + + return sel; +/* + printf ("flags:%d, paramSize: %d, what: %d\n\t", params->flags, params->paramSize, params->what); + for (int i = 0; i < params->paramSize; ++i) { + printf ("[%d]:%d(0x%x), ", i, params->params[i], params->params[i]); + } + printf("\n\tsel:%d\n", sel); +*/ +} + +#endif + +#if CA_DO_NOT_USE_AUDIO_COMPONENT +static OSStatus ComponentBase_GetComponentDescription (const AudioComponentInstance & inInstance, AudioComponentDescription &outDesc); +#endif + +AudioComponentDescription ComponentBase::GetComponentDescription() const +{ + AudioComponentDescription desc; + OSStatus result = 1; + + if (IsPluginObject()) { + ca_require_noerr(result = CB_GetComponentDescription (mComponentInstance, &desc), home); + } +#if !CA_USE_AUDIO_PLUGIN_ONLY + else { + ca_require_noerr(result = CMgr_GetComponentDescription (mComponentInstance, &desc), home); + } +#endif + +home: + if (result) + memset (&desc, 0, sizeof(AudioComponentDescription)); + + return desc; +} + +#if CA_USE_AUDIO_PLUGIN_ONLY +// everything we need is there and we should be linking against it +static OSStatus CB_GetComponentDescription (const AudioComponentInstance inInstance, AudioComponentDescription * outDesc) +{ + AudioComponent comp = AudioComponentInstanceGetComponent(inInstance); + if (comp) + return AudioComponentGetDescription(comp, outDesc); + + return kAudio_ParamError; +} + +#elif !TARGET_OS_WIN32 +// these are the direct dependencies on ComponentMgr calls that an AU +// that is a component mgr is dependent on + +// these are dynamically loaded so that these calls will work on Leopard +#include <dlfcn.h> + +static OSStatus CB_GetComponentDescription (const AudioComponentInstance inInstance, AudioComponentDescription * outDesc) +{ + typedef AudioComponent (*AudioComponentInstanceGetComponentProc) (AudioComponentInstance); + static AudioComponentInstanceGetComponentProc aciGCProc = NULL; + + typedef OSStatus (*AudioComponentGetDescriptionProc)(AudioComponent, AudioComponentDescription *); + static AudioComponentGetDescriptionProc acGDProc = NULL; + + static int doneInit = 0; + if (doneInit == 0) { + doneInit = 1; + void* theImage = dlopen("/System/Library/Frameworks/AudioUnit.framework/AudioUnit", RTLD_LAZY); + if (theImage != NULL) + { + aciGCProc = (AudioComponentInstanceGetComponentProc)dlsym (theImage, "AudioComponentInstanceGetComponent"); + if (aciGCProc) { + acGDProc = (AudioComponentGetDescriptionProc)dlsym (theImage, "AudioComponentGetDescription"); + } + } + } + + OSStatus result = kAudio_UnimplementedError; + if (acGDProc && aciGCProc) { + AudioComponent comp = (*aciGCProc)(inInstance); + if (comp) + result = (*acGDProc)(comp, outDesc); + } +#if !CA_USE_AUDIO_PLUGIN_ONLY + else { + result = CMgr_GetComponentDescription (inInstance, outDesc); + } +#endif + + return result; +} + +#if !CA_USE_AUDIO_PLUGIN_ONLY +// these are the direct dependencies on ComponentMgr calls that an AU +// that is a component mgr is dependent on + +// these are dynamically loaded + +#include <CoreServices/CoreServices.h> +#include <AudioUnit/AudioUnit.h> +#include "CAXException.h" +#include "ComponentBase.h" + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Component Manager +// Used for fast dispatch with audio units +typedef Handle (*GetComponentInstanceStorageProc)(ComponentInstance aComponentInstance); +static GetComponentInstanceStorageProc sGetComponentInstanceStorageProc = NULL; + +typedef OSErr (*GetComponentInfoProc)(Component, ComponentDescription *, void*, void*, void*); +static GetComponentInfoProc sGetComponentInfoProc = NULL; + +typedef void (*SetComponentInstanceStorageProc)(ComponentInstance, Handle); +static SetComponentInstanceStorageProc sSetComponentInstanceStorageProc = NULL; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +static void CSInitOnce(void* /*unused*/) +{ + void *theImage = dlopen("/System/Library/Frameworks/CoreServices.framework/CoreServices", RTLD_LAZY); + if (!theImage) return; + + sGetComponentInstanceStorageProc = (GetComponentInstanceStorageProc) dlsym(theImage, "GetComponentInstanceStorage"); + sGetComponentInfoProc = (GetComponentInfoProc)dlsym (theImage, "GetComponentInfo"); + sSetComponentInstanceStorageProc = (SetComponentInstanceStorageProc) dlsym(theImage, "SetComponentInstanceStorage"); +} + +#if TARGET_OS_MAC + +#include <dispatch/dispatch.h> + +static dispatch_once_t sCSInitOnce = 0; + +static void CSInit () +{ + dispatch_once_f(&sCSInitOnce, NULL, CSInitOnce); +} + +#else + +static void CSInit () +{ + static int sDoCSLoad = 1; + if (sDoCSLoad) { + sDoCSLoad = 0; + CSInitOnce(NULL); + } +} + +#endif + +OSStatus CMgr_GetComponentDescription (const AudioComponentInstance inInstance, AudioComponentDescription * outDesc) +{ + CSInit(); + if (sGetComponentInfoProc) + return (*sGetComponentInfoProc)((Component)inInstance, (ComponentDescription*)outDesc, NULL, NULL, NULL); + return kAudio_UnimplementedError; +} + +Handle CMgr_GetComponentInstanceStorage(ComponentInstance aComponentInstance) +{ + CSInit(); + if (sGetComponentInstanceStorageProc) + return (*sGetComponentInstanceStorageProc)(aComponentInstance); + return NULL; +} + +void CMgr_SetComponentInstanceStorage(ComponentInstance aComponentInstance, Handle theStorage) +{ + CSInit(); + if (sSetComponentInstanceStorageProc) + (*sSetComponentInstanceStorageProc)(aComponentInstance, theStorage); +} +#endif // !CA_USE_AUDIO_PLUGIN_ONLY + +#else +//#include "ComponentManagerDependenciesWin.h" +// everything we need is there and we should be linking against it +static OSStatus CB_GetComponentDescription (const AudioComponentInstance inInstance, AudioComponentDescription * outDesc) +{ + AudioComponent comp = AudioComponentInstanceGetComponent(inInstance); + if (comp) + return AudioComponentGetDescription(comp, outDesc); + + return kAudio_ParamError; +} + +#endif + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/ComponentBase.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/ComponentBase.h new file mode 100644 index 0000000000..67417aa9d1 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUBase/ComponentBase.h @@ -0,0 +1,353 @@ +/* + File: ComponentBase.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __ComponentBase_h__ +#define __ComponentBase_h__ + +#include <new> +#include "CADebugMacros.h" +#include "CAXException.h" + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include <CoreAudio/CoreAudioTypes.h> + #include <AudioUnit/AudioUnit.h> + + #if !CA_USE_AUDIO_PLUGIN_ONLY + #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/Components.h> + + #if (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5) + #define AudioComponentInstance ComponentInstance + #define AudioComponentDescription ComponentDescription + #define AudioComponent Component + #endif + Handle CMgr_GetComponentInstanceStorage(ComponentInstance aComponentInstance); + void CMgr_SetComponentInstanceStorage(ComponentInstance aComponentInstance, Handle theStorage); + #endif + + #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + typedef Float32 AudioUnitParameterValue; + #endif + #if COREAUDIOTYPES_VERSION < 1051 + typedef Float32 AudioUnitSampleType; + #endif + + #if !TARGET_OS_WIN32 + #include <pthread.h> + #endif + + #if TARGET_OS_WIN32 + #include "CAGuard.h" + #endif +#else + #include "CoreAudioTypes.h" + #if !CA_USE_AUDIO_PLUGIN_ONLY + #include "ComponentManagerDependenciesWin.h" + #endif + #include "AudioUnit.h" + #include "CAGuard.h" +#endif + +#ifndef COMPONENT_THROW + #if VERBOSE_COMPONENT_THROW + #define COMPONENT_THROW(throw_err) \ + do { DebugMessage(#throw_err); throw static_cast<OSStatus>(throw_err); } while (0) + #else + #define COMPONENT_THROW(throw_err) \ + throw static_cast<OSStatus>(throw_err) + #endif +#endif + +#define COMPONENT_CATCH \ + catch (const CAXException &ex) { result = ex.mError; } \ + catch (std::bad_alloc &) { result = kAudio_MemFullError; } \ + catch (OSStatus catch_err) { result = catch_err; } \ + catch (OSErr catch_err) { result = catch_err; } \ + catch (...) { result = -1; } + +/*! @class ComponentBase */ +class ComponentBase { +public: + // classic MacErrors + enum { noErr = 0}; + + /*! @ctor ComponentBase */ + ComponentBase(AudioComponentInstance inInstance); + + /*! @dtor ~ComponentBase */ + virtual ~ComponentBase(); + + /*! @method PostConstructor */ + virtual void PostConstructor(); + + /*! @method PreDestructor */ + virtual void PreDestructor(); + +#if !CA_USE_AUDIO_PLUGIN_ONLY + /*! @method Version */ + virtual OSStatus Version(); + + /*! @method ComponentEntryDispatch */ + static OSStatus ComponentEntryDispatch(ComponentParameters *p, ComponentBase *This); + + /*! GetSelectorForCanDo */ + static SInt16 GetSelectorForCanDo(ComponentParameters *params); +#endif + + /*! @method GetComponentInstance */ + AudioComponentInstance GetComponentInstance() const { return mComponentInstance; } + + /*! @method GetComponentDescription */ + AudioComponentDescription GetComponentDescription() const; + + // This global variable is so that new instances know how they were instantiated: via the Component Manager, + // or as AudioComponents. It's ugly, but preferable to altering the constructor of every class in the hierarchy. + // It's safe because construction is protected by ComponentInitLocker. + enum EInstanceType { kComponentMgrInstance, kAudioComponentInstance }; + static EInstanceType sNewInstanceType; + + /*! @method IsPluginObject */ + bool IsPluginObject () const { return mInstanceType == kAudioComponentInstance; } + /*! @method IsCMgrObject */ + bool IsCMgrObject () const { return mInstanceType == kComponentMgrInstance; } + + /*! @method AP_Open */ + static OSStatus AP_Open(void *self, AudioUnit compInstance); + + /*! @method AP_Close */ + static OSStatus AP_Close(void *self); + +protected: + /*! @var mComponentInstance */ + AudioComponentInstance mComponentInstance; + EInstanceType mInstanceType; +}; + +class ComponentInitLocker +{ +#if TARGET_OS_MAC +public: + ComponentInitLocker() + { + pthread_once(&sOnce, InitComponentInitLocker); + pthread_mutex_lock(&sComponentOpenMutex); + mPreviousNewInstanceType = ComponentBase::sNewInstanceType; + } + ~ComponentInitLocker() + { + ComponentBase::sNewInstanceType = mPreviousNewInstanceType; + pthread_mutex_unlock(&sComponentOpenMutex); + } + + // There are situations (11844772) where we need to be able to release the lock early. + class Unlocker { + public: + Unlocker() + { + pthread_mutex_unlock(&sComponentOpenMutex); + } + ~Unlocker() + { + pthread_mutex_lock(&sComponentOpenMutex); + } + }; + +private: + static pthread_mutex_t sComponentOpenMutex; + static pthread_once_t sOnce; + static void InitComponentInitLocker(); + +#elif TARGET_OS_WIN32 +public: + bool sNeedsUnlocking; + ComponentInitLocker() { sNeedsUnlocking = sComponentOpenGuard.Lock(); } + ~ComponentInitLocker() { if(sNeedsUnlocking) { sComponentOpenGuard.Unlock(); } } +private: + static CAGuard sComponentOpenGuard; +#endif + +private: + ComponentBase::EInstanceType mPreviousNewInstanceType; +}; + +/*! @class AudioComponentPlugInInstance */ +struct AudioComponentPlugInInstance { + AudioComponentPlugInInterface mPlugInInterface; + void * (*mConstruct)(void *memory, AudioComponentInstance ci); + void (*mDestruct)(void *memory); + void * mPad[2]; // 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 */ +template <class APMethodLookup, class Implementor> +class APFactory { +public: + static void *Construct(void *memory, AudioComponentInstance compInstance) + { + return new(memory) Implementor(compInstance); + } + + static void Destruct(void *memory) + { + ((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 */) + { + AudioComponentPlugInInstance *acpi = + (AudioComponentPlugInInstance *)malloc( 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 = NULL; + acpi->mConstruct = Construct; + acpi->mDestruct = Destruct; + acpi->mPad[0] = NULL; + acpi->mPad[1] = NULL; + return (AudioComponentPlugInInterface*)acpi; + } + + // 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) + { + AudioComponentDescription desc = { type, subtype, manuf, flags, 0 }; + return AudioComponentRegister(&desc, name, vers, Factory); + } +}; + +#if !CA_USE_AUDIO_PLUGIN_ONLY +/*! @class ComponentEntryPoint + * @discussion This is only used for a component manager version +*/ +template <class Class> +class ComponentEntryPoint { +public: + /*! @method Dispatch */ + static OSStatus Dispatch(ComponentParameters *params, Class *obj) + { + OSStatus result = noErr; + + try { + if (params->what == kComponentOpenSelect) { + // solve a host of initialization thread safety issues. + ComponentInitLocker lock; + + ComponentBase::sNewInstanceType = ComponentBase::kComponentMgrInstance; + ComponentInstance ci = (ComponentInstance)(params->params[0]); + Class *This = new Class((AudioComponentInstance)ci); + This->PostConstructor(); // allows base class to do additional initialization + // once the derived class is fully constructed + + CMgr_SetComponentInstanceStorage(ci, (Handle)This); + } else + result = Class::ComponentEntryDispatch(params, obj); + } + COMPONENT_CATCH + + return result; + } + + /*! @method Register */ + static Component Register(OSType compType, OSType subType, OSType manufacturer) + { + ComponentDescription description = {compType, subType, manufacturer, 0, 0}; + Component component = RegisterComponent(&description, (ComponentRoutineUPP) Dispatch, registerComponentGlobal, NULL, NULL, NULL); + if (component != NULL) { + SetDefaultComponent(component, defaultComponentAnyFlagsAnyManufacturerAnySubType); + } + return component; + } +}; + +// NOTE: Component Mgr is deprecated in ML. +// this macro should not be used with new audio components +// it is only for backwards compatibility with Lion and SL. +// this macro registers both a plugin and a component mgr version. +#define AUDIOCOMPONENT_ENTRY(FactoryType, Class) \ + extern "C" OSStatus Class##Entry(ComponentParameters *params, Class *obj); \ + extern "C" OSStatus Class##Entry(ComponentParameters *params, Class *obj) { \ + return ComponentEntryPoint<Class>::Dispatch(params, obj); \ + } \ + extern "C" void * Class##Factory(const AudioComponentDescription *inDesc); \ + extern "C" void * Class##Factory(const AudioComponentDescription *inDesc) { \ + return FactoryType<Class>::Factory(inDesc); \ + } + // the only component we still support are the carbon based view components + // you should be using this macro now to exclusively register those types +#define VIEW_COMPONENT_ENTRY(Class) \ + extern "C" OSStatus Class##Entry(ComponentParameters *params, Class *obj); \ + extern "C" OSStatus Class##Entry(ComponentParameters *params, Class *obj) { \ + return ComponentEntryPoint<Class>::Dispatch(params, obj); \ + } + + /*! @class ComponentRegistrar */ +template <class Class, OSType Type, OSType Subtype, OSType Manufacturer> +class ComponentRegistrar { +public: + /*! @ctor ComponentRegistrar */ + ComponentRegistrar() { ComponentEntryPoint<Class>::Register(Type, Subtype, Manufacturer); } +}; + +#define COMPONENT_REGISTER(Class,Type,Subtype,Manufacturer) \ + static ComponentRegistrar<Class, Type, Subtype, Manufacturer> gRegistrar##Class +#else +#define COMPONENT_ENTRY(Class) +#define COMPONENT_REGISTER(Class) +// this macro is used to generate the Entry Point for a given Audio Plugin +// you should be using this macro now with audio components +#define AUDIOCOMPONENT_ENTRY(FactoryType, Class) \ + extern "C" void * Class##Factory(const AudioComponentDescription *inDesc); \ + extern "C" void * Class##Factory(const AudioComponentDescription *inDesc) { \ + return FactoryType<Class>::Factory(inDesc); \ + } + +#endif // !CA_USE_AUDIO_PLUGIN_ONLY + + +#endif // __ComponentBase_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewBase.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewBase.cpp new file mode 100644 index 0000000000..332475335d --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewBase.cpp @@ -0,0 +1,403 @@ +/* + File: AUCarbonViewBase.cpp + Abstract: AUCarbonViewBase.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUCarbonViewBase.h" +#include "AUCarbonViewControl.h" +#include <algorithm> + +AUCarbonViewBase::AUCarbonViewBase(AudioUnitCarbonView inInstance, Float32 inNotificationInterval /* in seconds */) : + ComponentBase(inInstance), + mEditAudioUnit(0), + mParameterListener(NULL), +#if !__LP64__ + mEventListener(NULL), +#endif + mTimerRef (NULL), + mTimerUPP (NULL), + mCarbonWindow(NULL), + mCarbonPane(NULL), + mXOffset(0), + mYOffset(0) +{ + AUEventListenerCreate (ParameterListener, this, + CFRunLoopGetCurrent(), kCFRunLoopCommonModes, + inNotificationInterval, inNotificationInterval, + &mParameterListener); +} + +AUCarbonViewBase::~AUCarbonViewBase() +{ +#if !__LP64__ + if (mCarbonPane) + DisposeControl(mCarbonPane); + + for (ControlList::iterator it = mControlList.begin(); it != mControlList.end(); ++it) { + AUCarbonViewControl *ctl = *it; + delete ctl; + } + AUListenerDispose(mParameterListener); + + if (mTimerRef) + ::RemoveEventLoopTimer (mTimerRef); + + if (mTimerUPP) + DisposeEventLoopTimerUPP (mTimerUPP); +#endif +} + +void AUCarbonViewBase::AddControl(AUCarbonViewControl *control) +{ + ControlList::iterator it = find(mControlList.begin(), mControlList.end(), control); + if (it == mControlList.end()) + mControlList.push_back(control); +} + +void AUCarbonViewBase::RemoveControl(AUCarbonViewControl *control) +{ + ControlList::iterator it = find(mControlList.begin(), mControlList.end(), control); + if (it != mControlList.end()) { + AUCarbonViewControl *ctl = *it; + mControlList.erase(it); + delete ctl; + } +} + +void AUCarbonViewBase::ClearControls () +{ + for (ControlList::iterator it = mControlList.begin(); it != mControlList.end(); ++it) { + AUCarbonViewControl *ctl = *it; + delete ctl; + } + mControlList.clear(); +} + +void AUCarbonViewBase::ParameterListener(void * inCallbackRefCon, + void * inObject, + const AudioUnitEvent * inEvent, + UInt64 inEventHostTime, + Float32 inParameterValue) +{ + if (inEvent->mEventType == kAudioUnitEvent_ParameterValueChange) { + AUCarbonViewControl *ctl = (AUCarbonViewControl *)inObject; + ctl->ParameterToControl(inParameterValue); + } +} + + +OSStatus AUCarbonViewBase::CreateCarbonView(AudioUnit inAudioUnit, WindowRef inWindow, ControlRef inParentControl, const Float32Point &inLocation, const Float32Point &inSize, ControlRef &outParentControl) +{ +#if !__LP64__ + mEditAudioUnit = inAudioUnit; + mCarbonWindow = inWindow; + + WindowAttributes attributes; + verify_noerr(GetWindowAttributes(mCarbonWindow, &attributes)); + mCompositWindow = (attributes & kWindowCompositingAttribute) != 0; + + Rect area; + area.left = short(inLocation.x); area.top = short(inLocation.y); + area.right = short(area.left + inSize.x); area.bottom = short(area.top + inSize.y); + OSStatus err = ::CreateUserPaneControl(inWindow, &area, + kControlSupportsEmbedding, + &mCarbonPane); // subclass can resize mCarbonPane to taste + verify_noerr(err); + if (err) return err; + outParentControl = mCarbonPane; + + // register for mouse-down in our pane -- we want to clear focus + EventTypeSpec paneEvents[] = { + { kEventClassControl, kEventControlClick } + }; + WantEventTypes(GetControlEventTarget(mCarbonPane), GetEventTypeCount(paneEvents), paneEvents); + + if (IsCompositWindow()) { + verify_noerr(::HIViewAddSubview(inParentControl, mCarbonPane)); + mXOffset = 0; + mYOffset = 0; + } + else { + verify_noerr(::EmbedControl(mCarbonPane, inParentControl)); + mXOffset = inLocation.x; + mYOffset = inLocation.y; + } + mBottomRight.h = mBottomRight.v = 0; + + SizeControl(mCarbonPane, 0, 0); + if (err = CreateUI(mXOffset, mYOffset)) + return err; + + // we should only resize the control if a subclass has embedded + // controls in this AND this is done with the EmbedControl call below + // if mBottomRight is STILL equal to zero, then that wasn't done + // so don't size the control + Rect paneBounds; + GetControlBounds(mCarbonPane, &paneBounds); + // only resize mCarbonPane if it has not already been resized during CreateUI + if ((paneBounds.top == paneBounds.bottom) && (paneBounds.left == paneBounds.right)) { + if (mBottomRight.h != 0 && mBottomRight.v != 0) + SizeControl(mCarbonPane, (short) (mBottomRight.h - mXOffset), (short) (mBottomRight.v - mYOffset)); + } + + if (IsCompositWindow()) { + // prepare for handling scroll-events + EventTypeSpec scrollEvents[] = { + { kEventClassScrollable, kEventScrollableGetInfo }, + { kEventClassScrollable, kEventScrollableScrollTo } + }; + + WantEventTypes(GetControlEventTarget(mCarbonPane), GetEventTypeCount(scrollEvents), scrollEvents); + + mCurrentScrollPoint.x = mCurrentScrollPoint.y = 0.0f; + } + + return err; +#else + return noErr; +#endif +} + +OSStatus AUCarbonViewBase::CreateUI(Float32 inXOffset, Float32 inYOffset) +{ + return noErr; +} + +OSStatus AUCarbonViewBase::EmbedControl(ControlRef ctl) +{ +#if !__LP64__ + Rect r; + ::GetControlBounds(ctl, &r); + if (r.right > mBottomRight.h) mBottomRight.h = r.right; + if (r.bottom > mBottomRight.v) mBottomRight.v = r.bottom; + + if (IsCompositWindow()) + return ::HIViewAddSubview(mCarbonPane, ctl); + else + return ::EmbedControl(ctl, mCarbonPane); +#else + return noErr; +#endif +} + +void AUCarbonViewBase::AddCarbonControl(AUCarbonViewControl::ControlType type, const CAAUParameter ¶m, ControlRef control) +{ + verify_noerr(EmbedControl(control)); + + AUCarbonViewControl *auvc = new AUCarbonViewControl(this, mParameterListener, type, param, control); + auvc->Bind(); + AddControl(auvc); +} + +bool AUCarbonViewBase::HandleEvent(EventHandlerCallRef inHandlerRef, EventRef event) +{ +#if !__LP64__ + UInt32 eclass = GetEventClass(event); + UInt32 ekind = GetEventKind(event); + ControlRef control; + + switch (eclass) { + case kEventClassControl: + { + switch (ekind) { + case kEventControlClick: + GetEventParameter(event, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &control); + if (control == mCarbonPane) { + ClearKeyboardFocus(mCarbonWindow); + return true; + } + } + } + break; + + case kEventClassScrollable: + { + switch (ekind) { + case kEventScrollableGetInfo: + { + // [1/4] + /* <-- kEventParamImageSize (out, typeHISize) + * On exit, contains the size of the entire scrollable view. + */ + HISize originalSize = { mBottomRight.h, mBottomRight.v }; + verify_noerr(SetEventParameter(event, kEventParamImageSize, typeHISize, sizeof(HISize), &originalSize)); + + // [2/4] + /* <-- kEventParamViewSize (out, typeHISize) + * On exit, contains the amount of the scrollable view that is + * visible. + */ + HIViewRef parentView = HIViewGetSuperview(mCarbonPane); + HIRect parentBounds; + verify_noerr(HIViewGetBounds(parentView, &parentBounds)); + //HISize windowSize = { float(windowBounds.right - windowBounds.left), + // float(windowBounds.bottom - windowBounds.top) }; + verify_noerr(SetEventParameter(event, kEventParamViewSize, typeHISize, sizeof(HISize), &(parentBounds.size))); + + // [3/4] + /* <-- kEventParamLineSize (out, typeHISize) + * On exit, contains the amount that should be scrolled in + * response to a single click on a scrollbar arrow. + */ + HISize scrollIncrementSize = { 16.0f, float(20) }; + verify_noerr(SetEventParameter(event, kEventParamLineSize, typeHISize, sizeof(HISize), &scrollIncrementSize)); + + // [4/4] + /* <-- kEventParamOrigin (out, typeHIPoint) + * On exit, contains the scrollable viewÕs current origin (the + * view-relative coordinate that is drawn at the top left + * corner of its frame). These coordinates should always be + * greater than or equal to zero. They should be less than or + * equal to the viewÕs image size minus its view size. + */ + verify_noerr(SetEventParameter(event, kEventParamOrigin, typeHIPoint, sizeof(HIPoint), &mCurrentScrollPoint)); + } + return true; + + case kEventScrollableScrollTo: + { + /* + * kEventClassScrollable / kEventScrollableScrollTo + * + * Summary: + * Requests that an HIScrollViewÕs scrollable view should scroll to + * a particular origin. + */ + + /* --> kEventParamOrigin (in, typeHIPoint) + * The new origin for the scrollable view. The origin + * coordinates will vary from (0,0) to scrollable viewÕs image + * size minus its view size. + */ + HIPoint pointToScrollTo; + verify_noerr(GetEventParameter(event, kEventParamOrigin, typeHIPoint, NULL, sizeof(HIPoint), NULL, &pointToScrollTo)); + + float xDelta = mCurrentScrollPoint.x - pointToScrollTo.x; + float yDelta = mCurrentScrollPoint.y - pointToScrollTo.y; + // move visible portion the appropriate amount + verify_noerr(HIViewScrollRect(mCarbonPane, NULL, xDelta, yDelta)); + // set new content to be drawn + verify_noerr(HIViewSetBoundsOrigin(mCarbonPane, pointToScrollTo.x, pointToScrollTo.y)); + + mCurrentScrollPoint = pointToScrollTo; + } + return true; + + default: + break; + } + } + break; + + default: + break; + } +#endif + return false; +} + +/*! @method TellListener */ +void AUCarbonViewBase::TellListener (const CAAUParameter &auvp, AudioUnitCarbonViewEventID event, void *evpar) +{ +#if !__LP64__ + if (mEventListener) + (*mEventListener)(mEventListenerUserData, mComponentInstance, &auvp, event, evpar); +#endif + + AudioUnitEvent auEvent; + auEvent.mArgument.mParameter = auvp; + if (event == kAudioUnitCarbonViewEvent_MouseDownInControl) { + auEvent.mEventType = kAudioUnitEvent_BeginParameterChangeGesture; + } else { + auEvent.mEventType = kAudioUnitEvent_EndParameterChangeGesture; + } + AUEventListenerNotify(mParameterListener, this, &auEvent); +} + + +void AUCarbonViewBase::Update (bool inUIThread) +{ + for (ControlList::iterator iter = mControlList.begin(); iter != mControlList.end(); ++iter) + { + (*iter)->Update(inUIThread); + } +} + +pascal void AUCarbonViewBase::TheTimerProc (EventLoopTimerRef inTimer, void *inUserData) +{ + AUCarbonViewBase* This = reinterpret_cast<AUCarbonViewBase*>(inUserData); + This->RespondToEventTimer (inTimer); +} + +void AUCarbonViewBase::RespondToEventTimer (EventLoopTimerRef inTimer) +{} + +/* + THESE are reasonable values for these two times + 0.005 // delay + 0.050 // interval +*/ + +OSStatus AUCarbonViewBase::CreateEventLoopTimer (Float32 inDelay, Float32 inInterval) +{ + if (mTimerUPP) + return noErr; + + mTimerUPP = NewEventLoopTimerUPP(TheTimerProc); + + EventLoopRef mainEventLoop = GetMainEventLoop(); + + //doesn't seem to like too small a value + if (inDelay < 0.005) + inDelay = 0.005; + + OSStatus timerResult = ::InstallEventLoopTimer( + mainEventLoop, + inDelay, + inInterval, + mTimerUPP, + this, + &mTimerRef); + return timerResult; +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewBase.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewBase.h new file mode 100644 index 0000000000..910e53ffa6 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewBase.h @@ -0,0 +1,188 @@ +/* + File: AUCarbonViewBase.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUCarbonViewBase_h__ +#define __AUCarbonViewBase_h__ + +#include <vector> +#include "AUCarbonViewControl.h" +#include "ComponentBase.h" + +static const Float32 kDefaultNotificationInterval = 0.100; + + /*! @class AUCarbonViewBase */ +class AUCarbonViewBase : public ComponentBase, public CarbonEventHandler +{ +public: + /*! @ctor AUCarbonViewBase */ + AUCarbonViewBase ( AudioUnitCarbonView inInstance, + Float32 inNotificationInterval = kDefaultNotificationInterval /* in seconds */); + /*! @dtor ~AUCarbonViewBase */ + virtual ~AUCarbonViewBase(); + + // AUViewBase overrides + /*! @method CreateCarbonView */ + virtual OSStatus CreateCarbonView (AudioUnit inAudioUnit, WindowRef inWindow, ControlRef inParentControl, const Float32Point &inLocation, const Float32Point &inSize, ControlRef &outParentControl); + + // our own virtual methods + /*! @method CreateUI */ + virtual OSStatus CreateUI (Float32 inXOffset, Float32 inYOffset); + + /*! @method HandleEvent */ + virtual bool HandleEvent (EventHandlerCallRef inHandlerRef, EventRef event); + + /*! @method GetEditAudioUnit */ + const AudioUnit GetEditAudioUnit () const { return mEditAudioUnit; } + // + /*! @method ComponentEntryDispatch */ + static OSStatus ComponentEntryDispatch ( + ComponentParameters * params, + AUCarbonViewBase * This); + + /*! @method AddCarbonControl */ + void AddCarbonControl ( + AUCarbonViewControl::ControlType type, + const CAAUParameter & param, + ControlRef control); + + /*! @method GetCarbonWindow */ + WindowRef GetCarbonWindow () { return mCarbonWindow; } + /*! @method GetCarbonPane */ + ControlRef GetCarbonPane () { return mCarbonPane; } + /*! @method EmbedControl */ + OSStatus EmbedControl (ControlRef ctl); + + /*! @method TellListener */ + void TellListener (const CAAUParameter &auvp, AudioUnitCarbonViewEventID event, void *evpar); + + // pass in true if wanting an update to the view and you're calling this from a thread + // that is safe to do UI in. + // If you don't know, pass in false! + /*! @method Update */ + void Update (bool inUIThread); + + /*! @method GetXOffset */ + Float32 GetXOffset () { return mXOffset; } + /*! @method GetYOffset */ + Float32 GetYOffset () { return mYOffset; } + + /*! @method ClearControls */ + void ClearControls (); + + /*! @method IsCompositWindow */ + bool IsCompositWindow () const { return mCompositWindow; } + +protected: +#if !__LP64__ + /*! @method SetEventListener */ + void SetEventListener (AudioUnitCarbonViewEventListener listener, void *userData) + { + mEventListener = listener; + mEventListenerUserData = userData; + } +#endif + + /*! @method AddControl */ + void AddControl (AUCarbonViewControl *control); + /*! @method RemoveControl */ + void RemoveControl (AUCarbonViewControl *control); + + OSStatus CreateEventLoopTimer (Float32 inDelay, Float32 inInterval); + + /*! @method ParameterListener */ + static void ParameterListener (void * inCallbackRefCon, + void * inObject, + const AudioUnitEvent * inEvent, + UInt64 inEventHostTime, + Float32 inParameterValue); + + static pascal void TheTimerProc ( EventLoopTimerRef inTimer, + void * inUserData); + + virtual void RespondToEventTimer (EventLoopTimerRef inTimer); + + /*! @var mEditAudioUnit */ + AudioUnit mEditAudioUnit; // the AU we're controlling + /*! @var mParameterListener */ + AUEventListenerRef mParameterListener; + +#if !__LP64__ + /*! @var mEventListener */ + AudioUnitCarbonViewEventListener + mEventListener; +#endif + + /*! @var mEventListenerUserData */ + void * mEventListenerUserData; + +private: + typedef std::vector<AUCarbonViewControl *> ControlList; + /*! @var mControlList */ + ControlList mControlList; + + EventLoopTimerRef mTimerRef; + + EventLoopTimerUPP mTimerUPP; + +protected: + /*! @var mCarbonWindow */ + WindowRef mCarbonWindow; + /*! @var mCarbonPane */ + ControlRef mCarbonPane; // user pane, contains all other controls + /*! @var mBottomRight */ + Point mBottomRight; // largest width and height of child controls + /*! @var mXOffset */ + Float32 mXOffset; + /*! @var mYOffset */ + Float32 mYOffset; + /*! @var mCompositWindow */ + bool mCompositWindow; + /*! @var mCurrentScrollPoint */ + HIPoint mCurrentScrollPoint; // needed for scrolling +}; + + +#endif // __AUCarbonViewBase_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewControl.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewControl.cpp new file mode 100644 index 0000000000..981347b0f1 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewControl.cpp @@ -0,0 +1,710 @@ +/* + File: AUCarbonViewControl.cpp + Abstract: AUCarbonViewControl.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUCarbonViewControl.h" +#include "AUCarbonViewBase.h" +#include "AUViewLocalizedStringKeys.h" + +AUCarbonViewControl::AUCarbonViewControl(AUCarbonViewBase *ownerView, AUParameterListenerRef listener, ControlType type, const CAAUParameter ¶m, ControlRef control) : + mOwnerView(ownerView), + mListener(listener), + mType(type), + mParam(param), + mControl(control), + mInControlInitialization(0) +{ +#if !__LP64__ + SetControlReference(control, SRefCon(this)); +#endif +} + +AUCarbonViewControl::~AUCarbonViewControl() +{ + AUListenerRemoveParameter(mListener, this, &mParam); +} + +AUCarbonViewControl* AUCarbonViewControl::mLastControl = NULL; + +void AUCarbonViewControl::Bind() +{ +#if !__LP64__ + mInControlInitialization = 1; // true + AUListenerAddParameter(mListener, this, &mParam); + // will cause an almost-immediate callback + + EventTypeSpec events[] = { + { kEventClassControl, kEventControlValueFieldChanged } // N.B. OS X only + }; + + WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(events), events); + + if (mType == kTypeContinuous || mType == kTypeText || mType == kTypeDiscrete) { + EventTypeSpec events[] = { + { kEventClassControl, kEventControlHit }, + { kEventClassControl, kEventControlClick }, + { kEventClassControl, kEventControlTrack } + }; + WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(events), events); + } + + if (mType == kTypeText) { + EventTypeSpec events[] = { + { kEventClassControl, kEventControlSetFocusPart } + }; + WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(events), events); + ControlKeyFilterUPP proc = mParam.ValuesHaveStrings() ? StdKeyFilterCallback : NumericKeyFilterCallback; + // this will fail for a static text field + SetControlData(mControl, 0, kControlEditTextKeyFilterTag, sizeof(proc), &proc); + } + + Update(true); + mInControlInitialization = 0; // false +#endif +} + +void AUCarbonViewControl::ParameterToControl(Float32 paramValue) +{ +#if !__LP64__ + ++mInControlInitialization; + switch (mType) { + case kTypeContinuous: + SetValueFract(AUParameterValueToLinear(paramValue, &mParam)); + break; + case kTypeDiscrete: + { + long value = long(paramValue); + + // special case [1] -- menu parameters + if (mParam.HasNamedParams()) { + // if we're dealing with menus they behave differently! + // becaue setting min and max doesn't work correctly for the control value + // first menu item always reports a control value of 1 + ControlKind ctrlKind; + if (GetControlKind(mControl, &ctrlKind) == noErr) { + if ((ctrlKind.kind == kControlKindPopupArrow) + || (ctrlKind.kind == kControlKindPopupButton)) + { + value = value - long(mParam.ParamInfo().minValue) + 1; + } + } + } + + // special case [2] -- Write-only boolean parameters + AudioUnitParameterInfo AUPI = mParam.ParamInfo(); + + bool isWriteOnlyBoolParameter = ( (AUPI.unit == kAudioUnitParameterUnit_Boolean) && + (AUPI.flags & kAudioUnitParameterFlag_IsWritable) && + !(AUPI.flags & kAudioUnitParameterFlag_IsReadable) ); + if (!isWriteOnlyBoolParameter) { + SetValue (value); + } + } + break; + case kTypeText: + { + CFStringRef cfstr = mParam.GetStringFromValueCopy(¶mValue); + + if ( !(mParam.ParamInfo().flags & kAudioUnitParameterFlag_IsWritable) //READ ONLY PARAMS + && (mParam.ParamInfo().flags & kAudioUnitParameterFlag_IsReadable)) + { + if (mParam.GetParamTag()) { + CFMutableStringRef str = CFStringCreateMutableCopy(NULL, 256, cfstr); + CFRelease (cfstr); + CFStringAppend (str, CFSTR(" ")); + CFStringAppend (str, mParam.GetParamTag()); + cfstr = str; + } + } + SetTextValue(cfstr); + CFRelease (cfstr); + } + break; + } + --mInControlInitialization; +#endif +} + +void AUCarbonViewControl::ControlToParameter() +{ +#if !__LP64__ + if (mInControlInitialization) + return; + + switch (mType) { + case kTypeContinuous: + { + double controlValue = GetValueFract(); + Float32 paramValue = AUParameterValueFromLinear(controlValue, &mParam); + mParam.SetValue(mListener, this, paramValue); + } + break; + case kTypeDiscrete: + { + long value = GetValue(); + + // special case [1] -- Menus + if (mParam.HasNamedParams()) { + // if we're dealing with menus they behave differently! + // becaue setting min and max doesn't work correctly for the control value + // first menu item always reports a control value of 1 + ControlKind ctrlKind; + if (GetControlKind(mControl, &ctrlKind) == noErr) { + if ((ctrlKind.kind == kControlKindPopupArrow) + || (ctrlKind.kind == kControlKindPopupButton)) + { + value = value + long(mParam.ParamInfo().minValue) - 1; + } + } + } + + // special case [2] -- Write-only boolean parameters + AudioUnitParameterInfo AUPI = mParam.ParamInfo(); + + bool isWriteOnlyBoolParameter = ( (AUPI.unit == kAudioUnitParameterUnit_Boolean) && + (AUPI.flags & kAudioUnitParameterFlag_IsWritable) && + !(AUPI.flags & kAudioUnitParameterFlag_IsReadable) ); + if (isWriteOnlyBoolParameter) { + value = 1; + } + + mParam.SetValue (mListener, this, value); + } + break; + case kTypeText: + { + Float32 val = mParam.GetValueFromString (GetTextValue()); + mParam.SetValue(mListener, this, (mParam.IsIndexedParam() ? (int)val : val)); + if (mParam.ValuesHaveStrings()) + ParameterToControl(val); //make sure we display the correct text (from the AU) + } + break; + } +#endif +} + +void AUCarbonViewControl::SetValueFract(double value) +{ +#if !__LP64__ + SInt32 minimum = GetControl32BitMinimum(mControl); + SInt32 maximum = GetControl32BitMaximum(mControl); + SInt32 cval = SInt32(value * (maximum - minimum) + minimum + 0.5); + SetControl32BitValue(mControl, cval); +// printf("set: value=%lf, min=%ld, max=%ld, ctl value=%ld\n", value, minimum, maximum, cval); +#endif +} + +double AUCarbonViewControl::GetValueFract() +{ +#if !__LP64__ + SInt32 minimum = GetControl32BitMinimum(mControl); + SInt32 maximum = GetControl32BitMaximum(mControl); + SInt32 cval = GetControl32BitValue(mControl); + double result = double(cval - minimum) / double(maximum - minimum); +// printf("get: min=%ld, max=%ld, value=%ld, result=%f\n", minimum, maximum, cval, result); + return result; +#else + return 0; +#endif +} + +void AUCarbonViewControl::SetTextValue(CFStringRef cfstr) +{ +#if !__LP64__ + verify_noerr(SetControlData(mControl, 0, kControlEditTextCFStringTag, sizeof(CFStringRef), &cfstr)); +#endif +} + +CFStringRef AUCarbonViewControl::GetTextValue() +{ +#if !__LP64__ + CFStringRef cfstr; + verify_noerr(GetControlData(mControl, 0, kControlEditTextCFStringTag, sizeof(CFStringRef), &cfstr, NULL)); + return cfstr; +#else + return CFSTR(""); +#endif +} + +void AUCarbonViewControl::SetValue(long value) +{ +#if !__LP64__ + SetControl32BitValue(mControl, value); +#endif +} + +long AUCarbonViewControl::GetValue() +{ +#if !__LP64__ + return GetControl32BitValue(mControl); +#else + return 0; +#endif +} + +/* Notes on event handling + + Button (Click and release on button) + kEventControlClick received + kEventControlTrack received + kEventControlValueFieldChanged received + kEventControlHit received + + Button (Click and release outside of button bounds) + kEventControlClick received + kEventControlTrack received + + Slider (Click, drag, and release) + kEventControlClick received + kEventControlTrack received + kEventControlValueFieldChanged received + kEventControlValueFieldChanged received + kEventControlHit received + + Slider (Click, release without changing value) + kEventControlClick received + kEventControlTrack received +*/ +bool AUCarbonViewControl::HandleEvent(EventHandlerCallRef inHandlerRef, EventRef event) +{ + UInt32 eclass = GetEventClass(event); + UInt32 ekind = GetEventKind(event); + ControlRef control; + bool handled = true; + + switch (eclass) { + case kEventClassControl: + { + AudioUnitParameterInfo AUPI = mParam.ParamInfo(); + + bool isWriteOnlyBoolParameter = ( (AUPI.unit == kAudioUnitParameterUnit_Boolean) && + (AUPI.flags & kAudioUnitParameterFlag_IsWritable) && + !(AUPI.flags & kAudioUnitParameterFlag_IsReadable) ); + + switch (ekind) { + case kEventControlSetFocusPart: // tab + handled = !handled; // fall through to next case + mLastControl = this; + case kEventControlValueFieldChanged: + GetEventParameter(event, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &control); + verify(control == mControl); + ControlToParameter(); + return handled; + case kEventControlClick: + if (isWriteOnlyBoolParameter) { + GetEventParameter(event, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &control); + verify(control == mControl); + ControlToParameter(); + } else if (mLastControl != this) { + if (mLastControl != NULL) { + mLastControl->Update(false); + } + mLastControl = this; + } + mOwnerView->TellListener(mParam, kAudioUnitCarbonViewEvent_MouseDownInControl, NULL); + break; // don't return true, continue normal processing + case kEventControlHit: + if (mLastControl != this) { + if (mLastControl != NULL) + mLastControl->Update(false); + mLastControl = this; + } + mOwnerView->TellListener(mParam, kAudioUnitCarbonViewEvent_MouseUpInControl, NULL); + break; // don't return true, continue normal processing + case kEventControlTrack: + if (mLastControl != this) { + if (mLastControl != NULL) + mLastControl->Update(false); + mLastControl = this; + } + + CallNextEventHandler(inHandlerRef, event); + ControlToParameter(); // new code + mOwnerView->TellListener(mParam, kAudioUnitCarbonViewEvent_MouseUpInControl, NULL); + // old code: + // break; // don't return true, continue normal processing + + return handled; // don't return true, continue normal processing + } + } + } + return !handled; +} + +pascal void AUCarbonViewControl::SliderTrackProc(ControlRef theControl, ControlPartCode partCode) +{ + // this doesn't need to actually do anything +// AUCarbonViewControl *This = (AUCarbonViewControl *)GetControlReference(theControl); +} + +pascal ControlKeyFilterResult AUCarbonViewControl::StdKeyFilterCallback(ControlRef theControl, + SInt16 *keyCode, SInt16 *charCode, + EventModifiers *modifiers) +{ +#if !__LP64__ + SInt16 c = *charCode; + if (c >= ' ' || c == '\b' || c == 0x7F || (c >= 0x1c && c <= 0x1f) || c == '\t') + return kControlKeyFilterPassKey; + if (c == '\r' || c == 3) { // return or Enter + AUCarbonViewControl *This = (AUCarbonViewControl *)GetControlReference(theControl); + ControlEditTextSelectionRec sel = { 0, 32767 }; + SetControlData(This->mControl, 0, kControlEditTextSelectionTag, sizeof(sel), &sel); + This->ControlToParameter(); + } +#endif + return kControlKeyFilterBlockKey; +} + +pascal ControlKeyFilterResult AUCarbonViewControl::NumericKeyFilterCallback(ControlRef theControl, + SInt16 *keyCode, SInt16 *charCode, + EventModifiers *modifiers) +{ +#if !__LP64__ + SInt16 c = *charCode; + if (isdigit(c) || c == '+' || c == '-' || c == '.' || c == '\b' || c == 0x7F || (c >= 0x1c && c <= 0x1f) + || c == '\t') + return kControlKeyFilterPassKey; + if (c == '\r' || c == 3) { // return or Enter + AUCarbonViewControl *This = (AUCarbonViewControl *)GetControlReference(theControl); + ControlEditTextSelectionRec sel = { 0, 32767 }; + SetControlData(This->mControl, 0, kControlEditTextSelectionTag, sizeof(sel), &sel); + This->ControlToParameter(); + } +#endif + return kControlKeyFilterBlockKey; +} + +Boolean AUCarbonViewControl::SizeControlToFit(ControlRef inControl, SInt16 *outWidth, SInt16 *outHeight) +{ +#if !__LP64__ + if (inControl == 0) return false; + + Boolean bValue = false; + // this only works on text controls -- returns an error for other controls, but doesn't do anything, + // so the error is irrelevant + SetControlData(inControl, kControlEntireControl, 'stim' /* kControlStaticTextIsMultilineTag */, sizeof(Boolean), &bValue); + + SInt16 baseLineOffset; + Rect bestRect; + OSErr err = GetBestControlRect(inControl, &bestRect, &baseLineOffset); + if (err != noErr) return false; + + int width = (bestRect.right - bestRect.left) + 1; + int height = (bestRect.bottom - bestRect.top) + 1; + + Rect boundsRect; + GetControlBounds (inControl, &boundsRect); + + Rect newRect; + newRect.top = boundsRect.top; + newRect.bottom = newRect.top + height; + newRect.left = boundsRect.left; + newRect.right = newRect.left + width; + + SetControlBounds (inControl, &newRect); + + if (outWidth) + *outWidth = width; + + if (outHeight) + *outHeight = height; +#endif + return true; +} + +#pragma mark ___AUPropertyControl +bool AUPropertyControl::HandleEvent(EventHandlerCallRef inHandlerRef, EventRef event) +{ + UInt32 eclass = GetEventClass(event); + UInt32 ekind = GetEventKind(event); + switch (eclass) { + case kEventClassControl: + switch (ekind) { + case kEventControlValueFieldChanged: + HandleControlChange(); + return true; // handled + } + } + + return false; +} + +void AUPropertyControl::RegisterEvents () +{ +#if !__LP64__ + EventTypeSpec events[] = { + { kEventClassControl, kEventControlValueFieldChanged } // N.B. OS X only + }; + + WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(events), events); +#endif +} + +void AUPropertyControl::EmbedControl (ControlRef theControl) +{ + mView->EmbedControl (theControl); +} + +WindowRef AUPropertyControl::GetCarbonWindow() +{ + return mView->GetCarbonWindow(); +} + +#pragma mark ___AUVPreset +#if !__LP64__ +static CFStringRef kStringFactoryPreset = kAUViewLocalizedStringKey_FactoryPreset; +static bool sAUVPresetLocalized = false; +#endif + +AUVPresets::AUVPresets (AUCarbonViewBase* inParentView, + CFArrayRef& inPresets, + Point inLocation, + int nameWidth, + int controlWidth, + ControlFontStyleRec & inFontStyle) + : AUPropertyControl (inParentView), + mPresets (inPresets), + mView (inParentView) +{ +#if !__LP64__ + Rect r; + + // ok we now have an array of factory presets + // get their strings and display them + + r.top = inLocation.v; r.bottom = r.top; + r.left = inLocation.h; r.right = r.left; + + // localize as necessary + if (!sAUVPresetLocalized) { + CFBundleRef mainBundle = CFBundleGetBundleWithIdentifier(kLocalizedStringBundle_AUView); + if (mainBundle) { + kStringFactoryPreset = CFCopyLocalizedStringFromTableInBundle( + kAUViewLocalizedStringKey_FactoryPreset, kLocalizedStringTable_AUView, + mainBundle, CFSTR("FactoryPreset title string")); + sAUVPresetLocalized = true; + } + } + + // create localized title string + CFMutableStringRef factoryPresetsTitle = CFStringCreateMutable(NULL, 0); + CFStringAppend(factoryPresetsTitle, kStringFactoryPreset); + CFStringAppend(factoryPresetsTitle, kAUViewUnlocalizedString_TitleSeparator); + + ControlRef theControl; + verify_noerr(CreateStaticTextControl(mView->GetCarbonWindow(), &r, factoryPresetsTitle, &inFontStyle, &theControl)); + SInt16 width = 0; + AUCarbonViewControl::SizeControlToFit(theControl, &width, &mHeight); + CFRelease(factoryPresetsTitle); + EmbedControl(theControl); + + r.top -= 2; + r.left += width + 10; + r.right = r.left; + r.bottom = r.top; + + verify_noerr(CreatePopupButtonControl ( mView->GetCarbonWindow(), &r, NULL, + -12345, // DON'T GET MENU FROM RESOURCE mMenuID,!!! + FALSE, // variableWidth, + 0, // titleWidth, + 0, // titleJustification, + 0, // titleStyle, + &mControl)); + + MenuRef menuRef; + verify_noerr(CreateNewMenu(1, 0, &menuRef)); + + int numPresets = CFArrayGetCount(mPresets); + + for (int i = 0; i < numPresets; ++i) + { + AUPreset* preset = (AUPreset*) CFArrayGetValueAtIndex (mPresets, i); + verify_noerr(AppendMenuItemTextWithCFString (menuRef, preset->presetName, 0, 0, 0)); + } + + verify_noerr(SetControlData(mControl, 0, kControlPopupButtonMenuRefTag, sizeof(menuRef), &menuRef)); + verify_noerr (SetControlFontStyle (mControl, &inFontStyle)); + + SetControl32BitMaximum (mControl, numPresets); + + // size popup + SInt16 height = 0; + + AUCarbonViewControl::SizeControlToFit(mControl, &width, &height); + + if (height > mHeight) mHeight = height; + if (mHeight < 0) mHeight = 0; + + // find which menu item is the Default preset + UInt32 propertySize = sizeof(AUPreset); + AUPreset defaultPreset; + OSStatus result = AudioUnitGetProperty (mView->GetEditAudioUnit(), + kAudioUnitProperty_PresentPreset, + kAudioUnitScope_Global, + 0, + &defaultPreset, + &propertySize); + + mPropertyID = kAudioUnitProperty_PresentPreset; +#endif +#ifndef __LP64__ + if (result != noErr) { // if the PresentPreset property is not implemented, fall back to the CurrentPreset property + OSStatus result = AudioUnitGetProperty (mView->GetEditAudioUnit(), + kAudioUnitProperty_CurrentPreset, + kAudioUnitScope_Global, + 0, + &defaultPreset, + &propertySize); + mPropertyID = kAudioUnitProperty_CurrentPreset; + if (result == noErr) + CFRetain (defaultPreset.presetName); + } +#endif +#if !__LP64__ + EmbedControl (mControl); + + HandlePropertyChange(defaultPreset); + + RegisterEvents(); +#endif +} + +void AUVPresets::AddInterest (AUEventListenerRef inListener, + void * inObject) +{ + AudioUnitEvent e; + e.mEventType = kAudioUnitEvent_PropertyChange; + e.mArgument.mProperty.mAudioUnit = mView->GetEditAudioUnit(); + e.mArgument.mProperty.mPropertyID = mPropertyID; + e.mArgument.mProperty.mScope = kAudioUnitScope_Global; + e.mArgument.mProperty.mElement = 0; + + AUEventListenerAddEventType(inListener, inObject, &e); +} + +void AUVPresets::RemoveInterest (AUEventListenerRef inListener, + void * inObject) +{ + AudioUnitEvent e; + e.mEventType = kAudioUnitEvent_PropertyChange; + e.mArgument.mProperty.mAudioUnit = mView->GetEditAudioUnit(); + e.mArgument.mProperty.mPropertyID = mPropertyID; + e.mArgument.mProperty.mScope = kAudioUnitScope_Global; + e.mArgument.mProperty.mElement = 0; + + AUEventListenerRemoveEventType(inListener, inObject, &e); +} + +void AUVPresets::HandleControlChange () +{ +#if !__LP64__ + SInt32 i = GetControl32BitValue(mControl); + if (i > 0) + { + AUPreset* preset = (AUPreset*) CFArrayGetValueAtIndex (mPresets, i-1); + + verify_noerr(AudioUnitSetProperty (mView->GetEditAudioUnit(), + mPropertyID, // either currentPreset or PresentPreset depending on which is supported + kAudioUnitScope_Global, + 0, + preset, + sizeof(AUPreset))); + + // when we change a preset we can't expect the AU to update its state + // as it isn't meant to know that its being viewed! + // so we broadcast a notification to all listeners that all parameters on this AU have changed + AudioUnitParameter changedUnit; + changedUnit.mAudioUnit = mView->GetEditAudioUnit(); + changedUnit.mParameterID = kAUParameterListener_AnyParameter; + verify_noerr (AUParameterListenerNotify (NULL, NULL, &changedUnit) ); + } +#endif +} + +void AUVPresets::HandlePropertyChange(AUPreset &preset) +{ +#if !__LP64__ + // check to see if the preset is in our menu + int numPresets = CFArrayGetCount(mPresets); + if (preset.presetNumber < 0) { + SetControl32BitValue (mControl, 0); //controls are one-based + } else { + for (SInt32 i = 0; i < numPresets; ++i) { + AUPreset* currPreset = (AUPreset*) CFArrayGetValueAtIndex (mPresets, i); + if (preset.presetNumber == currPreset->presetNumber) { + SetControl32BitValue (mControl, ++i); //controls are one-based + break; + } + } + } + + if (preset.presetName) + CFRelease (preset.presetName); +#endif +} + +bool AUVPresets::HandlePropertyChange (const AudioUnitProperty &inProp) +{ + if (inProp.mPropertyID == mPropertyID) + { + UInt32 theSize = sizeof(AUPreset); + AUPreset currentPreset; + + OSStatus result = AudioUnitGetProperty(inProp.mAudioUnit, + inProp.mPropertyID, + inProp.mScope, + inProp.mElement, ¤tPreset, &theSize); + + if (result == noErr) { +#ifndef __LP64__ + if (inProp.mPropertyID == kAudioUnitProperty_CurrentPreset && currentPreset.presetName) + CFRetain (currentPreset.presetName); +#endif + HandlePropertyChange(currentPreset); + return true; + } + } + return false; +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewControl.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewControl.h new file mode 100644 index 0000000000..3d5f975614 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewControl.h @@ -0,0 +1,230 @@ +/* + File: AUCarbonViewControl.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUCarbonViewControl_h__ +#define __AUCarbonViewControl_h__ + +#include <Carbon/Carbon.h> +#include <AudioUnit/AudioUnitCarbonView.h> +#include <AudioToolbox/AudioUnitUtilities.h> +#include "CarbonEventHandler.h" +#include "CAAUParameter.h" + +class AUCarbonViewBase; + +// ____________________________________________________________________________ +// AUCarbonViewControl +// Wrapper for a control that is wired to an AudioUnit parameter. + /*! @class AUCarbonViewControl */ +class AUCarbonViewControl : public CarbonEventHandler { + // note that the controls are never disposed; that's managed by the AUCarbonViewBase's + // parent pane which contains all of them ... if we later need to be able to delete + // individual controls on the fly, extra work needed +public: + enum ControlType { + kTypeContinuous, // e.g. slider + kTypeDiscrete, // e.g. pop-up menu + kTypeText + }; + + AUCarbonViewControl(AUCarbonViewBase *ownerView, AUParameterListenerRef listener, ControlType type, const CAAUParameter ¶m, ControlRef control); + ~AUCarbonViewControl(); + + /*! @method Bind */ + virtual void Bind(); // second-stage construction + + /*! @method ControlToParameter */ + virtual void ControlToParameter(); + /*! @method ParameterToControl */ + virtual void ParameterToControl(Float32 newValue); + + /*! @method SetValueFract */ + virtual void SetValueFract(double value); + /*! @method GetValueFract */ + virtual double GetValueFract(); + /*! @method SetTextValue */ + virtual void SetTextValue(CFStringRef str); + /*! @method GetTextValue */ + virtual CFStringRef GetTextValue(); + /*! @method SetValue */ + virtual void SetValue(long value); + /*! @method GetValue */ + virtual long GetValue(); + + /*! @method GetOwnerView */ + AUCarbonViewBase * GetOwnerView() {return mOwnerView;} + + /*! @method Update */ + void Update (bool inUIThread) + { + if (inUIThread) + ParameterToControl (mParam.GetValue()); + else + AUParameterListenerNotify (mListener, this, &mParam); + } + + + // CarbonEventHandler overrides + /*! @method HandleEvent */ + virtual bool HandleEvent(EventHandlerCallRef inHandlerRef, EventRef event); + + /*! @method ControlRef */ + operator ControlRef() { return mControl; } + + /*! @method SizeControlToFit */ + static Boolean SizeControlToFit(ControlRef inControl, SInt16 *outWidth = NULL, SInt16 *outHeight = NULL); + + /*! @method SliderTrackProc */ + static pascal void SliderTrackProc(ControlRef theControl, ControlPartCode partCode); + /*! @method NumericKeyFilterCallback */ + static pascal ControlKeyFilterResult NumericKeyFilterCallback(ControlRef theControl, SInt16 *keyCode, SInt16 *charCode, + EventModifiers *modifiers); +protected: + /*! @method ParamInfo */ + const AudioUnitParameterInfo &ParamInfo() { return mParam.ParamInfo(); } + + /*! @var mOwnerView */ + AUCarbonViewBase * mOwnerView; + /*! @var mListener */ + AUParameterListenerRef mListener; + /*! @var mType */ + ControlType mType; + /*! @var mParam */ + CAAUParameter mParam; + + /*! @var mControl */ + ControlRef mControl; + + /*! @method StdKeyFilterCallback */ + static pascal ControlKeyFilterResult StdKeyFilterCallback(ControlRef theControl, SInt16 *keyCode, SInt16 *charCode, + EventModifiers *modifiers); + SInt16 mInControlInitialization; + + static AUCarbonViewControl* mLastControl; +}; + + /*! @class AUPropertyControl */ +class AUPropertyControl : public CarbonEventHandler { +public: + /*! @ctor AUPropertyControl */ + AUPropertyControl (AUCarbonViewBase * inBase) : mControl(0), mView (inBase), mHeight(0) {} + + /*! @method HandleEvent */ + virtual bool HandleEvent(EventHandlerCallRef inHandlerRef, EventRef event); + + /*! @method HandlePropertyChange */ + virtual bool HandlePropertyChange (const AudioUnitProperty &inProp) = 0; + + /*! @method AddInterest */ + virtual void AddInterest (AUEventListenerRef inListener, + void * inObject) = 0; + + /*! @method RemoveInterest */ + virtual void RemoveInterest (AUEventListenerRef inListener, + void * inObject) = 0; + + /*! @method GetHeight */ + int GetHeight() { return mHeight;} + +protected: + /*! @method HandleControlChange */ + virtual void HandleControlChange () = 0; + + /*! @method RegisterEvents */ + void RegisterEvents (); + + /*! @method EmbedControl */ + void EmbedControl (ControlRef theControl); + + /*! @method GetCarbonWindow */ + WindowRef GetCarbonWindow(); + + /*! @var mControl */ + ControlRef mControl; + /*! @var mView */ + AUCarbonViewBase* mView; + /*! @var mHeight */ + SInt16 mHeight; +}; + + /*! @class AUVPresets */ +class AUVPresets : public AUPropertyControl { +public: + /*! @ctor HandleControlChange */ + AUVPresets (AUCarbonViewBase * inBase, + CFArrayRef& inPresets, + Point inLocation, + int nameWidth, + int controlWidth, + ControlFontStyleRec & inFontStyle); + + virtual ~AUVPresets () { CFRelease (mPresets); } + + /*! @method HandlePropertyChange */ + virtual bool HandlePropertyChange (const AudioUnitProperty &inProp); + + /*! @method AddInterest */ + virtual void AddInterest (AUEventListenerRef inListener, + void * inObject); + + /*! @method RemoveInterest */ + virtual void RemoveInterest (AUEventListenerRef inListener, + void * inObject); + +protected: + /*! @method HandleControlChange */ + virtual void HandleControlChange (); + + /*! @var mPresets */ + CFArrayRef mPresets; + /*! @var mView */ + AUCarbonViewBase* mView; + AudioUnitPropertyID mPropertyID; + + void HandlePropertyChange(AUPreset &preset); +}; + +#endif // __AUCarbonViewControl_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewDispatch.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewDispatch.cpp new file mode 100644 index 0000000000..15a04e827e --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUCarbonViewDispatch.cpp @@ -0,0 +1,125 @@ +/* + File: AUCarbonViewDispatch.cpp + Abstract: AUCarbonViewDispatch.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUCarbonViewBase.h" + +// ____________________________________________________________________________ +// component dispatch + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + struct AudioUnitCarbonViewCreateGluePB { + unsigned char componentFlags; + unsigned char componentParamSize; + short componentWhat; + ControlRef* outControl; + const Float32Point* inSize; + const Float32Point* inLocation; + ControlRef inParentControl; + WindowRef inWindow; + AudioUnit inAudioUnit; + AudioUnitCarbonView inView; + }; +#if !__LP64__ + struct AudioUnitCarbonViewSetEventListenerGluePB { + unsigned char componentFlags; + unsigned char componentParamSize; + short componentWhat; + void* inUserData; + AudioUnitCarbonViewEventListener inCallback; + AudioUnitCarbonView inView; + }; +#endif +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#define CheckNull(x) if ((x) == NULL) return paramErr; + +OSStatus AUCarbonViewBase::ComponentEntryDispatch(ComponentParameters *p, AUCarbonViewBase *This) +{ + if (This == NULL) return paramErr; + + OSStatus result = noErr; + + switch (p->what) { + case kAudioUnitCarbonViewCreateSelect: + { + AudioUnitCarbonViewCreateGluePB *pb = (AudioUnitCarbonViewCreateGluePB *)p; + CheckNull(pb->inAudioUnit); + CheckNull(pb->inWindow); + CheckNull(pb->inParentControl); + CheckNull(pb->inSize); + CheckNull(pb->inLocation); + CheckNull(pb->outControl); + result = This->CreateCarbonView(pb->inAudioUnit, pb->inWindow, pb->inParentControl, + *pb->inLocation, *pb->inSize, *pb->outControl); + } + break; +#if !__LP64__ + case kAudioUnitCarbonViewSetEventListenerSelect: + { + AudioUnitCarbonViewSetEventListenerGluePB *pb = (AudioUnitCarbonViewSetEventListenerGluePB *)p; + This->SetEventListener(pb->inCallback, pb->inUserData); + } + break; +#endif + + default: + result = ComponentBase::ComponentEntryDispatch(p, This); + break; + } + return result; +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUControlGroup.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUControlGroup.cpp new file mode 100644 index 0000000000..8976b401c0 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUControlGroup.cpp @@ -0,0 +1,359 @@ +/* + File: AUControlGroup.cpp + Abstract: AUControlGroup.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include <Carbon/Carbon.h> +#include "AUCarbonViewBase.h" +#include "AUCarbonViewControl.h" +#include "AUControlGroup.h" +#include "AUViewLocalizedStringKeys.h" + +#define kSliderThinDimension 10 +#define kLabelAndSliderSpacing 4 + +#if !__LP64__ +static CFStringRef kStringManufacturer = kAUViewLocalizedStringKey_Manufacturer; +static bool sLocalized = false; +#endif + +void AUControlGroup::CreateLabelledSlider( + AUCarbonViewBase * auView, + const CAAUParameter & auvp, + const Rect & area, + Point labelSize, + const ControlFontStyleRec & inFontStyle) +{ +#if !__LP64__ + ControlFontStyleRec fontStyle = inFontStyle; + Rect minValRect, maxValRect, sliderRect; + ControlRef newControl; + int width = area.right - area.left, height = area.bottom - area.top; + CFStringRef cfstr; + int sliderValueMax, sliderValueMin, sliderValueDefault; + AUCarbonViewControl::ControlType sliderType; + + bool horizontal = (width > height); + + if (horizontal) { + maxValRect.top = minValRect.top = area.top + (height - labelSize.v) / 2; + minValRect.left = area.left; + maxValRect.left = area.right - labelSize.h; + + minValRect.bottom = minValRect.top + labelSize.v; + minValRect.right = minValRect.left + labelSize.h; + maxValRect.bottom = maxValRect.top + labelSize.v; + maxValRect.right = maxValRect.left + labelSize.h; + + sliderRect.left = minValRect.right + kLabelAndSliderSpacing; + sliderRect.right = maxValRect.left - kLabelAndSliderSpacing; + sliderRect.top = area.top + (height - kSliderThinDimension) / 2; + sliderRect.bottom = sliderRect.top + kSliderThinDimension + 4; + + if (auvp.IsIndexedParam ()) { + sliderValueMin = sliderValueDefault = int(auvp.ParamInfo().minValue); + sliderValueMax = int(auvp.ParamInfo().maxValue); + sliderType = AUCarbonViewControl::kTypeDiscrete; + } else { + sliderValueMin = sliderValueDefault = 0; + sliderValueMax = sliderRect.right - sliderRect.left; + sliderType = AUCarbonViewControl::kTypeContinuous; + } + } else { + maxValRect.left = minValRect.left = area.left + (width - labelSize.h) / 2; + maxValRect.top = area.top; + minValRect.top = area.bottom - labelSize.v; + + minValRect.bottom = minValRect.top + labelSize.v; + minValRect.right = minValRect.left + labelSize.h; + maxValRect.bottom = maxValRect.top + labelSize.v; + maxValRect.right = maxValRect.left + labelSize.h; + + sliderRect.left = area.left + (width - kSliderThinDimension) / 2; + sliderRect.right = sliderRect.left + kSliderThinDimension + 4; + sliderRect.top = maxValRect.bottom + kLabelAndSliderSpacing; + sliderRect.bottom = minValRect.top - kLabelAndSliderSpacing; + + if (auvp.IsIndexedParam ()) { + sliderValueMin = sliderValueDefault = int(auvp.ParamInfo().minValue); + sliderValueMax = int(auvp.ParamInfo().maxValue); + sliderType = AUCarbonViewControl::kTypeDiscrete; + } else { + sliderValueMin = sliderValueDefault = 0; + sliderValueMax = sliderRect.bottom - sliderRect.top; + sliderType = AUCarbonViewControl::kTypeContinuous; + } + } + + // minimum value label + if (labelSize.v > 0 && labelSize.h > 0) { + // check to see if the minimum value has a label + cfstr = auvp.GetStringFromValueCopy(&auvp.ParamInfo().minValue); + fontStyle.just = horizontal ? teFlushRight : teCenter; + verify_noerr(CreateStaticTextControl(auView->GetCarbonWindow(), &minValRect, cfstr, &fontStyle, &newControl)); + CFRelease(cfstr); + verify_noerr(auView->EmbedControl(newControl)); + + // maximum value label + cfstr = auvp.GetStringFromValueCopy(&auvp.ParamInfo().maxValue); + fontStyle.just = horizontal ? teFlushLeft : teCenter; + verify_noerr(CreateStaticTextControl(auView->GetCarbonWindow(), &maxValRect, cfstr, &fontStyle, &newControl)); + CFRelease(cfstr); + verify_noerr(auView->EmbedControl(newControl)); + } + + // slider + verify_noerr(CreateSliderControl(auView->GetCarbonWindow(), &sliderRect, sliderValueDefault, sliderValueMin, sliderValueMax, kControlSliderDoesNotPoint, 0, true, AUCarbonViewControl::SliderTrackProc, &newControl)); + + + ControlSize small = kControlSizeSmall; + SetControlData(newControl, kControlEntireControl, kControlSizeTag, sizeof(ControlSize), &small); + auView->AddCarbonControl(sliderType, auvp, newControl); +#endif +} + +void AUControlGroup::CreateLabelledSliderAndEditText( + AUCarbonViewBase * auView, + const CAAUParameter & auvp, + const Rect & area, + Point labelSize, + Point editTextSize, + const ControlFontStyleRec & inFontStyle) +{ +#if !__LP64__ + ControlFontStyleRec fontStyle = inFontStyle; + Rect sliderArea, textArea; + ControlRef newControl; + int width = area.right - area.left, height = area.bottom - area.top; + + bool horizontal = (width > height); + + sliderArea = area; + textArea = area; + if (horizontal) { + textArea.left = area.right - editTextSize.h; + // provide a large text box if param is generic and its values have strings... + if (auvp.ValuesHaveStrings() && (auvp.ParamInfo().unit == kAudioUnitParameterUnit_Generic)) + { + textArea.right += 30; + } + sliderArea.right = textArea.left - kLabelAndSliderSpacing; + textArea.top = area.top + (height - editTextSize.v) / 2; + textArea.bottom = textArea.top + editTextSize.v; + } else { + textArea.top = area.bottom - editTextSize.v; + sliderArea.bottom = textArea.top - kLabelAndSliderSpacing; + textArea.left = area.left + (width - editTextSize.h) / 2; + textArea.right = textArea.left + editTextSize.h; + } + CreateLabelledSlider(auView, auvp, sliderArea, labelSize, fontStyle); + + verify_noerr(CreateEditUnicodeTextControl(auView->GetCarbonWindow(), &textArea, CFSTR(""), false, + &fontStyle, &newControl)); + auView->AddCarbonControl(AUCarbonViewControl::kTypeText, auvp, newControl); +#endif +} + +void AUControlGroup::CreatePopupMenu (AUCarbonViewBase * auView, + const CAAUParameter & auvp, + const Rect & area, + const ControlFontStyleRec & inFontStyle, + const bool inSizeToFit) +{ +#if !__LP64__ + ControlRef thePopUp; + + verify_noerr(CreatePopupButtonControl (auView->GetCarbonWindow(), &area, NULL, + -12345, // DON'T GET MENU FROM RESOURCE mMenuID + FALSE, // variableWidth, + 0, // titleWidth, + 0, // titleJustification, + 0, // titleStyle, + &thePopUp)); + + ControlSize small = kControlSizeSmall; + SetControlData(thePopUp, kControlEntireControl, kControlSizeTag, sizeof(ControlSize), &small); + + MenuRef menuRef; + verify_noerr(CreateNewMenu( 1, 0, &menuRef)); + + for (int i = 0; i < auvp.GetNumIndexedParams(); ++i) { + verify_noerr(AppendMenuItemTextWithCFString (menuRef, auvp.GetParamName(i), kMenuItemAttrIgnoreMeta, 0, 0)); + } + + verify_noerr(SetControlData(thePopUp, 0, kControlPopupButtonMenuRefTag, sizeof(menuRef), &menuRef)); + SetControl32BitMaximum(thePopUp, auvp.GetNumIndexedParams()); + + verify_noerr (SetControlFontStyle (thePopUp, &inFontStyle)); + + if (inSizeToFit) { + AUCarbonViewControl::SizeControlToFit(thePopUp); + } + + auView->AddCarbonControl(AUCarbonViewControl::kTypeDiscrete, auvp, thePopUp); +#endif +} + +void AUControlGroup::AddAUInfo ( AUCarbonViewBase * auView, + const Point & inLocation, + const SInt16 inRightOffset, + const SInt16 inTotalWidth) +{ +#if !__LP64__ + // get component info + ComponentDescription desc; + Handle h1 = NewHandleClear(4); + OSStatus err = GetComponentInfo ((Component)auView->GetEditAudioUnit(), &desc, h1, 0, 0); + + if (err == noErr) { + // Get the manufacturer's name... look for the ':' character convention + HLock(h1); + char* ptr1 = *h1; + int len = *ptr1++; + char* displayStr = 0; + + for (int i = 0; i < len; ++i) { + if (ptr1[i] == ':') { // found the name + ptr1[i++] = 0; + displayStr = ptr1; + break; + } + } + + // localize as necessary: + if (!sLocalized) { + CFBundleRef mainBundle = CFBundleGetBundleWithIdentifier(kLocalizedStringBundle_AUView); + if (mainBundle) { + kStringManufacturer = CFCopyLocalizedStringFromTableInBundle( + kAUViewLocalizedStringKey_Manufacturer, kLocalizedStringTable_AUView, + mainBundle, CFSTR("Manufacturer title string")); + sLocalized = true; + } + } + + // display strings + ControlRef newControl; + Rect r; + r.top = SInt16(inLocation.v); r.bottom = SInt16(inLocation.v) + 16; + ControlFontStyleRec fontStyle; + fontStyle.flags = kControlUseFontMask | kControlUseJustMask; + fontStyle.font = kControlFontSmallBoldSystemFont; + + // display manufacturer string + if (displayStr) { + CFMutableStringRef mfrstring = CFStringCreateMutable(NULL, 0); + CFStringAppend(mfrstring, kStringManufacturer); // "Manufacturer" + CFStringAppend(mfrstring, kAUViewUnlocalizedString_TitleSeparator); + // "Manufacturer: " + CFStringRef mfrname = CFStringCreateWithCString(NULL, displayStr, kCFStringEncodingUTF8); + if (mfrname) { + CFStringAppend(mfrstring, mfrname); // "Manufacturer: MFRName" + CFRelease (mfrname); + } + + r.left = inLocation.h + inRightOffset; + r.right = inLocation.h + inTotalWidth - 28; + fontStyle.just = teFlushRight; + + verify_noerr(CreateStaticTextControl(auView->GetCarbonWindow(), &r, mfrstring, &fontStyle, &newControl)); + verify_noerr(auView->EmbedControl(newControl)); + CFRelease (mfrstring); + + //move displayStr ptr past the manu, to the name + // we move the characters down an index, because the handle doesn't have any room + // at the end for the \0 + int i = strlen(displayStr), j = 0; + while (displayStr[++i] == ' ' && i < len) + ; + while (i < len) + displayStr[j++] = displayStr[i++]; + displayStr[j] = 0; + } else { + displayStr = ptr1; + int i = 0, j = 0; + do { + displayStr[j] = displayStr[i]; + ++j; ++i; + } while (i < len); + + displayStr[j] = 0; + } + + // display AudioUnit string + r.left = inLocation.h; r.right = r.left + inRightOffset; + fontStyle.just = 0; + + CFMutableStringRef cfstr = CFStringCreateMutable(NULL, 0); + CFStringAppend(cfstr, kAUViewLocalizedStringKey_AudioUnit); // "Audio Unit" + CFStringAppend(cfstr, kAUViewUnlocalizedString_TitleSeparator); + // "Audio Unit: " + + CFStringRef auname = CFStringCreateWithCString(NULL, displayStr, kCFStringEncodingUTF8); + CFStringAppend(cfstr, auname); // "Audio Unit: AUName" + CFRelease (auname); + + verify_noerr(CreateStaticTextControl(auView->GetCarbonWindow(), &r, cfstr, &fontStyle, &newControl)); + + // size text control correctly + Boolean bValue = false; + SetControlData(newControl, kControlEntireControl, 'stim' /* kControlStaticTextIsMultilineTag */, sizeof(Boolean), &bValue); + SInt16 baseLineOffset; + Rect bestRect; + err = GetBestControlRect(newControl, &bestRect, &baseLineOffset); + if (err == noErr) + { + int width = (bestRect.right - bestRect.left) + 1; + int height = (bestRect.bottom - bestRect.top) + 1; + SizeControl (newControl, width, height); + } + + verify_noerr(auView->EmbedControl(newControl)); + CFRelease (cfstr); + } + + DisposeHandle (h1); +#endif +} + + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUControlGroup.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUControlGroup.h new file mode 100644 index 0000000000..0e16100d2a --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/AUControlGroup.h @@ -0,0 +1,90 @@ +/* + File: AUControlGroup.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUControlGroup_h__ +#define __AUControlGroup_h__ + +#include <Carbon/Carbon.h> + +class AUCarbonViewBase; +class CAAUParameter; + +// Utility class to create clusters of controls related to a single parameter + /*! @class AUControlGroup */ +class AUControlGroup { +public: + /*! @method CreateLabelledSlider */ + static void CreateLabelledSlider( AUCarbonViewBase * auView, + const CAAUParameter & auvp, + const Rect & area, + Point labelSize, + const ControlFontStyleRec & fontStyle); + + /*! @method CreateLabelledSliderAndEditText */ + static void CreateLabelledSliderAndEditText( + AUCarbonViewBase * auView, + const CAAUParameter & auvp, + const Rect & area, + Point labelSize, + Point editTextSize, + const ControlFontStyleRec & fontStyle); + + /*! @method CreatePopupMenu */ + static void CreatePopupMenu ( AUCarbonViewBase * auView, + const CAAUParameter & auvp, + const Rect & area, + const ControlFontStyleRec & inFontStyle, + const bool inSizeToFit = false); + + /*! @method AddAUInfo */ + static void AddAUInfo ( AUCarbonViewBase * auView, + const Point & inLocation, + const SInt16 inRightOffset, + const SInt16 inTotalWidth); +}; + + +#endif // __AUControlGroup_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/CarbonEventHandler.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/CarbonEventHandler.cpp new file mode 100644 index 0000000000..fe9a731d1d --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/CarbonEventHandler.cpp @@ -0,0 +1,90 @@ +/* + File: CarbonEventHandler.cpp + Abstract: CarbonEventHandler.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "CarbonEventHandler.h" + +static pascal OSStatus TheEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData) +{ + CarbonEventHandler *handler = (CarbonEventHandler *)inUserData; + if (handler->HandleEvent(inHandlerRef, inEvent)) + return noErr; + else return eventNotHandledErr; +} + +CarbonEventHandler::CarbonEventHandler() : + mHandlers(NULL) +{ +} + +CarbonEventHandler::~CarbonEventHandler() +{ + if (mHandlers != NULL) { + int count = static_cast<int>(CFDictionaryGetCount(mHandlers)); + EventHandlerRef *theHandlers = (EventHandlerRef*) malloc(count * sizeof(EventHandlerRef)); + CFDictionaryGetKeysAndValues(mHandlers, NULL, (const void **)theHandlers); + + for (int i = 0; i < count; i++) + RemoveEventHandler(theHandlers[i]); + CFDictionaryRemoveAllValues(mHandlers); + CFRelease (mHandlers); + free(theHandlers); + } +} + +void CarbonEventHandler::WantEventTypes(EventTargetRef target, UInt32 inNumTypes, const EventTypeSpec *inList) +{ + if (mHandlers == NULL) + mHandlers = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); + + EventHandlerRef handler; + + if (CFDictionaryGetValueIfPresent (mHandlers, target, (const void **)&handler)) // if there is already a handler for the target, add the type + verify_noerr(AddEventTypesToHandler(handler, inNumTypes, inList)); + else { + verify_noerr(InstallEventHandler(target, TheEventHandler, inNumTypes, inList, this, &handler)); + CFDictionaryAddValue(mHandlers, target, handler); + } +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/CarbonEventHandler.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/CarbonEventHandler.h new file mode 100644 index 0000000000..9225e1acd7 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUCarbonViewBase/CarbonEventHandler.h @@ -0,0 +1,71 @@ +/* + File: CarbonEventHandler.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __CarbonEventHandler_h__ +#define __CarbonEventHandler_h__ + +#include <Carbon/Carbon.h> + + /*! @class CarbonEventHandler */ +class CarbonEventHandler { +public: + /*! @ctor CarbonEventHandler */ + CarbonEventHandler(); + /*! @dtor ~CarbonEventHandler */ + virtual ~CarbonEventHandler(); + + /*! @method WantEventTypes */ + virtual void WantEventTypes(EventTargetRef target, UInt32 inNumTypes, const EventTypeSpec *inList); + + /*! @method HandleEvent */ + virtual bool HandleEvent(EventHandlerCallRef inHandlerRef, EventRef event) = 0; + +protected: + /*! @var mHandlers */ + CFMutableDictionaryRef mHandlers; +}; + +#endif // __CarbonEventHandler_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.cpp new file mode 100644 index 0000000000..1ce8b308b4 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.cpp @@ -0,0 +1,843 @@ +/* + File: AUInstrumentBase.cpp + Abstract: AUInstrumentBase.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUInstrumentBase.h" +#include "AUMIDIDefs.h" + +#if DEBUG + #define DEBUG_PRINT 0 + #define DEBUG_PRINT_NOTE 0 + #define DEBUG_PRINT_RENDER 0 +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const UInt32 kEventQueueSize = 1024; + +AUInstrumentBase::AUInstrumentBase( + AudioComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups, + UInt32 numParts) + : MusicDeviceBase(inInstance, numInputs, numOutputs, numGroups), + mAbsoluteSampleFrame(0), + mEventQueue(kEventQueueSize), + mNumNotes(0), + mNumActiveNotes(0), + mMaxActiveNotes(0), + mNotes(0), + mNoteSize(0), + mInitNumPartEls(numParts) +{ +#if DEBUG_PRINT + printf("new AUInstrumentBase\n"); +#endif + mFreeNotes.mState = kNoteState_Free; + SetWantsRenderThreadID(true); +} + + +AUInstrumentBase::~AUInstrumentBase() +{ +#if DEBUG_PRINT + printf("delete AUInstrumentBase\n"); +#endif +} + +AUElement * AUInstrumentBase::CreateElement(AudioUnitScope inScope, AudioUnitElement element) +{ + switch (inScope) + { + case kAudioUnitScope_Group: + return new SynthGroupElement(this, element, new MidiControls); + case kAudioUnitScope_Part: + return new SynthPartElement (this, element); + } + return MusicDeviceBase::CreateElement(inScope, element); +} + +void AUInstrumentBase::CreateExtendedElements() +{ + Parts().Initialize(this, kAudioUnitScope_Part, mInitNumPartEls); +} + +AUScope * AUInstrumentBase::GetScopeExtended (AudioUnitScope inScope) +{ + if (inScope == kAudioUnitScope_Part) + return &mPartScope; + return NULL; +} + + +void AUInstrumentBase::SetNotes(UInt32 inNumNotes, UInt32 inMaxActiveNotes, SynthNote* inNotes, UInt32 inNoteDataSize) +{ +#if DEBUG_PRINT_NOTE + printf("AUInstrumentBase::SetNotes %d %d %p %d\n", inNumNotes, inMaxActiveNotes, inNotes, inNoteDataSize); +#endif + mNumNotes = inNumNotes; + mMaxActiveNotes = inMaxActiveNotes; + mNoteSize = inNoteDataSize; + mNotes = inNotes; + + for (UInt32 i=0; i<mNumNotes; ++i) + { + SynthNote *note = GetNote(i); + note->Reset(); + mFreeNotes.AddNote(note); + } +} + +UInt32 AUInstrumentBase::CountActiveNotes() +{ + // debugging tool. + UInt32 sum = 0; + for (UInt32 i=0; i<mNumNotes; ++i) + { + SynthNote *note = GetNote(i); + if (note->GetState() <= kNoteState_Released) + sum++; + } + return sum; +} + +void AUInstrumentBase::AddFreeNote(SynthNote* inNote) +{ + // Fast-released notes are already considered inactive and have already decr'd the active count + if (inNote->GetState() < kNoteState_FastReleased) { + DecNumActiveNotes(); + } +#if DEBUG_PRINT_NOTE + else { + printf("AUInstrumentBase::AddFreeNote: adding fast-released note %p\n", inNote); + } + printf("AUInstrumentBase::AddFreeNote (%p) mNumActiveNotes %lu\n", inNote, mNumActiveNotes); +#endif + mFreeNotes.AddNote(inNote); +} + +OSStatus AUInstrumentBase::Initialize() +{ +/* +TO DO: + Currently ValidFormat will check and validate that the num channels is not being + changed if the AU doesn't support the SupportedNumChannels property - which is correct + + What needs to happen here is that IFF the AU does support this property, (ie, the AU + can be configured to have different num channels than its original configuration) then + the state of the AU at Initialization needs to be validated. + + This is work still to be done - see AUEffectBase for the kind of logic that needs to be applied here +*/ + + // override to call SetNotes + + mNoteIDCounter = 128; // reset this every time we initialise + mAbsoluteSampleFrame = 0; + return noErr; +} + +void AUInstrumentBase::Cleanup() +{ + mFreeNotes.Empty(); +} + + +OSStatus AUInstrumentBase::Reset( AudioUnitScope inScope, + AudioUnitElement inElement) +{ +#if DEBUG_PRINT + printf("AUInstrumentBase::Reset\n"); +#endif + if (inScope == kAudioUnitScope_Global) + { + // kill all notes.. + mFreeNotes.Empty(); + for (UInt32 i=0; i<mNumNotes; ++i) + { + SynthNote *note = GetNote(i); + if (note->IsSounding()) + note->Kill(0); + note->ListRemove(); + mFreeNotes.AddNote(note); + } + mNumActiveNotes = 0; + mAbsoluteSampleFrame = 0; + + // empty lists. + UInt32 numGroups = Groups().GetNumberOfElements(); + for (UInt32 j = 0; j < numGroups; ++j) + { + SynthGroupElement *group = (SynthGroupElement*)Groups().GetElement(j); + group->Reset(); + } + } + return MusicDeviceBase::Reset(inScope, inElement); +} + +void AUInstrumentBase::PerformEvents(const AudioTimeStamp& inTimeStamp) +{ +#if DEBUG_PRINT_RENDER + printf("AUInstrumentBase::PerformEvents\n"); +#endif + SynthEvent *event; + SynthGroupElement *group; + + while ((event = mEventQueue.ReadItem()) != NULL) + { +#if DEBUG_PRINT_RENDER + printf("event %08X %d\n", event, event->GetEventType()); +#endif + switch(event->GetEventType()) + { + case SynthEvent::kEventType_NoteOn : + RealTimeStartNote(GetElForGroupID (event->GetGroupID()), event->GetNoteID(), + event->GetOffsetSampleFrame(), *event->GetParams()); + break; + case SynthEvent::kEventType_NoteOff : + RealTimeStopNote(event->GetGroupID(), event->GetNoteID(), + event->GetOffsetSampleFrame()); + break; + case SynthEvent::kEventType_SustainOn : + group = GetElForGroupID (event->GetGroupID()); + group->SustainOn(event->GetOffsetSampleFrame()); + break; + case SynthEvent::kEventType_SustainOff : + group = GetElForGroupID (event->GetGroupID()); + group->SustainOff(event->GetOffsetSampleFrame()); + break; + case SynthEvent::kEventType_SostenutoOn : + group = GetElForGroupID (event->GetGroupID()); + group->SostenutoOn(event->GetOffsetSampleFrame()); + break; + case SynthEvent::kEventType_SostenutoOff : + group = GetElForGroupID (event->GetGroupID()); + group->SostenutoOff(event->GetOffsetSampleFrame()); + break; + case SynthEvent::kEventType_AllNotesOff : + group = GetElForGroupID (event->GetGroupID()); + group->AllNotesOff(event->GetOffsetSampleFrame()); + break; + case SynthEvent::kEventType_AllSoundOff : + group = GetElForGroupID (event->GetGroupID()); + group->AllSoundOff(event->GetOffsetSampleFrame()); + break; + case SynthEvent::kEventType_ResetAllControllers : + group = GetElForGroupID (event->GetGroupID()); + group->ResetAllControllers(event->GetOffsetSampleFrame()); + break; + } + + mEventQueue.AdvanceReadPtr(); + } +} + + +OSStatus AUInstrumentBase::Render( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inNumberFrames) +{ + PerformEvents(inTimeStamp); + + AUScope &outputs = Outputs(); + UInt32 numOutputs = outputs.GetNumberOfElements(); + for (UInt32 j = 0; j < numOutputs; ++j) + { + GetOutput(j)->PrepareBuffer(inNumberFrames); // AUBase::DoRenderBus() only does this for the first output element + AudioBufferList& bufferList = GetOutput(j)->GetBufferList(); + for (UInt32 k = 0; k < bufferList.mNumberBuffers; ++k) + { + memset(bufferList.mBuffers[k].mData, 0, bufferList.mBuffers[k].mDataByteSize); + } + } + UInt32 numGroups = Groups().GetNumberOfElements(); + for (UInt32 j = 0; j < numGroups; ++j) + { + SynthGroupElement *group = (SynthGroupElement*)Groups().GetElement(j); + OSStatus err = group->Render((SInt64)inTimeStamp.mSampleTime, inNumberFrames, outputs); + if (err) return err; + } + mAbsoluteSampleFrame += inNumberFrames; + return noErr; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// AUInstrumentBase::ValidFormat +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bool AUInstrumentBase::ValidFormat( AudioUnitScope inScope, + AudioUnitElement inElement, + const CAStreamBasicDescription & inNewFormat) +{ + // if the AU supports this, then we should just let this go through to the Init call + if (SupportedNumChannels (NULL)) + return MusicDeviceBase::ValidFormat(inScope, inElement, inNewFormat); + + bool isGood = MusicDeviceBase::ValidFormat (inScope, inElement, inNewFormat); + if (!isGood) return false; + + // if we get to here, then the basic criteria is that the + // num channels cannot change on an existing bus + AUIOElement *el = GetIOElement (inScope, inElement); + return (el->GetStreamFormat().NumberChannels() == inNewFormat.NumberChannels()); +} + + +bool AUInstrumentBase::StreamFormatWritable( AudioUnitScope scope, + AudioUnitElement element) +{ + return IsInitialized() ? false : true; +} + +OSStatus AUInstrumentBase::RealTimeStartNote( SynthGroupElement *inGroup, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams) +{ + return noErr; +} + +SynthPartElement * AUInstrumentBase::GetPartElement (AudioUnitElement inPartElement) +{ + AUScope & parts = Parts(); + unsigned int numEls = parts.GetNumberOfElements(); + for (unsigned int i = 0; i < numEls; ++i) { + SynthPartElement* el = reinterpret_cast<SynthPartElement*>(parts.GetElement(i)); + if (el->GetIndex() == inPartElement) { + return el; + } + } + return NULL; +} + +SynthGroupElement * AUInstrumentBase::GetElForGroupID (MusicDeviceGroupID inGroupID) +{ + AUScope & groups = Groups(); + unsigned int numEls = groups.GetNumberOfElements(); + SynthGroupElement* unassignedEl = NULL; + + for (unsigned int i = 0; i < numEls; ++i) { + SynthGroupElement* el = reinterpret_cast<SynthGroupElement*>(groups.GetElement(i)); + if (el->GroupID() == inGroupID) + return el; + if (el->GroupID() == SynthGroupElement::kUnassignedGroup) { + unassignedEl = el; + break; // we fill this up from the start of the group scope vector + } + } + if (unassignedEl) { + unassignedEl->SetGroupID(inGroupID); + return unassignedEl; + } + throw static_cast<OSStatus>(kAudioUnitErr_InvalidElement); +} + +OSStatus AUInstrumentBase::RealTimeStopNote( + MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame) +{ +#if DEBUG_PRINT + printf("AUInstrumentBase::RealTimeStopNote ch %d id %d\n", inGroupID, inNoteInstanceID); +#endif + + SynthGroupElement *gp = (inGroupID == kMusicNoteEvent_Unused + ? GetElForNoteID (inNoteInstanceID) + : GetElForGroupID(inGroupID)); + if (gp) + { + gp->NoteOff (inNoteInstanceID, inOffsetSampleFrame); + } + + return noErr; +} + +SynthGroupElement * AUInstrumentBase::GetElForNoteID (NoteInstanceID inNoteID) +{ +#if DEBUG_PRINT + printf("GetElForNoteID id %u\n", inNoteID); +#endif + AUScope & groups = Groups(); + unsigned int numEls = groups.GetNumberOfElements(); + + for (unsigned int i = 0; i < numEls; ++i) { + SynthGroupElement* el = reinterpret_cast<SynthGroupElement*>(groups.GetElement(i)); + if (el->GetNote(inNoteID) != NULL) // searches for any note state + return el; + } + throw static_cast<OSStatus>(kAudioUnitErr_InvalidElement); +} + +OSStatus AUInstrumentBase::StartNote( MusicDeviceInstrumentID inInstrument, + MusicDeviceGroupID inGroupID, + NoteInstanceID * outNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams) +{ + OSStatus err = noErr; + + NoteInstanceID noteID; + if (outNoteInstanceID) { + noteID = NextNoteID(); + *outNoteInstanceID = noteID; + } else + noteID = (UInt32)inParams.mPitch; + +#if DEBUG_PRINT + printf("AUInstrumentBase::StartNote ch %u, key %u, offset %u\n", inGroupID, (unsigned) inParams.mPitch, inOffsetSampleFrame); +#endif + + if (InRenderThread ()) + { + err = RealTimeStartNote( + GetElForGroupID(inGroupID), + noteID, + inOffsetSampleFrame, + inParams); + } + else + { + SynthEvent *event = mEventQueue.WriteItem(); + if (!event) return -1; // queue full + + event->Set( + SynthEvent::kEventType_NoteOn, + inGroupID, + noteID, + inOffsetSampleFrame, + &inParams + ); + + mEventQueue.AdvanceWritePtr(); + } + return err; +} + +OSStatus AUInstrumentBase::StopNote( MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame) +{ +#if DEBUG_PRINT + printf("AUInstrumentBase::StopNote ch %u, id %u, offset %u\n", (unsigned)inGroupID, (unsigned)inNoteInstanceID, inOffsetSampleFrame); +#endif + OSStatus err = noErr; + + if (InRenderThread ()) + { + err = RealTimeStopNote( + inGroupID, + inNoteInstanceID, + inOffsetSampleFrame); + } + else + { + SynthEvent *event = mEventQueue.WriteItem(); + if (!event) return -1; // queue full + + event->Set( + SynthEvent::kEventType_NoteOff, + inGroupID, + inNoteInstanceID, + inOffsetSampleFrame, + NULL + ); + + mEventQueue.AdvanceWritePtr(); + } + return err; +} + +OSStatus AUInstrumentBase::SendPedalEvent(MusicDeviceGroupID inGroupID, UInt32 inEventType, UInt32 inOffsetSampleFrame) +{ + + if (InRenderThread ()) + { + SynthGroupElement *group = GetElForGroupID(inGroupID); + if (!group) + return kAudioUnitErr_InvalidElement; + + switch (inEventType) + { + case SynthEvent::kEventType_SustainOn : + group->SustainOn(inOffsetSampleFrame); + break; + case SynthEvent::kEventType_SustainOff : + group->SustainOff(inOffsetSampleFrame); + break; + case SynthEvent::kEventType_SostenutoOn : + group->SostenutoOn(inOffsetSampleFrame); + break; + case SynthEvent::kEventType_SostenutoOff : + group->SostenutoOff(inOffsetSampleFrame); + break; + case SynthEvent::kEventType_AllNotesOff : + group->AllNotesOff(inOffsetSampleFrame); + mNumActiveNotes = CountActiveNotes(); + break; + case SynthEvent::kEventType_AllSoundOff : + group->AllSoundOff(inOffsetSampleFrame); + mNumActiveNotes = CountActiveNotes(); + break; + case SynthEvent::kEventType_ResetAllControllers : + group->ResetAllControllers(inOffsetSampleFrame); + break; + } + } + else + { + SynthEvent *event = mEventQueue.WriteItem(); + if (!event) return -1; // queue full + + event->Set(inEventType, inGroupID, 0, 0, NULL); + + mEventQueue.AdvanceWritePtr(); + } + return noErr; +} + +OSStatus AUInstrumentBase::HandleControlChange( UInt8 inChannel, + UInt8 inController, + UInt8 inValue, + UInt32 inStartFrame) +{ +#if DEBUG_PRINT + printf("AUInstrumentBase::HandleControlChange ch %u ctlr: %u val: %u frm: %u\n", inChannel, inController, inValue, inStartFrame); +#endif + SynthGroupElement *gp = GetElForGroupID(inChannel); + if (gp) + { + gp->ChannelMessage(inController, inValue); + } + else + return kAudioUnitErr_InvalidElement; + switch (inController) + { + case kMidiController_Sustain : + if (inValue >= 64) + SendPedalEvent(inChannel, SynthEvent::kEventType_SustainOn, inStartFrame); + else + SendPedalEvent(inChannel, SynthEvent::kEventType_SustainOff, inStartFrame); + break; + case kMidiController_Sostenuto : + if (inValue >= 64) + SendPedalEvent(inChannel, SynthEvent::kEventType_SostenutoOn, inStartFrame); + else + SendPedalEvent(inChannel, SynthEvent::kEventType_SostenutoOff, inStartFrame); + break; + case kMidiController_OmniModeOff: + case kMidiController_OmniModeOn: + case kMidiController_MonoModeOn: + case kMidiController_MonoModeOff: + HandleAllSoundOff(inChannel); + break; + } + return noErr; +} + +OSStatus AUInstrumentBase::HandlePitchWheel( UInt8 inChannel, + UInt8 inPitch1, // LSB + UInt8 inPitch2, // MSB + UInt32 inStartFrame) +{ + SynthGroupElement *gp = GetElForGroupID(inChannel); + if (gp) + { + gp->ChannelMessage(kMidiMessage_PitchWheel, (inPitch2 << 7) | inPitch1); + return noErr; + } + else + return kAudioUnitErr_InvalidElement; +} + + +OSStatus AUInstrumentBase::HandleChannelPressure(UInt8 inChannel, + UInt8 inValue, + UInt32 inStartFrame) +{ + SynthGroupElement *gp = GetElForGroupID(inChannel); + if (gp) + { + gp->ChannelMessage(kMidiMessage_ChannelPressure, inValue); + return noErr; + } + else + return kAudioUnitErr_InvalidElement; +} + + +OSStatus AUInstrumentBase::HandleProgramChange( UInt8 inChannel, + UInt8 inValue) +{ +#if DEBUG_PRINT + printf("AUInstrumentBase::HandleProgramChange %u %u\n", inChannel, inValue); +#endif + SynthGroupElement *gp = GetElForGroupID(inChannel); + if (gp) + { + gp->ChannelMessage(kMidiMessage_ProgramChange, inValue); + return noErr; + } + else + return kAudioUnitErr_InvalidElement; +} + + +OSStatus AUInstrumentBase::HandlePolyPressure( UInt8 inChannel, + UInt8 inKey, + UInt8 inValue, + UInt32 inStartFrame) +{ + SynthGroupElement *gp = GetElForGroupID(inChannel); + if (gp) + { + // Combine key and value into single argument. UGLY! + gp->ChannelMessage(kMidiMessage_PolyPressure, (inKey << 7) | inValue); + return noErr; + } + else + return kAudioUnitErr_InvalidElement; +} + + +OSStatus AUInstrumentBase::HandleResetAllControllers( UInt8 inChannel) +{ + return SendPedalEvent (inChannel, SynthEvent::kEventType_ResetAllControllers, 0); +} + + +OSStatus AUInstrumentBase::HandleAllNotesOff( UInt8 inChannel) +{ + return SendPedalEvent (inChannel, SynthEvent::kEventType_AllNotesOff, 0); +} + + +OSStatus AUInstrumentBase::HandleAllSoundOff( UInt8 inChannel) +{ + return SendPedalEvent (inChannel, SynthEvent::kEventType_AllSoundOff, 0); +} + +SynthNote* AUInstrumentBase::GetAFreeNote(UInt32 inFrame) +{ +#if DEBUG_PRINT_NOTE + printf("AUInstrumentBase::GetAFreeNote: %lu available\n", mFreeNotes.Length()); +#endif + SynthNote *note = mFreeNotes.mHead; + if (note) + { + mFreeNotes.RemoveNote(note); + return note; + } + + return VoiceStealing(inFrame, true); +} + +SynthNote* AUInstrumentBase::VoiceStealing(UInt32 inFrame, bool inKillIt) +{ + +#if DEBUG_PRINT_NOTE + printf("AUInstrumentBase::VoiceStealing\n"); +#endif + // free list was empty so we need to kill a note. + UInt32 startState = inKillIt ? kNoteState_FastReleased : kNoteState_Released; + for (UInt32 i = startState; i <= startState; --i) + { +#if DEBUG_PRINT_NOTE + printf(" checking state %d...\n", i); +#endif + UInt32 numGroups = Groups().GetNumberOfElements(); + for (UInt32 j = 0; j < numGroups; ++j) + { + SynthGroupElement *group = (SynthGroupElement*)Groups().GetElement(j); +#if DEBUG_PRINT_NOTE + printf("\tsteal group %d size %d\n", j, group->mNoteList[i].Length()); +#endif + if (group->mNoteList[i].NotEmpty()) { +#if DEBUG_PRINT_NOTE + printf("\t-- not empty\n"); +#endif + SynthNote *note = group->mNoteList[i].FindMostQuietNote(); + if (inKillIt) { +#if DEBUG_PRINT_NOTE + printf("\t--=== KILL ===---\n"); +#endif + note->Kill(inFrame); + group->mNoteList[i].RemoveNote(note); + if (i != kNoteState_FastReleased) + DecNumActiveNotes(); + return note; + } else { +#if DEBUG_PRINT_NOTE + printf("\t--=== FAST RELEASE ===---\n"); +#endif + group->mNoteList[i].RemoveNote(note); + note->FastRelease(inFrame); + group->mNoteList[kNoteState_FastReleased].AddNote(note); + DecNumActiveNotes(); // kNoteState_FastReleased counts as inactive for voice stealing purposes. + return NULL; + } + } + } + } +#if DEBUG_PRINT_NOTE + printf("no notes to steal????\n"); +#endif + return NULL; // It should be impossible to get here. It means there were no notes to kill in any state. +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +AUMonotimbralInstrumentBase::AUMonotimbralInstrumentBase( + AudioComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups, + UInt32 numParts) + : AUInstrumentBase(inInstance, numInputs, numOutputs, numGroups, numParts) +{ +} + +OSStatus AUMonotimbralInstrumentBase::RealTimeStartNote( + SynthGroupElement *inGroup, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams) +{ +#if DEBUG_PRINT_RENDER + printf("AUMonotimbralInstrumentBase::RealTimeStartNote %d\n", inNoteInstanceID); +#endif + + if (NumActiveNotes() + 1 > MaxActiveNotes()) + { + VoiceStealing(inOffsetSampleFrame, false); + } + SynthNote *note = GetAFreeNote(inOffsetSampleFrame); + if (!note) return -1; + + SynthPartElement *part = GetPartElement (0); // Only one part for monotimbral + + IncNumActiveNotes(); + inGroup->NoteOn(note, part, inNoteInstanceID, inOffsetSampleFrame, inParams); + + return noErr; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +OSStatus AUMultitimbralInstrumentBase::GetPropertyInfo(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable) +{ + OSStatus result = noErr; + + switch (inID) + { +#if !TARGET_OS_IPHONE + case kMusicDeviceProperty_PartGroup: + if (inScope != kAudioUnitScope_Part) return kAudioUnitErr_InvalidScope; + outDataSize = sizeof(UInt32); + outWritable = true; + break; +#endif + default: + result = AUInstrumentBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable); + } + return result; +} + +OSStatus AUMultitimbralInstrumentBase::GetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData) +{ + OSStatus result = noErr; + + switch (inID) + { +#if !TARGET_OS_IPHONE + case kMusicDeviceProperty_PartGroup: + if (inScope != kAudioUnitScope_Group) return kAudioUnitErr_InvalidScope; + // ?? + return -1; //unimpl + break; +#endif + default: + result = AUInstrumentBase::GetProperty (inID, inScope, inElement, outData); + } + + return result; +} + + + +OSStatus AUMultitimbralInstrumentBase::SetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize) +{ + OSStatus result = noErr; + + switch (inID) + { +#if !TARGET_OS_IPHONE + case kMusicDeviceProperty_PartGroup: + if (inScope != kAudioUnitScope_Group) return kAudioUnitErr_InvalidScope; + // ?? + return -1; //unimpl + break; +#endif + default: + result = MusicDeviceBase::SetProperty (inID, inScope, inElement, inData, inDataSize); + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.h new file mode 100644 index 0000000000..3ad7e03a66 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/AUInstrumentBase.h @@ -0,0 +1,269 @@ +/* + File: AUInstrumentBase.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUInstrumentBase__ +#define __AUInstrumentBase__ + +#include <vector> +#include <stdexcept> +#include <AudioUnit/AudioUnit.h> +#include <CoreAudio/CoreAudio.h> +#include <libkern/OSAtomic.h> +#include "MusicDeviceBase.h" +#include "LockFreeFIFO.h" +#include "SynthEvent.h" +#include "SynthNote.h" +#include "SynthElement.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef LockFreeFIFOWithFree<SynthEvent> SynthEventQueue; + +class AUInstrumentBase : public MusicDeviceBase +{ +public: + AUInstrumentBase( + AudioComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups = 16, + UInt32 numParts = 1); + virtual ~AUInstrumentBase(); + + virtual OSStatus Initialize(); + + /*! @method Parts */ + AUScope & Parts() { return mPartScope; } + + /*! @method GetPart */ + AUElement * GetPart( AudioUnitElement inElement) + { + return mPartScope.SafeGetElement(inElement); + } + + virtual AUScope * GetScopeExtended (AudioUnitScope inScope); + + virtual AUElement * CreateElement( AudioUnitScope inScope, + AudioUnitElement inElement); + + virtual void CreateExtendedElements(); + + virtual void Cleanup(); + + virtual OSStatus Reset( AudioUnitScope inScope, + AudioUnitElement inElement); + + virtual bool ValidFormat( AudioUnitScope inScope, + AudioUnitElement inElement, + const CAStreamBasicDescription & inNewFormat); + + virtual bool StreamFormatWritable( AudioUnitScope scope, + AudioUnitElement element); + + virtual bool CanScheduleParameters() const { return false; } + + virtual OSStatus Render( AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inNumberFrames); + + virtual OSStatus StartNote( MusicDeviceInstrumentID inInstrument, + MusicDeviceGroupID inGroupID, + NoteInstanceID * outNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams); + + virtual OSStatus StopNote( MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame); + + virtual OSStatus RealTimeStartNote( SynthGroupElement *inGroup, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams); + + virtual OSStatus RealTimeStopNote( MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame); + + virtual OSStatus HandleControlChange( UInt8 inChannel, + UInt8 inController, + UInt8 inValue, + UInt32 inStartFrame); + + virtual OSStatus HandlePitchWheel( UInt8 inChannel, + UInt8 inPitch1, + UInt8 inPitch2, + UInt32 inStartFrame); + + virtual OSStatus HandleChannelPressure( UInt8 inChannel, + UInt8 inValue, + UInt32 inStartFrame); + + virtual OSStatus HandleProgramChange( UInt8 inChannel, + UInt8 inValue); + + virtual OSStatus HandlePolyPressure( UInt8 inChannel, + UInt8 inKey, + UInt8 inValue, + UInt32 inStartFrame); + + virtual OSStatus HandleResetAllControllers( UInt8 inChannel); + + virtual OSStatus HandleAllNotesOff( UInt8 inChannel); + + virtual OSStatus HandleAllSoundOff( UInt8 inChannel); + + SynthNote* GetNote(UInt32 inIndex) + { + if (!mNotes) + throw std::runtime_error("no notes"); + return (SynthNote*)((char*)mNotes + inIndex * mNoteSize); + } + + SynthNote* GetAFreeNote(UInt32 inFrame); + void AddFreeNote(SynthNote* inNote); + + friend class SynthGroupElement; +protected: + + UInt32 NextNoteID() { return OSAtomicIncrement32((int32_t *)&mNoteIDCounter); } + + + // call SetNotes in your Initialize() method to give the base class your note structures and to set the maximum + // number of active notes. inNoteData should be an array of size inMaxActiveNotes. + void SetNotes(UInt32 inNumNotes, UInt32 inMaxActiveNotes, SynthNote* inNotes, UInt32 inNoteSize); + + void PerformEvents( const AudioTimeStamp & inTimeStamp); + OSStatus SendPedalEvent(MusicDeviceGroupID inGroupID, UInt32 inEventType, UInt32 inOffsetSampleFrame); + virtual SynthNote* VoiceStealing(UInt32 inFrame, bool inKillIt); + UInt32 MaxActiveNotes() const { return mMaxActiveNotes; } + UInt32 NumActiveNotes() const { return mNumActiveNotes; } + void IncNumActiveNotes() { ++mNumActiveNotes; } + void DecNumActiveNotes() { --mNumActiveNotes; } + UInt32 CountActiveNotes(); + + SynthPartElement * GetPartElement (AudioUnitElement inPartElement); + + // this call throws if there's no assigned element for the group ID + virtual SynthGroupElement * GetElForGroupID (MusicDeviceGroupID inGroupID); + virtual SynthGroupElement * GetElForNoteID (NoteInstanceID inNoteID); + + SInt64 mAbsoluteSampleFrame; + + +private: + + SInt32 mNoteIDCounter; + + SynthEventQueue mEventQueue; + + UInt32 mNumNotes; + UInt32 mNumActiveNotes; + UInt32 mMaxActiveNotes; + SynthNote* mNotes; + SynthNoteList mFreeNotes; + UInt32 mNoteSize; + + AUScope mPartScope; + const UInt32 mInitNumPartEls; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class AUMonotimbralInstrumentBase : public AUInstrumentBase +{ +public: + AUMonotimbralInstrumentBase( + AudioComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups = 16, + UInt32 numParts = 1); + + virtual OSStatus RealTimeStartNote( SynthGroupElement *inGroup, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams); +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// this is a work in progress! The mono-timbral one is finished though! +class AUMultitimbralInstrumentBase : public AUInstrumentBase +{ +public: + AUMultitimbralInstrumentBase( + AudioComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups, + UInt32 numParts); + + virtual OSStatus GetPropertyInfo( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & 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); + +private: + +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/LockFreeFIFO.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/LockFreeFIFO.h new file mode 100644 index 0000000000..ea6c4c26e1 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/LockFreeFIFO.h @@ -0,0 +1,168 @@ +/* + File: LockFreeFIFO.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include <libkern/OSAtomic.h> + +template <class ITEM> +class LockFreeFIFOWithFree +{ + LockFreeFIFOWithFree(); // private, unimplemented. +public: + LockFreeFIFOWithFree(UInt32 inMaxSize) + : mReadIndex(0), mWriteIndex(0), mFreeIndex(0) + { + //assert(IsPowerOfTwo(inMaxSize)); + mItems = new ITEM[inMaxSize]; + mMask = inMaxSize - 1; + } + + ~LockFreeFIFOWithFree() + { + delete [] mItems; + } + + + void Reset() + { + FreeItems(); + mReadIndex = 0; + mWriteIndex = 0; + mFreeIndex = 0; + } + + ITEM* WriteItem() + { + //printf("WriteItem %d %d\n", mReadIndex, mWriteIndex); + FreeItems(); // free items on the write thread. + int32_t nextWriteIndex = (mWriteIndex + 1) & mMask; + if (nextWriteIndex == mFreeIndex) return NULL; + return &mItems[mWriteIndex]; + } + + ITEM* ReadItem() + { + //printf("ReadItem %d %d\n", mReadIndex, mWriteIndex); + if (mReadIndex == mWriteIndex) return NULL; + return &mItems[mReadIndex]; + } + void AdvanceWritePtr() { OSAtomicCompareAndSwap32(mWriteIndex, (mWriteIndex + 1) & mMask, &mWriteIndex); } + void AdvanceReadPtr() { OSAtomicCompareAndSwap32(mReadIndex, (mReadIndex + 1) & mMask, &mReadIndex); } +private: + ITEM* FreeItem() + { + if (mFreeIndex == mReadIndex) return NULL; + return &mItems[mFreeIndex]; + } + void AdvanceFreePtr() { OSAtomicCompareAndSwap32(mFreeIndex, (mFreeIndex + 1) & mMask, &mFreeIndex); } + + void FreeItems() + { + ITEM* item; + while ((item = FreeItem()) != NULL) + { + item->Free(); + AdvanceFreePtr(); + } + } + + volatile int32_t mReadIndex, mWriteIndex, mFreeIndex; + int32_t mMask; + ITEM *mItems; +}; + + + +// Same as above but no free. + +template <class ITEM> +class LockFreeFIFO +{ + LockFreeFIFO(); // private, unimplemented. +public: + LockFreeFIFO(UInt32 inMaxSize) + : mReadIndex(0), mWriteIndex(0) + { + //assert(IsPowerOfTwo(inMaxSize)); + mItems = new ITEM[inMaxSize]; + mMask = inMaxSize - 1; + } + + ~LockFreeFIFO() + { + delete [] mItems; + } + + void Reset() + { + mReadIndex = 0; + mWriteIndex = 0; + } + + ITEM* WriteItem() + { + int32_t nextWriteIndex = (mWriteIndex + 1) & mMask; + if (nextWriteIndex == mReadIndex) return NULL; + return &mItems[mWriteIndex]; + } + + ITEM* ReadItem() + { + if (mReadIndex == mWriteIndex) return NULL; + return &mItems[mReadIndex]; + } + + // the CompareAndSwap will always succeed. We use CompareAndSwap because it calls the PowerPC sync instruction, + // plus any processor bug workarounds for various CPUs. + void AdvanceWritePtr() { OSAtomicCompareAndSwap32(mWriteIndex, (mWriteIndex + 1) & mMask, &mWriteIndex); } + void AdvanceReadPtr() { OSAtomicCompareAndSwap32(mReadIndex, (mReadIndex + 1) & mMask, &mReadIndex); } + +private: + + volatile int32_t mReadIndex, mWriteIndex; + int32_t mMask; + ITEM *mItems; +}; + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/MIDIControlHandler.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/MIDIControlHandler.h new file mode 100644 index 0000000000..0f8003fcd5 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/MIDIControlHandler.h @@ -0,0 +1,92 @@ +/* + File: MIDIControlHandler.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __MIDICONTROLHANDLER_H__ +#define __MIDICONTROLHANDLER_H__ + +#include <CoreAudio/CoreAudio.h> + +/*! Abstract interface base class for classes which handle all incoming MIDI data */ + +class MIDIControlHandler +{ +public: + virtual ~MIDIControlHandler() {} + virtual void Reset() = 0; //! Restore all state to defaults + virtual bool SetProgramChange(UInt16 inProgram) = 0; + virtual bool SetPitchWheel(UInt16 inValue) = 0; + virtual bool SetChannelPressure(UInt8 inValue) = 0; + virtual bool SetPolyPressure(UInt8 inKey, UInt8 inValue) = 0; + virtual bool SetController(UInt8 inControllerNumber, UInt8 inValue) = 0; + virtual bool SetSysex(void *inSysexMsg) = 0; + + virtual float GetPitchBend() const = 0; + + /*! Default controller values. These represent MSB values unless indicated in the name */ + + enum + { + kDefault_Midpoint = 0x40, //! Used for all center-null-point controllers + kDefault_Volume = 100, + kDefault_Pan = kDefault_Midpoint, + kDefault_ModWheel = 0, + kDefault_Pitch = kDefault_Midpoint, + kDefault_Expression = 0x7f, + kDefault_ChannelPressure = 0, + kDefault_ReverbSend = 40, + kDefault_ChorusSend = 0, + + kDefault_RPN_LSB = 0x7f, + kDefault_RPN_MSB = 0x7f, + kDefault_PitchBendRange = 2, + kDefault_FineTuning = kDefault_Midpoint, + kDefault_CoarseTuning = kDefault_Midpoint, + kDefault_ModDepthRange = 0, + kDefault_ModDepthRangeLSB = kDefault_Midpoint + }; +}; + +#endif // __MIDICONTROLHANDLER_H__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.cpp new file mode 100644 index 0000000000..fd329f84e8 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.cpp @@ -0,0 +1,419 @@ +/* + File: SynthElement.cpp + Abstract: SynthElement.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "SynthElement.h" +#include "AUInstrumentBase.h" +#include "AUMIDIDefs.h" + +#undef DEBUG_PRINT +#define DEBUG_PRINT 0 +#define DEBUG_PRINT_NOTE 0 +#define DEBUG_PRINT_RENDER 0 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +MidiControls::MidiControls() +{ + Reset(); +} + +void MidiControls::Reset() +{ + memset(mControls, 0, sizeof(mControls)); + memset(mPolyPressure, 0, sizeof(mPolyPressure)); + mMonoPressure = 0; + mProgramChange = 0; + mPitchBend = 0; + mActiveRPN = 0; + mActiveNRPN = 0; + mActiveRPValue = 0; + mActiveNRPValue = 0; + mControls[kMidiController_Pan] = 64; + mControls[kMidiController_Expression] = 127; + mPitchBendDepth = 24 << 7; + mFPitchBendDepth = 24.0f; + mFPitchBend = 0.0f; +} + + +SynthElement::SynthElement(AUInstrumentBase *audioUnit, UInt32 inElement) + : AUElement(audioUnit), mIndex(inElement) +{ +} + +SynthElement::~SynthElement() +{ +} + +SynthGroupElement::SynthGroupElement(AUInstrumentBase *audioUnit, UInt32 inElement, MIDIControlHandler *inHandler) + : SynthElement(audioUnit, inElement), + mCurrentAbsoluteFrame(-1), + mMidiControlHandler(inHandler), + mSustainIsOn(false), mSostenutoIsOn(false), mOutputBus(0), mGroupID(kUnassignedGroup) +{ + for (UInt32 i=0; i<kNumberOfSoundingNoteStates; ++i) + mNoteList[i].mState = (SynthNoteState) i; +} + +SynthGroupElement::~SynthGroupElement() +{ + delete mMidiControlHandler; +} + +void SynthGroupElement::SetGroupID (MusicDeviceGroupID inGroup) +{ + // can't re-assign a group once its been assigned + if (mGroupID != kUnassignedGroup) throw static_cast<OSStatus>(kAudioUnitErr_InvalidElement); + mGroupID = inGroup; +} + +void SynthGroupElement::Reset() +{ +#if DEBUG_PRINT + printf("SynthGroupElement::Reset\n"); +#endif + mMidiControlHandler->Reset(); + for (UInt32 i=0; i<kNumberOfSoundingNoteStates; ++i) + mNoteList[i].Empty(); +} + +SynthPartElement::SynthPartElement(AUInstrumentBase *audioUnit, UInt32 inElement) + : SynthElement(audioUnit, inElement) +{ +} + +// Return the SynthNote with the given inNoteID, if found. If unreleasedOnly is true, only look for +// attacked and sostenutoed notes, otherwise search all states. Return state of found note via outNoteState. + +SynthNote *SynthGroupElement::GetNote(NoteInstanceID inNoteID, bool unreleasedOnly, UInt32 *outNoteState) +{ +#if DEBUG_PRINT_RENDER + printf("SynthGroupElement::GetNote %d, unreleased = %d\n", inNoteID, unreleasedOnly); +#endif + const UInt32 lastNoteState = unreleasedOnly ? + (mSostenutoIsOn ? kNoteState_Sostenutoed : kNoteState_Attacked) + : kNoteState_Released; + SynthNote *note = NULL; + // Search for notes in each successive state + for (UInt32 noteState = kNoteState_Attacked; noteState <= lastNoteState; ++noteState) + { + if (outNoteState) *outNoteState = noteState; // even if we find nothing + note = mNoteList[noteState].mHead; + while (note && note->mNoteID != inNoteID) + { +#if DEBUG_PRINT_RENDER + printf(" checking %p id: %d\n", note, note->mNoteID); +#endif + note = note->mNext; + } + if (note) + { +#if DEBUG_PRINT_RENDER + printf(" found %p\n", note); +#endif + break; + } + } + return note; +} + +void SynthGroupElement::NoteOn(SynthNote *note, + SynthPartElement *part, + NoteInstanceID inNoteID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams) +{ +#if DEBUG_PRINT_NOTE + printf("SynthGroupElement::NoteOn %d\n", inNoteID); +#endif + // TODO: CONSIDER FIXING this to not need to initialize mCurrentAbsoluteFrame to -1. + UInt64 absoluteFrame = (mCurrentAbsoluteFrame == -1) ? inOffsetSampleFrame : mCurrentAbsoluteFrame + inOffsetSampleFrame; + if (note->AttackNote(part, this, inNoteID, absoluteFrame, inOffsetSampleFrame, inParams)) { + mNoteList[kNoteState_Attacked].AddNote(note); + } +} + +void SynthGroupElement::NoteOff(NoteInstanceID inNoteID, UInt32 inFrame) +{ +#if DEBUG_PRINT_NOTE + printf("SynthGroupElement::NoteOff %d\n", inNoteID); +#endif + UInt32 noteState = kNoteState_Attacked; + SynthNote *note = GetNote(inNoteID, true, ¬eState); // asking for unreleased only + if (note) + { +#if DEBUG_PRINT_NOTE + printf(" old note state: %d\n", note->mState); +#endif + if (noteState == kNoteState_Attacked) + { + mNoteList[noteState].RemoveNote(note); + if (mSustainIsOn) { + mNoteList[kNoteState_ReleasedButSustained].AddNote(note); + } else { + note->Release(inFrame); + mNoteList[kNoteState_Released].AddNote(note); + } +#if DEBUG_PRINT_NOTE + printf(" new note state: %d\n", note->mState); +#endif + } + else /* if (noteState == kNoteState_Sostenutoed) */ + { + mNoteList[kNoteState_Sostenutoed].RemoveNote(note); + mNoteList[kNoteState_ReleasedButSostenutoed].AddNote(note); + } + } +} + +void SynthGroupElement::NoteEnded(SynthNote *inNote, UInt32 inFrame) +{ +#if DEBUG_PRINT_NOTE + printf("SynthGroupElement::NoteEnded: id %d state %d\n", inNote->mNoteID, inNote->mState); +#endif + if (inNote->IsSounding()) { + SynthNoteList *list = &mNoteList[inNote->GetState()]; + list->RemoveNote(inNote); + } + + GetAUInstrument()->AddFreeNote(inNote); +} + +void SynthGroupElement::NoteFastReleased(SynthNote *inNote) +{ +#if DEBUG_PRINT_NOTE + printf("SynthGroupElement::NoteFastReleased id %d state %d\n", inNote->mNoteID, inNote->mState); +#endif + if (inNote->IsActive()) { + mNoteList[inNote->GetState()].RemoveNote(inNote); + GetAUInstrument()->DecNumActiveNotes(); + mNoteList[kNoteState_FastReleased].AddNote(inNote); + } + else { + Assert(true, "ASSERT FAILED: Attempting to fast-release non-active note"); + } +} + +bool SynthGroupElement::ChannelMessage(UInt16 controllerID, UInt16 inValue) +{ + bool handled = true; +#if DEBUG_PRINT + printf("SynthGroupElement::ChannelMessage(0x%x, %u)\n", controllerID, inValue); +#endif + // Sustain and sostenuto are "pedal events", and are handled during render cycle + if (controllerID <= kMidiController_RPN_MSB && controllerID != kMidiController_Sustain && controllerID != kMidiController_Sostenuto) + handled = mMidiControlHandler->SetController(controllerID, UInt8(inValue)); + else + { + switch (controllerID) + { + case kMidiMessage_ProgramChange: + handled = mMidiControlHandler->SetProgramChange(inValue); + break; + case kMidiMessage_PitchWheel: + handled = mMidiControlHandler->SetPitchWheel(inValue); + break; + case kMidiMessage_ChannelPressure: +#if DEBUG_PRINT + printf("SynthGroupElement::ChannelMessage: Channel Pressure %u\n", inValue); +#endif + handled = mMidiControlHandler->SetChannelPressure(UInt8(inValue)); + break; + case kMidiMessage_PolyPressure: + { UInt8 inKey = inValue >> 7; + UInt8 val = inValue & 0x7f; + handled = mMidiControlHandler->SetPolyPressure(inKey, val); + break; + } + default: + handled = false; + break; + } + } + return handled; +} + +void SynthGroupElement::SostenutoOn(UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::SostenutoOn\n"); +#endif + if (!mSostenutoIsOn) { + mMidiControlHandler->SetController(kMidiController_Sostenuto, 127); + mSostenutoIsOn = true; + mNoteList[kNoteState_Sostenutoed].TransferAllFrom(&mNoteList[kNoteState_Attacked], inFrame); + } +} + +void SynthGroupElement::SostenutoOff(UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::SostenutoOff\n"); +#endif + if (mSostenutoIsOn) { + mMidiControlHandler->SetController(kMidiController_Sostenuto, 0); + mSostenutoIsOn = false; + mNoteList[kNoteState_Attacked].TransferAllFrom(&mNoteList[kNoteState_Sostenutoed], inFrame); + if (mSustainIsOn) + mNoteList[kNoteState_ReleasedButSustained].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSostenutoed], inFrame); + else + mNoteList[kNoteState_Released].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSostenutoed], inFrame); + } +} + + +void SynthGroupElement::SustainOn(UInt32 inFrame) +{ +#if DEBUG_PRINT +// printf("SynthGroupElement::SustainOn\n"); +#endif + if (!mSustainIsOn) { + mMidiControlHandler->SetController(kMidiController_Sustain, 127); + mSustainIsOn = true; + } +} + +void SynthGroupElement::SustainOff(UInt32 inFrame) +{ +#if DEBUG_PRINT +// printf("SynthGroupElement::SustainOff\n"); +#endif + if (mSustainIsOn) { + mMidiControlHandler->SetController(kMidiController_Sustain, 0); + mSustainIsOn = false; + + mNoteList[kNoteState_Released].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSustained], inFrame); + } +} + +void SynthGroupElement::AllNotesOff(UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::AllNotesOff\n"); +#endif + SynthNote *note; + for (UInt32 i=0 ; i<=kNoteState_Sostenutoed; ++i) + { + UInt32 newState = (i == kNoteState_Attacked) ? + kNoteState_Released : kNoteState_ReleasedButSostenutoed; + note = mNoteList[i].mHead; + while (note) + { + SynthNote *nextNote = note->mNext; + + mNoteList[i].RemoveNote(note); + note->Release(inFrame); + mNoteList[newState].AddNote(note); + + note = nextNote; + } + } +} + +void SynthGroupElement::AllSoundOff(UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::AllSoundOff\n"); +#endif + SynthNote *note; + + for (UInt32 i=0 ; i<kNumberOfActiveNoteStates; ++i) + { + note = mNoteList[i].mHead; + while (note) + { + SynthNote *nextNote = note->mNext; + + mNoteList[i].RemoveNote(note); + note->FastRelease(inFrame); + mNoteList[kNoteState_FastReleased].AddNote(note); + GetAUInstrument()->DecNumActiveNotes(); + note = nextNote; + } + } +} + +void SynthGroupElement::ResetAllControllers(UInt32 inFrame) +{ +#if DEBUG_PRINT + printf("SynthGroupElement::ResetAllControllers\n"); +#endif + mMidiControlHandler->Reset(); +} + +OSStatus SynthGroupElement::Render(SInt64 inAbsoluteSampleFrame, UInt32 inNumberFrames, AUScope &outputs) +{ + // Avoid duplicate calls at same sample offset + if (inAbsoluteSampleFrame != mCurrentAbsoluteFrame) + { + mCurrentAbsoluteFrame = inAbsoluteSampleFrame; + AudioBufferList* buffArray[16]; + UInt32 numOutputs = outputs.GetNumberOfElements(); + for (UInt32 outBus = 0; outBus < numOutputs && outBus < 16; ++outBus) + { + buffArray[outBus] = &GetAudioUnit()->GetOutput(outBus)->GetBufferList(); + } + + for (UInt32 i=0 ; i<kNumberOfSoundingNoteStates; ++i) + { + SynthNote *note = mNoteList[i].mHead; + while (note) + { +#if DEBUG_PRINT_RENDER + printf("SynthGroupElement::Render: state %d, note %p\n", i, note); +#endif + SynthNote *nextNote = note->mNext; + + OSStatus err = note->Render(inAbsoluteSampleFrame, inNumberFrames, buffArray, numOutputs); + if (err) return err; + + note = nextNote; + } + } + } + return noErr; +} + + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.h new file mode 100644 index 0000000000..4ca3643106 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthElement.h @@ -0,0 +1,227 @@ +/* + File: SynthElement.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __SynthElement__ +#define __SynthElement__ + +#include <AudioUnit/AudioUnit.h> +#include "MusicDeviceBase.h" +#include "SynthNoteList.h" +#include "MIDIControlHandler.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +class AUInstrumentBase; + +class SynthElement : public AUElement +{ +public: + SynthElement(AUInstrumentBase *audioUnit, UInt32 inElement); + virtual ~SynthElement(); + + UInt32 GetIndex() const { return mIndex; } + + AUInstrumentBase* GetAUInstrument() { return (AUInstrumentBase*)GetAudioUnit(); } + +private: + UInt32 mIndex; +}; + +class MidiControls : public MIDIControlHandler +{ + enum { kMaxControls = 128 }; +public: + MidiControls(); + virtual ~MidiControls() {} + virtual void Reset(); + virtual bool SetProgramChange(UInt16 inProgram) { mProgramChange = inProgram; return true; } + virtual bool SetPitchWheel(UInt16 inValue) { + mPitchBend = inValue; + mFPitchBend = (float)(((SInt16)mPitchBend - 8192) / 8192.); + return true; + } + virtual bool SetChannelPressure(UInt8 inValue) { mMonoPressure = inValue; return true; } + virtual bool SetPolyPressure(UInt8 inKey, UInt8 inValue) { + mPolyPressure[inKey] = inValue; + return true; + } + virtual bool SetController(UInt8 inControllerNumber, UInt8 inValue) { + if (inControllerNumber < kMaxControls) { + mControls[inControllerNumber] = inValue; + return true; + } + return false; + } + virtual bool SetSysex(void *inSysexMsg) { return false; } + + virtual float GetPitchBend() const { return mFPitchBend * mFPitchBendDepth; } + + SInt16 GetHiResControl(UInt32 inIndex) const + { + return ((mControls[inIndex] & 127) << 7) | (mControls[inIndex + 32] & 127); + } + + float GetControl(UInt32 inIndex) const + { + if (inIndex < 32) { + return (float)(mControls[inIndex] + (mControls[inIndex + 32] / 127.)); + } else { + return (float)mControls[inIndex]; + } + } + + +private: + + UInt8 mControls[128]; + UInt8 mPolyPressure[128]; + UInt8 mMonoPressure; + UInt8 mProgramChange; + UInt16 mPitchBend; + UInt16 mActiveRPN; + UInt16 mActiveNRPN; + UInt16 mActiveRPValue; + UInt16 mActiveNRPValue; + + UInt16 mPitchBendDepth; + float mFPitchBendDepth; + float mFPitchBend; + + void SetHiResControl(UInt32 inIndex, UInt8 inMSB, UInt8 inLSB) + { + mControls[inIndex] = inMSB; + mControls[inIndex + 32] = inLSB; + } + +}; + + +class SynthGroupElement : public SynthElement +{ +public: + enum { + kUnassignedGroup = 0xFFFFFFFF + }; + + SynthGroupElement(AUInstrumentBase *audioUnit, UInt32 inElement, MIDIControlHandler *inHandler); + virtual ~SynthGroupElement(); + + virtual void NoteOn(SynthNote *note, SynthPartElement *part, NoteInstanceID inNoteID, UInt32 inOffsetSampleFrame, const MusicDeviceNoteParams &inParams); + virtual void NoteOff(NoteInstanceID inNoteID, UInt32 inOffsetSampleFrame); + void SustainOn(UInt32 inFrame); + void SustainOff(UInt32 inFrame); + void SostenutoOn(UInt32 inFrame); + void SostenutoOff(UInt32 inFrame); + + void NoteEnded(SynthNote *inNote, UInt32 inFrame); + void NoteFastReleased(SynthNote *inNote); + + virtual bool ChannelMessage(UInt16 controlID, UInt16 controlValue); + virtual void AllNotesOff(UInt32 inFrame); + virtual void AllSoundOff(UInt32 inFrame); + void ResetAllControllers(UInt32 inFrame); + + SynthNote * GetNote(NoteInstanceID inNoteID, bool unreleasedOnly=false, UInt32 *outNoteState=NULL); + + void Reset(); + + virtual OSStatus Render(SInt64 inAbsoluteSampleFrame, UInt32 inNumberFrames, AUScope &outputs); + + float GetPitchBend() const { return mMidiControlHandler->GetPitchBend(); } + SInt64 GetCurrentAbsoluteFrame() const { return mCurrentAbsoluteFrame; } + + MusicDeviceGroupID GroupID () const { return mGroupID; } + virtual void SetGroupID (MusicDeviceGroupID inGroup); + + MIDIControlHandler * GetMIDIControlHandler() const { return mMidiControlHandler; } + +protected: + SInt64 mCurrentAbsoluteFrame; + SynthNoteList mNoteList[kNumberOfSoundingNoteStates]; + MIDIControlHandler *mMidiControlHandler; + +private: + friend class AUInstrumentBase; + friend class AUMonotimbralInstrumentBase; + friend class AUMultitimbralInstrumentBase; + + bool mSustainIsOn; + bool mSostenutoIsOn; + UInt32 mOutputBus; + MusicDeviceGroupID mGroupID; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct SynthKeyZone +{ + UInt8 mLoNote; + UInt8 mHiNote; + UInt8 mLoVelocity; + UInt8 mHiVelocity; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const UInt32 kUnlimitedPolyphony = 0xFFFFFFFF; + +class SynthPartElement : public SynthElement +{ +public: + SynthPartElement(AUInstrumentBase *audioUnit, UInt32 inElement); + + UInt32 GetGroupIndex() const { return mGroupIndex; } + bool InRange(Float32 inNote, Float32 inVelocity); + + UInt32 GetMaxPolyphony() const { return mMaxPolyphony; } + void SetMaxPolyphony(UInt32 inMaxPolyphony) { mMaxPolyphony = inMaxPolyphony; } + +private: + UInt32 mGroupIndex; + UInt32 mPatchIndex; + UInt32 mMaxPolyphony; + SynthKeyZone mKeyZone; +}; + +#endif diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthEvent.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthEvent.h new file mode 100644 index 0000000000..9c27aee04f --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthEvent.h @@ -0,0 +1,145 @@ +/* + File: SynthEvent.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +/* You can either fill in code here or remove this and create or add new files. */ + +#ifndef __SynthEvent__ +#define __SynthEvent__ + +#include <AudioUnit/AudioUnit.h> +#include <CoreAudio/CoreAudio.h> +#include "MusicDeviceBase.h" +#include <stdexcept> + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +class SynthEvent +{ +public: + enum { + kEventType_NoteOn = 1, + kEventType_NoteOff = 2, + kEventType_SustainOn = 3, + kEventType_SustainOff = 4, + kEventType_SostenutoOn = 5, + kEventType_SostenutoOff = 6, + kEventType_AllNotesOff = 7, + kEventType_AllSoundOff = 8, + kEventType_ResetAllControllers = 9 + }; + + + SynthEvent() {} + ~SynthEvent() {} + + void Set( + UInt32 inEventType, + MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams* inNoteParams + ) + { + mEventType = inEventType; + mGroupID = inGroupID; + mNoteID = inNoteID; + mOffsetSampleFrame = inOffsetSampleFrame; + + if (inNoteParams) + { + UInt32 paramSize = offsetof(MusicDeviceNoteParams, mControls) + (inNoteParams->argCount-2)*sizeof(NoteParamsControlValue); + mNoteParams = inNoteParams->argCount > 3 + ? (MusicDeviceNoteParams*)malloc(paramSize) + : &mSmallNoteParams; + memcpy(mNoteParams, inNoteParams, paramSize); + } + else + mNoteParams = NULL; + } + + + void Free() + { + if (mNoteParams) + { + if (mNoteParams->argCount > 3) + free(mNoteParams); + mNoteParams = NULL; + } + } + + UInt32 GetEventType() const { return mEventType; } + MusicDeviceGroupID GetGroupID() const { return mGroupID; } + NoteInstanceID GetNoteID() const { return mNoteID; } + UInt32 GetOffsetSampleFrame() const { return mOffsetSampleFrame; } + + MusicDeviceNoteParams* GetParams() const { return mNoteParams; } + + UInt32 GetArgCount() const { return mNoteParams->argCount; } + UInt32 NumberParameters() const { return mNoteParams->argCount - 2; } + + Float32 GetNote() const { return mNoteParams->mPitch; } + Float32 GetVelocity() const { return mNoteParams->mVelocity; } + + NoteParamsControlValue GetParameter(UInt32 inIndex) const + { + if (inIndex >= NumberParameters()) + throw std::runtime_error("index out of range"); + return mNoteParams->mControls[inIndex]; + } + +private: + UInt32 mEventType; + MusicDeviceGroupID mGroupID; + NoteInstanceID mNoteID; + UInt32 mOffsetSampleFrame; + MusicDeviceNoteParams* mNoteParams; + MusicDeviceNoteParams mSmallNoteParams; // inline a small one to eliminate malloc for the simple case. +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.cpp new file mode 100644 index 0000000000..96a24d33c2 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.cpp @@ -0,0 +1,140 @@ +/* + File: SynthNote.cpp + Abstract: SynthNote.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "SynthNote.h" +#include "SynthElement.h" +#include "AUInstrumentBase.h" + +bool SynthNote::AttackNote( + SynthPartElement * inPart, + SynthGroupElement * inGroup, + NoteInstanceID inNoteID, + UInt64 inAbsoluteSampleFrame, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams) +{ +#if DEBUG_PRINT + printf("SynthNote::AttackNote %lu %lu abs frame %llu rel frame %lu\n", (UInt32)inGroup->GroupID(), (UInt32)inNoteID, inAbsoluteSampleFrame, inOffsetSampleFrame); +#endif + mPart = inPart; + mGroup = inGroup; + mNoteID = inNoteID; + + mAbsoluteStartFrame = inAbsoluteSampleFrame; + mRelativeStartFrame = inOffsetSampleFrame; + mRelativeReleaseFrame = -1; + mRelativeKillFrame = -1; + + mPitch = inParams.mPitch; + mVelocity = inParams.mVelocity; + + + return Attack(inParams); +} + + +void SynthNote::Reset() +{ + mPart = 0; + mGroup = 0; + mAbsoluteStartFrame = 0; + mRelativeStartFrame = 0; + mRelativeReleaseFrame = 0; + mRelativeKillFrame = 0; +} + +void SynthNote::Kill(UInt32 inFrame) +{ + mRelativeKillFrame = inFrame; +} + +void SynthNote::Release(UInt32 inFrame) +{ + mRelativeReleaseFrame = inFrame; +} + +void SynthNote::FastRelease(UInt32 inFrame) +{ + mRelativeReleaseFrame = inFrame; +} + +double SynthNote::TuningA() const +{ + return 440.0; +} + +double SynthNote::Frequency() +{ + return TuningA() * pow(2., (mPitch - 69. + GetPitchBend()) / 12.); +} + +double SynthNote::SampleRate() +{ + return GetAudioUnit()->GetOutput(0)->GetStreamFormat().mSampleRate; +} + +AUInstrumentBase* SynthNote::GetAudioUnit() const +{ + return (AUInstrumentBase*)mGroup->GetAudioUnit(); +} + +Float32 SynthNote::GetGlobalParameter(AudioUnitParameterID inParamID) const +{ + return mGroup->GetAudioUnit()->Globals()->GetParameter(inParamID); +} + +void SynthNote::NoteEnded(UInt32 inFrame) +{ + mGroup->NoteEnded(this, inFrame); + mNoteID = 0xFFFFFFFF; +} + +float SynthNote::GetPitchBend() const +{ + return mGroup->GetPitchBend(); +} + + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.h new file mode 100644 index 0000000000..cec6377217 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNote.h @@ -0,0 +1,187 @@ +/* + File: SynthNote.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __SynthNote__ +#define __SynthNote__ + +#include <AudioUnit/AudioUnit.h> +#include <CoreAudio/CoreAudio.h> +#include "MusicDeviceBase.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +enum SynthNoteState { + kNoteState_Attacked = 0, + kNoteState_Sostenutoed = 1, + kNoteState_ReleasedButSostenutoed = 2, + kNoteState_ReleasedButSustained = 3, + kNoteState_Released = 4, + kNoteState_FastReleased = 5, + kNoteState_Free = 6, + kNumberOfActiveNoteStates = 5, + kNumberOfSoundingNoteStates = 6, + kNumberOfNoteStates = 7, + kNoteState_Unset = kNumberOfNoteStates +}; + +/* + This table describes the state transitions for SynthNotes + + EVENT CURRENT STATE NEW STATE + note on free attacked + note off attacked (and sustain on) released but sustained + note off attacked released + note off sostenutoed released but sostenutoed + sustain on -- no changes -- + sustain off released but sustained released + sostenuto on attacked sostenutoed + sostenuto off sostenutoed attacked + sostenuto off released but sostenutoed (and sustain on) released but sustained + sostenuto off released but sostenutoed released + end of note any state free + soft voice stealing any state fast released + hard voice stealing any state free + + soft voice stealing happens when there is a note on event and NumActiveNotes > MaxActiveNotes + hard voice stealing happens when there is a note on event and NumActiveNotes == NumNotes (no free notes) + voice stealing removes the quietest note in the highest numbered state that has sounding notes. +*/ + +class SynthGroupElement; +class SynthPartElement; +class AUInstrumentBase; + +struct SynthNote +{ + SynthNote() : + mPrev(0), mNext(0), mPart(0), mGroup(0), + mNoteID(0xffffffff), + mState(kNoteState_Unset), + mAbsoluteStartFrame(0), + mRelativeStartFrame(0), + mRelativeReleaseFrame(-1), + mRelativeKillFrame(-1), + mPitch(0.0f), + mVelocity(0.0f) + { + } + + virtual ~SynthNote() {} + + virtual void Reset(); + //! Returns true if active note resulted from this call, otherwise false + virtual bool AttackNote( + SynthPartElement * inPart, + SynthGroupElement * inGroup, + NoteInstanceID inNoteID, + UInt64 inAbsoluteSampleFrame, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams &inParams + ); + + virtual OSStatus Render(UInt64 inAbsoluteSampleFrame, UInt32 inNumFrames, AudioBufferList** inBufferList, UInt32 inOutBusCount) = 0; + //! Returns true if active note resulted from this call, otherwise false + virtual bool Attack(const MusicDeviceNoteParams &inParams) = 0; + virtual void Kill(UInt32 inFrame); // voice is being stolen. + virtual void Release(UInt32 inFrame); + virtual void FastRelease(UInt32 inFrame); + virtual Float32 Amplitude() = 0; // used for finding quietest note for voice stealing. + + virtual void NoteEnded(UInt32 inFrame); + + SynthGroupElement* GetGroup() const { return mGroup; } + SynthPartElement* GetPart() const { return mPart; } + + AUInstrumentBase* GetAudioUnit() const; + + Float32 GetGlobalParameter(AudioUnitParameterID inParamID) const; + + NoteInstanceID GetNoteID() const { return mNoteID; } + SynthNoteState GetState() const { return mState; } + UInt8 GetMidiKey() const { return (UInt8) mPitch; } + UInt8 GetMidiVelocity() const { return (UInt8) mVelocity; } + + Boolean IsSounding() const { return mState < kNumberOfSoundingNoteStates; } + Boolean IsActive() const { return mState < kNumberOfActiveNoteStates; } + UInt64 GetAbsoluteStartFrame() const { return mAbsoluteStartFrame; } + SInt32 GetRelativeStartFrame() const { return mRelativeStartFrame; } + SInt32 GetRelativeReleaseFrame() const { return mRelativeReleaseFrame; } + SInt32 GetRelativeKillFrame() const { return mRelativeKillFrame; } + + void ListRemove() { mPrev = mNext = 0; } // only use when lists will be reset. + + float GetPitchBend() const; + double TuningA() const; + + Float32 GetPitch() const { return mPitch; } // returns raw pitch from MusicDeviceNoteParams + virtual double Frequency(); // returns the frequency of note + pitch bend. + virtual double SampleRate(); + + // linked list pointers + SynthNote *mPrev; + SynthNote *mNext; + + friend class SynthGroupElement; + friend struct SynthNoteList; +protected: + void SetState(SynthNoteState inState) { mState = inState; } +private: + SynthPartElement* mPart; + SynthGroupElement* mGroup; + + NoteInstanceID mNoteID; + SynthNoteState mState; + UInt64 mAbsoluteStartFrame; + SInt32 mRelativeStartFrame; + SInt32 mRelativeReleaseFrame; + SInt32 mRelativeKillFrame; + + Float32 mPitch; + Float32 mVelocity; +}; + +#endif + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.cpp new file mode 100644 index 0000000000..849abd266b --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.cpp @@ -0,0 +1,93 @@ +/* + File: SynthNoteList.cpp + Abstract: SynthNoteList.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "SynthNoteList.h" +#include <stdexcept> + +void SynthNoteList::SanityCheck() const +{ + if (mState >= kNoteState_Unset) { + throw std::runtime_error("SanityCheck: mState is bad"); + } + + if (mHead == NULL) { + if (mTail != NULL) + throw std::runtime_error("SanityCheck: mHead is NULL but not mTail"); + return; + } + if (mTail == NULL) { + throw std::runtime_error("SanityCheck: mTail is NULL but not mHead"); + } + + if (mHead->mPrev) { + throw std::runtime_error("SanityCheck: mHead has a mPrev"); + } + if (mTail->mNext) { + throw std::runtime_error("SanityCheck: mTail has a mNext"); + } + + SynthNote *note = mHead; + while (note) + { + if (note->mState != mState) + throw std::runtime_error("SanityCheck: note in wrong state"); + if (note->mNext) { + if (note->mNext->mPrev != note) + throw std::runtime_error("SanityCheck: bad link 1"); + } else { + if (mTail != note) + throw std::runtime_error("SanityCheck: note->mNext is nil, but mTail != note"); + } + if (note->mPrev) { + if (note->mPrev->mNext != note) + throw std::runtime_error("SanityCheck: bad link 2"); + } else { + if (mHead != note) + throw std::runtime_error("SanityCheck: note->mPrev is nil, but mHead != note"); + } + note = note->mNext; + } +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.h new file mode 100644 index 0000000000..47a3593018 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUInstrumentBase/SynthNoteList.h @@ -0,0 +1,232 @@ +/* + File: SynthNoteList.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __SynthNoteList__ +#define __SynthNoteList__ + +#include "SynthNote.h" + +#if DEBUG +#ifndef DEBUG_PRINT + #define DEBUG_PRINT 0 +#endif + #define USE_SANITY_CHECK 0 +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct SynthNoteList +{ + SynthNoteList() : mState(kNoteState_Unset), mHead(0), mTail(0) {} + + bool NotEmpty() const { return mHead != NULL; } + bool IsEmpty() const { return mHead == NULL; } + void Empty() { +#if USE_SANITY_CHECK + SanityCheck(); +#endif + mHead = mTail = NULL; + } + + UInt32 Length() const { +#if USE_SANITY_CHECK + SanityCheck(); +#endif + UInt32 length = 0; + for (SynthNote* note = mHead; note; note = note->mNext) + length++; + return length; + }; + + void AddNote(SynthNote *inNote) + { +#if DEBUG_PRINT + printf("AddNote(inNote=%p) to state: %lu\n", inNote, mState); +#endif +#if USE_SANITY_CHECK + SanityCheck(); +#endif + inNote->SetState(mState); + inNote->mNext = mHead; + inNote->mPrev = NULL; + + if (mHead) { mHead->mPrev = inNote; mHead = inNote; } + else mHead = mTail = inNote; +#if USE_SANITY_CHECK + SanityCheck(); +#endif + } + + void RemoveNote(SynthNote *inNote) + { +#if DEBUG_PRINT + printf("RemoveNote(inNote=%p) from state: %lu\n", inNote, mState); +#endif +#if USE_SANITY_CHECK + SanityCheck(); +#endif + if (inNote->mPrev) inNote->mPrev->mNext = inNote->mNext; + else mHead = inNote->mNext; + + if (inNote->mNext) inNote->mNext->mPrev = inNote->mPrev; + else mTail = inNote->mPrev; + + inNote->mPrev = 0; + inNote->mNext = 0; +#if USE_SANITY_CHECK + SanityCheck(); +#endif + } + + void TransferAllFrom(SynthNoteList *inNoteList, UInt32 inFrame) + { +#if DEBUG_PRINT + printf("TransferAllFrom: from state %lu into state %lu\n", inNoteList->mState, mState); +#endif +#if USE_SANITY_CHECK + SanityCheck(); + inNoteList->SanityCheck(); +#endif + if (!inNoteList->mTail) return; + + if (mState == kNoteState_Released) + { + for (SynthNote* note = inNoteList->mHead; note; note = note->mNext) + { +#if DEBUG_PRINT + printf("TransferAllFrom: releasing note %p\n", note); +#endif + note->Release(inFrame); + note->SetState(mState); + } + } + else + { + for (SynthNote* note = inNoteList->mHead; note; note = note->mNext) + { + note->SetState(mState); + } + } + + inNoteList->mTail->mNext = mHead; + + if (mHead) mHead->mPrev = inNoteList->mTail; + else mTail = inNoteList->mTail; + + mHead = inNoteList->mHead; + + inNoteList->mHead = NULL; + inNoteList->mTail = NULL; +#if USE_SANITY_CHECK + SanityCheck(); + inNoteList->SanityCheck(); +#endif + } + + SynthNote* FindOldestNote() + { +#if DEBUG_PRINT + printf("FindOldestNote\n"); +#endif +#if USE_SANITY_CHECK + SanityCheck(); +#endif + UInt64 minStartFrame = -1; + SynthNote* oldestNote = NULL; + for (SynthNote* note = mHead; note; note = note->mNext) + { + if (note->mAbsoluteStartFrame < minStartFrame) + { + oldestNote = note; + minStartFrame = note->mAbsoluteStartFrame; + } + } + return oldestNote; + } + + SynthNote* FindMostQuietNote() + { +#if DEBUG_PRINT + printf("FindMostQuietNote\n"); +#endif + Float32 minAmplitude = 1e9f; + UInt64 minStartFrame = -1; + SynthNote* mostQuietNote = NULL; + for (SynthNote* note = mHead; note; note = note->mNext) + { + Float32 amp = note->Amplitude(); +#if DEBUG_PRINT + printf(" amp %g minAmplitude %g\n", amp, minAmplitude); +#endif + if (amp < minAmplitude) + { + mostQuietNote = note; + minAmplitude = amp; + minStartFrame = note->mAbsoluteStartFrame; + } + else if (amp == minAmplitude && note->mAbsoluteStartFrame < minStartFrame) + { + // use earliest start time as a tie breaker + mostQuietNote = note; + minStartFrame = note->mAbsoluteStartFrame; + } + } +#if USE_SANITY_CHECK + SanityCheck(); +#endif + return mostQuietNote; + } + + void SanityCheck() const; + + SynthNoteState mState; + SynthNote * mHead; + SynthNote * mTail; +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUViewBase/AUViewLocalizedStringKeys.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUViewBase/AUViewLocalizedStringKeys.h new file mode 100644 index 0000000000..741be00fe0 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/AUViewBase/AUViewLocalizedStringKeys.h @@ -0,0 +1,88 @@ +/* + File: AUViewLocalizedStringKeys.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUViewLocalizedStringKeys_h__ +#define __AUViewLocalizedStringKeys_h__ + +// ACCESS POINT: +#define kLocalizedStringBundle_AUView CFSTR("com.apple.audio.units.Components") +#define kLocalizedStringTable_AUView CFSTR("CustomUI") + +// UNLOCALIZED STRINGS: + #define kAUViewUnlocalizedString_TitleSeparator CFSTR(": ") + +// Generic View: + #define kAUViewLocalizedStringKey_AudioUnit CFSTR("Audio Unit") + #define kAUViewLocalizedStringKey_Manufacturer CFSTR("Manufacturer") + + #define kAUViewLocalizedStringKey_FactoryPreset CFSTR("Factory Preset") + + #define kAUViewLocalizedStringKey_Properties CFSTR("Properties") + #define kAUViewLocalizedStringKey_Parameters CFSTR("Parameters") + + #define kAUViewLocalizedStringKey_Standard CFSTR("Standard") + #define kAUViewLocalizedStringKey_Expert CFSTR("Expert") + +// AULoadCPU: + #define kAUViewLocalizedStringKey_RestrictCPULoad CFSTR("Restrict CPU Load") + #define kAUViewLocalizedStringKey_PercentSymbol CFSTR("%") + #define kAUViewLocalizedStringKey_NotApplicable CFSTR("n/a") + +// AUDiskStreamingCheckbox: + #define kAUViewLocalizedStringKey_StreamFromDisk CFSTR("Stream From Disk") + +// AURenderQualityPopup: + #define kAUViewLocalizedStringKey_RenderQuality CFSTR("Render Quality") + #define kAUViewLocalizedStringKey_Maximum CFSTR("Maximum") + #define kAUViewLocalizedStringKey_High CFSTR("High") + #define kAUViewLocalizedStringKey_Medium CFSTR("Medium") + #define kAUViewLocalizedStringKey_Low CFSTR("Low") + #define kAUViewLocalizedStringKey_Minimum CFSTR("Minimum") + +// AUChannelLayoutPopUp: + #define kAUViewLocalizedStringKey_AudioChannelLayout CFSTR("Audio Channel Layout") + +#endif //__AUViewLocalizedStringKeys_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUEffectBase.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUEffectBase.cpp new file mode 100644 index 0000000000..010082fc3f --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUEffectBase.cpp @@ -0,0 +1,466 @@ +/* + File: AUEffectBase.cpp + Abstract: AUEffectBase.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUEffectBase.h" + +/* + 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 + + This will be fixed in a future update... +*/ + +//_____________________________________________________________________________ +// +AUEffectBase::AUEffectBase( AudioComponentInstance audioUnit, + bool inProcessesInPlace ) : + AUBase(audioUnit, 1, 1), // 1 in bus, 1 out bus + mBypassEffect(false), + mParamSRDep (false), + mProcessesInPlace(inProcessesInPlace), + mMainOutput(NULL), mMainInput(NULL) +#if TARGET_OS_IPHONE + , mOnlyOneKernel(false) +#endif +{ +} + +//_____________________________________________________________________________ +// +AUEffectBase::~AUEffectBase() +{ + Cleanup(); +} + +//_____________________________________________________________________________ +// +void AUEffectBase::Cleanup() +{ + for (KernelList::iterator it = mKernelList.begin(); it != mKernelList.end(); ++it) + delete *it; + + mKernelList.clear(); + mMainOutput = NULL; + mMainInput = NULL; +} + + +//_____________________________________________________________________________ +// +OSStatus AUEffectBase::Initialize() +{ + // get our current numChannels for input and output + SInt16 auNumInputs = (SInt16) GetInput(0)->GetStreamFormat().mChannelsPerFrame; + SInt16 auNumOutputs = (SInt16) GetOutput(0)->GetStreamFormat().mChannelsPerFrame; + + // does the unit publish specific information about channel configurations? + const AUChannelInfo *auChannelConfigs = NULL; + UInt32 numIOconfigs = SupportedNumChannels(&auChannelConfigs); + + if ((numIOconfigs > 0) && (auChannelConfigs != NULL)) + { + bool foundMatch = false; + for (UInt32 i = 0; (i < numIOconfigs) && !foundMatch; ++i) + { + SInt16 configNumInputs = auChannelConfigs[i].inChannels; + SInt16 configNumOutputs = auChannelConfigs[i].outChannels; + if ((configNumInputs < 0) && (configNumOutputs < 0)) + { + // unit accepts any number of channels on input and output + if (((configNumInputs == -1) && (configNumOutputs == -2)) + || ((configNumInputs == -2) && (configNumOutputs == -1))) + { + 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 + bool inputMatch = (auNumInputs == configNumInputs) || (configNumInputs == -1); + 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 = GetOutput(0); + mMainInput = GetInput(0); + + const CAStreamBasicDescription& format = GetStreamFormat(kAudioUnitScope_Output, 0); + format.IdentifyCommonPCMFormat(mCommonPCMFormat, NULL); + mBytesPerFrame = format.mBytesPerFrame; + + return noErr; +} + +OSStatus AUEffectBase::Reset( AudioUnitScope inScope, + AudioUnitElement inElement) +{ + for (KernelList::iterator it = mKernelList.begin(); it != mKernelList.end(); ++it) { + AUKernelBase *kernel = *it; + if (kernel != NULL) + kernel->Reset(); + } + + return AUBase::Reset(inScope, inElement); +} + +OSStatus AUEffectBase::GetPropertyInfo (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable) +{ + if (inScope == kAudioUnitScope_Global) { + switch (inID) { + case kAudioUnitProperty_BypassEffect: + outWritable = true; + outDataSize = sizeof (UInt32); + return noErr; + case kAudioUnitProperty_InPlaceProcessing: + outWritable = true; + outDataSize = sizeof (UInt32); + return noErr; + } + } + 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: + *((UInt32*)outData) = (IsBypassEffect() ? 1 : 0); + return noErr; + case kAudioUnitProperty_InPlaceProcessing: + *((UInt32*)outData) = (mProcessesInPlace ? 1 : 0); + return noErr; + } + } + 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; + + bool tempNewSetting = *((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(0, 0); + SetBypassEffect (tempNewSetting); + } + return noErr; + } + case kAudioUnitProperty_InPlaceProcessing: + mProcessesInPlace = (*((UInt32*)inData) != 0); + return noErr; + } + } + return AUBase::SetProperty (inID, inScope, inElement, inData, inDataSize); +} + + +void AUEffectBase::MaintainKernels() +{ +#if TARGET_OS_IPHONE + UInt32 nKernels = mOnlyOneKernel ? 1 : GetNumberOfChannels(); +#else + UInt32 nKernels = GetNumberOfChannels(); +#endif + + if (mKernelList.size() < nKernels) { + mKernelList.reserve(nKernels); + for (UInt32 i = (UInt32)mKernelList.size(); i < nKernels; ++i) + mKernelList.push_back(NewKernel()); + } else { + while (mKernelList.size() > nKernels) { + AUKernelBase *kernel = mKernelList.back(); + delete kernel; + mKernelList.pop_back(); + } + } + + for(unsigned int i = 0; i < nKernels; i++ ) + { + if(mKernelList[i]) { + mKernelList[i]->SetChannelNum (i); + } + } +} + +bool AUEffectBase::StreamFormatWritable( AudioUnitScope scope, + AudioUnitElement element) +{ + return IsInitialized() ? false : true; +} + +OSStatus AUEffectBase::ChangeStreamFormat( AudioUnitScope inScope, + AudioUnitElement inElement, + const CAStreamBasicDescription & inPrevFormat, + const CAStreamBasicDescription & inNewFormat) +{ + 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() && fnotequal(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 ) +{ + ScheduledProcessParams &sliceParams = *((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(unsigned int i = 0; i < inputBufferList.mNumberBuffers; i++ ) { + inputBufferList.mBuffers[i].mDataByteSize = inputBufferList.mBuffers[i].mNumberChannels * channelSize; + } + + for(unsigned int i = 0; i < outputBufferList.mNumberBuffers; i++ ) { + outputBufferList.mBuffers[i].mDataByteSize = outputBufferList.mBuffers[i].mNumberChannels * channelSize; + } + // process the buffer + 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(unsigned int i = 0; i < inputBufferList.mNumberBuffers; i++ ) { + inputBufferList.mBuffers[i].mData = + (char *)inputBufferList.mBuffers[i].mData + inputBufferList.mBuffers[i].mNumberChannels * channelSize; + } + + for(unsigned int i = 0; i < outputBufferList.mNumberBuffers; i++ ) { + outputBufferList.mBuffers[i].mData = + (char *)outputBufferList.mBuffers[i].mData + outputBufferList.mBuffers[i].mNumberChannels * channelSize; + } + + 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 + { + if(mParamList.size() == 0 ) + { + // 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; + processParams.actionFlags = &ioActionFlags; + processParams.inputBufferList = &inputBufferList; + processParams.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( mParamList, + nFrames, + &processParams ); + + + // fixup the buffer pointers to how they were before we started + UInt32 channelSize = nFrames * mBytesPerFrame; + for(unsigned int i = 0; i < inputBufferList.mNumberBuffers; i++ ) { + UInt32 size = inputBufferList.mBuffers[i].mNumberChannels * channelSize; + inputBufferList.mBuffers[i].mData = (char *)inputBufferList.mBuffers[i].mData - size; + inputBufferList.mBuffers[i].mDataByteSize = size; + } + + for(unsigned int i = 0; i < outputBufferList.mNumberBuffers; i++ ) { + UInt32 size = outputBufferList.mBuffers[i].mNumberChannels * channelSize; + outputBufferList.mBuffers[i].mData = (char *)outputBufferList.mBuffers[i].mData - size; + outputBufferList.mBuffers[i].mDataByteSize = size; + } + } + } + + if ( (ioActionFlags & kAudioUnitRenderAction_OutputIsSilence) && !ProcessesInPlace() ) + { + AUBufferList::ZeroBuffer(mMainOutput->GetBufferList() ); + } + } + + return result; +} + + +OSStatus AUEffectBase::ProcessBufferLists( + AudioUnitRenderActionFlags & ioActionFlags, + const AudioBufferList & inBuffer, + AudioBufferList & outBuffer, + UInt32 inFramesToProcess ) +{ + if (ShouldBypassEffect()) + return noErr; + + // interleaved (or mono) + switch (mCommonPCMFormat) { + case CAStreamBasicDescription::kPCMFormatFloat32 : + ProcessBufferListsT<Float32>(ioActionFlags, inBuffer, outBuffer, inFramesToProcess); + break; + case CAStreamBasicDescription::kPCMFormatFixed824 : + ProcessBufferListsT<SInt32>(ioActionFlags, inBuffer, outBuffer, inFramesToProcess); + break; + case CAStreamBasicDescription::kPCMFormatInt16 : + ProcessBufferListsT<SInt16>(ioActionFlags, inBuffer, outBuffer, inFramesToProcess); + break; + default : + throw CAException(kAudio_UnimplementedError); + } + + return noErr; +} + +Float64 AUEffectBase::GetSampleRate() +{ + return GetOutput(0)->GetStreamFormat().mSampleRate; +} + +UInt32 AUEffectBase::GetNumberOfChannels() +{ + return GetOutput(0)->GetStreamFormat().mChannelsPerFrame; +} + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUEffectBase.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUEffectBase.h new file mode 100644 index 0000000000..13ba96b393 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUEffectBase.h @@ -0,0 +1,377 @@ +/* + File: AUEffectBase.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUEffectBase_h__ +#define __AUEffectBase_h__ + +#include "AUBase.h" +#include "AUSilentTimeout.h" +#include "CAException.h" + +class AUKernelBase; + +// Base class for an effect with one input stream, one output stream, +// any number of channels. + /*! @class AUEffectBase */ +class AUEffectBase : public AUBase { +public: + /*! @ctor AUEffectBase */ + AUEffectBase( AudioComponentInstance audioUnit, + bool inProcessesInPlace = true ); + /*! @dtor ~AUEffectBase */ + ~AUEffectBase(); + + /*! @method Initialize */ + virtual OSStatus Initialize(); + + /*! @method Cleanup */ + virtual void Cleanup(); + + + /*! @method Reset */ + virtual OSStatus Reset( AudioUnitScope inScope, + AudioUnitElement inElement); + + /*! @method GetPropertyInfo */ + virtual OSStatus GetPropertyInfo (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable); + + /*! @method GetProperty */ + virtual OSStatus GetProperty (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData); + + /*! @method SetProperty */ + virtual OSStatus SetProperty(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize); + + /*! @method StreamFormatWritable */ + virtual bool StreamFormatWritable (AudioUnitScope scope, + AudioUnitElement element); + + /*! @method ChangeStreamFormat */ + virtual OSStatus ChangeStreamFormat ( + AudioUnitScope inScope, + AudioUnitElement inElement, + const CAStreamBasicDescription & inPrevFormat, + const CAStreamBasicDescription & inNewFormat); + + /*! @method Render */ + virtual OSStatus Render(AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inNumberFrames); + + // 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. + /*! @method NewKernel */ + virtual AUKernelBase * NewKernel() { return NULL; } + + /*! @method ProcessBufferLists */ + virtual OSStatus ProcessBufferLists( + AudioUnitRenderActionFlags & ioActionFlags, + const AudioBufferList & inBuffer, + AudioBufferList & outBuffer, + UInt32 inFramesToProcess ); + + // convenience format accessors (use output 0's format) + /*! @method GetSampleRate */ + Float64 GetSampleRate(); + + /*! @method GetNumberOfChannels */ + UInt32 GetNumberOfChannels(); + + // convenience wrappers for accessing parameters in the global scope + /*! @method SetParameter */ + using AUBase::SetParameter; + void SetParameter( AudioUnitParameterID paramID, + AudioUnitParameterValue value) + { + Globals()->SetParameter(paramID, value); + } + + /*! @method GetParameter */ + using AUBase::GetParameter; + AudioUnitParameterValue GetParameter( AudioUnitParameterID paramID ) + { + return Globals()->GetParameter(paramID ); + } + + /*! @method CanScheduleParameters */ + virtual bool CanScheduleParameters() const { return true; } + + /*! @method IsBypassEffect */ + // This is used for the property value - to reflect to the UI if an effect is bypassed + bool IsBypassEffect () { return mBypassEffect; } + +protected: + + /*! @method MaintainKernels */ + void MaintainKernels(); + + /*! @method ShouldBypassEffect */ + // 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(); } + +public: + /*! @method SetBypassEffect */ + virtual void SetBypassEffect (bool inFlag) { mBypassEffect = inFlag; } + + /*! @method SetParamHasSampleRateDependency */ + void SetParamHasSampleRateDependency (bool inFlag) + { + mParamSRDep = inFlag; + } + + /*! @method GetParamHasSampleRateDependency */ + bool GetParamHasSampleRateDependency () const { return mParamSRDep; } + + struct ScheduledProcessParams // pointer passed in as void* userData param for ProcessScheduledSlice() + { + AudioUnitRenderActionFlags *actionFlags; + AudioBufferList *inputBufferList; + AudioBufferList *outputBufferList; + }; + + virtual OSStatus ProcessScheduledSlice( void *inUserData, + UInt32 inStartFrameInBuffer, + UInt32 inSliceFramesToProcess, + UInt32 inTotalBufferFrames ); + + + bool ProcessesInPlace() const {return mProcessesInPlace;}; + void SetProcessesInPlace(bool inProcessesInPlace) {mProcessesInPlace = inProcessesInPlace;}; + + typedef std::vector<AUKernelBase *> KernelList; + + + +protected: + /*! @var mKernelList */ + KernelList mKernelList; + + AUKernelBase* GetKernel(UInt32 index) { return mKernelList[index]; } + + /*! @method IsInputSilent */ + bool IsInputSilent (AudioUnitRenderActionFlags inActionFlags, UInt32 inFramesToProcess) + { + bool inputSilent = (inActionFlags & kAudioUnitRenderAction_OutputIsSilence) != 0; + + // take latency and tail time into account when propagating the silent bit + UInt32 silentTimeoutFrames = UInt32(GetSampleRate() * (GetLatency() + GetTailTime())); + mSilentTimeout.Process (inFramesToProcess, silentTimeoutFrames, inputSilent); + return inputSilent; + } + +#if TARGET_OS_IPHONE + void SetOnlyOneKernel(bool inUseOnlyOneKernel) { mOnlyOneKernel = inUseOnlyOneKernel; } // set in ctor of subclass that wants it. +#endif + + template <typename T> + void ProcessBufferListsT( + AudioUnitRenderActionFlags & ioActionFlags, + const AudioBufferList & inBuffer, + AudioBufferList & outBuffer, + UInt32 inFramesToProcess ); + + CAStreamBasicDescription::CommonPCMFormat GetCommonPCMFormat() const { return mCommonPCMFormat; } + + +private: + /*! @var mBypassEffect */ + bool mBypassEffect; + /*! @var mParamSRDep */ + bool mParamSRDep; + + /*! @var mProcessesInplace */ + bool mProcessesInPlace; + + /*! @var mSilentTimeout */ + AUSilentTimeout mSilentTimeout; + + /*! @var mMainOutput */ + AUOutputElement * mMainOutput; + + /*! @var mMainInput */ + AUInputElement * mMainInput; + +#if TARGET_OS_IPHONE + /*! @var mOnlyOneKernel */ + bool mOnlyOneKernel; +#endif + + /*! @var mCommonPCMFormat */ + CAStreamBasicDescription::CommonPCMFormat mCommonPCMFormat; + UInt32 mBytesPerFrame; +}; + + +// Base class for a "kernel", an object that performs DSP on one channel of an interleaved stream. + /*! @class AUKernelBase */ +class AUKernelBase { +public: + /*! @ctor AUKernelBase */ + AUKernelBase(AUEffectBase *inAudioUnit ) : + mAudioUnit(inAudioUnit) { } + + /*! @dtor ~AUKernelBase */ + virtual ~AUKernelBase() { } + + /*! @method Reset */ + virtual void Reset() { } + + /*! @method Process */ + virtual void Process( const Float32 * inSourceP, + Float32 * inDestP, + UInt32 inFramesToProcess, + UInt32 inNumChannels, + bool & ioSilence) { throw CAException(kAudio_UnimplementedError ); } + + /*! @method Process */ + virtual void Process( const SInt32 * inSourceP, + SInt32 * inDestP, + UInt32 inFramesToProcess, + UInt32 inNumChannels, + bool & ioSilence) { throw CAException(kAudio_UnimplementedError ); } + + /*! @method Process */ + virtual void Process( const SInt16 * inSourceP, + SInt16 * inDestP, + UInt32 inFramesToProcess, + UInt32 inNumChannels, + bool & ioSilence) { throw CAException(kAudio_UnimplementedError ); } + + /*! @method GetSampleRate */ + Float64 GetSampleRate() + { + return mAudioUnit->GetSampleRate(); + } + + /*! @method GetParameter */ + AudioUnitParameterValue GetParameter (AudioUnitParameterID paramID) + { + return mAudioUnit->GetParameter(paramID); + } + + void SetChannelNum (UInt32 inChan) { mChannelNum = inChan; } + UInt32 GetChannelNum () { return mChannelNum; } + +protected: + /*! @var mAudioUnit */ + AUEffectBase * mAudioUnit; + UInt32 mChannelNum; + +}; + +template <typename T> +void AUEffectBase::ProcessBufferListsT( + AudioUnitRenderActionFlags & ioActionFlags, + const AudioBufferList & inBuffer, + AudioBufferList & outBuffer, + UInt32 inFramesToProcess ) +{ + bool ioSilence; + + bool silentInput = IsInputSilent (ioActionFlags, inFramesToProcess); + ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence; + + // call the kernels to handle either interleaved or deinterleaved + if (inBuffer.mNumberBuffers == 1) { + if (inBuffer.mBuffers[0].mNumberChannels == 0) + throw CAException(kAudio_ParamError); + + for (UInt32 channel = 0; channel < mKernelList.size(); ++channel) { + AUKernelBase *kernel = mKernelList[channel]; + + if (kernel == NULL) continue; + ioSilence = silentInput; + + // process each interleaved channel individually + kernel->Process( + (const T *)inBuffer.mBuffers[0].mData + channel, + (T *)outBuffer.mBuffers[0].mData + channel, + inFramesToProcess, + inBuffer.mBuffers[0].mNumberChannels, + ioSilence); + + if (!ioSilence) + ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence; + } + } else { + for (UInt32 channel = 0; channel < mKernelList.size(); ++channel) { + AUKernelBase *kernel = mKernelList[channel]; + + if (kernel == NULL) continue; + + ioSilence = silentInput; + const AudioBuffer *srcBuffer = &inBuffer.mBuffers[channel]; + AudioBuffer *destBuffer = &outBuffer.mBuffers[channel]; + + kernel->Process( + (const T *)srcBuffer->mData, + (T *)destBuffer->mData, + inFramesToProcess, + 1, + ioSilence); + + if (!ioSilence) + ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence; + } + } +} + + +#endif // __AUEffectBase_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIBase.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIBase.cpp new file mode 100644 index 0000000000..e5e358d685 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIBase.cpp @@ -0,0 +1,495 @@ +/* + File: AUMIDIBase.cpp + Abstract: AUMIDIBase.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUMIDIBase.h" +#include <CoreMIDI/CoreMIDI.h> +#include "CAXException.h" + +//temporaray location +enum +{ + kMidiMessage_NoteOff = 0x80, + kMidiMessage_NoteOn = 0x90, + kMidiMessage_PolyPressure = 0xA0, + kMidiMessage_ControlChange = 0xB0, + kMidiMessage_ProgramChange = 0xC0, + kMidiMessage_ChannelPressure = 0xD0, + kMidiMessage_PitchWheel = 0xE0, + + kMidiController_AllSoundOff = 120, + kMidiController_ResetAllControllers = 121, + kMidiController_AllNotesOff = 123 +}; + +AUMIDIBase::AUMIDIBase(AUBase* inBase) + : mAUBaseInstance (*inBase) +{ +#if CA_AUTO_MIDI_MAP + mMapManager = new CAAUMIDIMapManager(); +#endif +} + +AUMIDIBase::~AUMIDIBase() +{ +#if CA_AUTO_MIDI_MAP + if (mMapManager) + delete mMapManager; +#endif +} + +#if TARGET_API_MAC_OSX +OSStatus AUMIDIBase::DelegateGetPropertyInfo(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable) +{ + OSStatus result = noErr; + + switch (inID) { +#if !TARGET_OS_IPHONE + case kMusicDeviceProperty_MIDIXMLNames: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + if (GetXMLNames(NULL) == noErr) { + outDataSize = sizeof(CFURLRef); + outWritable = false; + } else + result = kAudioUnitErr_InvalidProperty; + break; +#endif +#if CA_AUTO_MIDI_MAP + case kAudioUnitProperty_AllParameterMIDIMappings: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + outWritable = true; + outDataSize = sizeof (AUParameterMIDIMapping)*mMapManager->NumMaps(); + result = noErr; + break; + + case kAudioUnitProperty_HotMapParameterMIDIMapping: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + outWritable = true; + outDataSize = sizeof (AUParameterMIDIMapping); + result = noErr; + break; + + case kAudioUnitProperty_AddParameterMIDIMapping: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + outWritable = true; + outDataSize = sizeof (AUParameterMIDIMapping); + result = noErr; + break; + + case kAudioUnitProperty_RemoveParameterMIDIMapping: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + outWritable = true; + outDataSize = sizeof (AUParameterMIDIMapping); + result = noErr; + break; +#endif + + default: + result = kAudioUnitErr_InvalidProperty; + break; + } + return result; + +#if CA_AUTO_MIDI_MAP || (!TARGET_OS_IPHONE) +InvalidScope: + return kAudioUnitErr_InvalidScope; +InvalidElement: + return kAudioUnitErr_InvalidElement; +#endif +} + +OSStatus AUMIDIBase::DelegateGetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData) +{ + OSStatus result; + + switch (inID) { +#if !TARGET_OS_IPHONE + case kMusicDeviceProperty_MIDIXMLNames: + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + result = GetXMLNames((CFURLRef *)outData); + break; +#endif +#if CA_AUTO_MIDI_MAP + case kAudioUnitProperty_AllParameterMIDIMappings:{ + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + AUParameterMIDIMapping* maps = (static_cast<AUParameterMIDIMapping*>(outData)); + mMapManager->GetMaps(maps); +// printf ("GETTING MAPS\n"); +// mMapManager->Print(); + result = noErr; + break; + } + + case kAudioUnitProperty_HotMapParameterMIDIMapping:{ + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + AUParameterMIDIMapping * map = (static_cast<AUParameterMIDIMapping*>(outData)); + mMapManager->GetHotParameterMap (*map); + result = noErr; + break; + } +#endif + + default: + result = kAudioUnitErr_InvalidProperty; + break; + } + return result; + +#if CA_AUTO_MIDI_MAP || (!TARGET_OS_IPHONE) +InvalidScope: + return kAudioUnitErr_InvalidScope; +InvalidElement: + return kAudioUnitErr_InvalidElement; +#endif +} + +OSStatus AUMIDIBase::DelegateSetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize) +{ + OSStatus result; + + switch (inID) { +#if CA_AUTO_MIDI_MAP + case kAudioUnitProperty_AddParameterMIDIMapping:{ + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + AUParameterMIDIMapping * maps = (AUParameterMIDIMapping*)inData; + mMapManager->SortedInsertToParamaterMaps (maps, (inDataSize / sizeof(AUParameterMIDIMapping)), mAUBaseInstance); + mAUBaseInstance.PropertyChanged (kAudioUnitProperty_AllParameterMIDIMappings, kAudioUnitScope_Global, 0); + result = noErr; + break; + } + + case kAudioUnitProperty_RemoveParameterMIDIMapping:{ + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + AUParameterMIDIMapping * maps = (AUParameterMIDIMapping*)inData; + bool didChange; + mMapManager->SortedRemoveFromParameterMaps(maps, (inDataSize / sizeof(AUParameterMIDIMapping)), didChange); + if (didChange) + mAUBaseInstance.PropertyChanged (kAudioUnitProperty_AllParameterMIDIMappings, kAudioUnitScope_Global, 0); + result = noErr; + break; + } + + case kAudioUnitProperty_HotMapParameterMIDIMapping:{ + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + AUParameterMIDIMapping & map = *((AUParameterMIDIMapping*)inData); + mMapManager->SetHotMapping (map); + result = noErr; + break; + } + case kAudioUnitProperty_AllParameterMIDIMappings:{ + ca_require(inScope == kAudioUnitScope_Global, InvalidScope); + ca_require(inElement == 0, InvalidElement); + AUParameterMIDIMapping * mappings = (AUParameterMIDIMapping*)inData; + mMapManager->ReplaceAllMaps (mappings, (inDataSize / sizeof(AUParameterMIDIMapping)), mAUBaseInstance); + result = noErr; + break; + } +#endif + + default: + result = kAudioUnitErr_InvalidProperty; + break; + } + return result; +#if CA_AUTO_MIDI_MAP + InvalidScope: + return kAudioUnitErr_InvalidScope; + InvalidElement: + return kAudioUnitErr_InvalidElement; +#endif +} + + + +#endif //TARGET_API_MAC_OSX + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#pragma mark ____MidiDispatch + + +inline const Byte * NextMIDIEvent(const Byte *event, const Byte *end) +{ + Byte c = *event; + switch (c >> 4) { + default: // data byte -- assume in sysex + while ((*++event & 0x80) == 0 && event < end) + ; + break; + case 0x8: + case 0x9: + case 0xA: + case 0xB: + case 0xE: + event += 3; + break; + case 0xC: + case 0xD: + event += 2; + break; + case 0xF: + switch (c) { + case 0xF0: + while ((*++event & 0x80) == 0 && event < end) + ; + break; + case 0xF1: + case 0xF3: + event += 2; + break; + case 0xF2: + event += 3; + break; + default: + ++event; + break; + } + } + return (event >= end) ? end : event; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// AUMIDIBase::HandleMIDIPacketList +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +OSStatus AUMIDIBase::HandleMIDIPacketList(const MIDIPacketList *pktlist) +{ + if (!mAUBaseInstance.IsInitialized()) return kAudioUnitErr_Uninitialized; + + int nPackets = pktlist->numPackets; + const MIDIPacket *pkt = pktlist->packet; + + while (nPackets-- > 0) { + const Byte *event = pkt->data, *packetEnd = event + pkt->length; + long startFrame = (long)pkt->timeStamp; + while (event < packetEnd) { + Byte status = event[0]; + if (status & 0x80) { + // really a status byte (not sysex continuation) + HandleMidiEvent(status & 0xF0, status & 0x0F, event[1], event[2], static_cast<UInt32>(startFrame)); + // note that we're generating a bogus channel number for system messages (0xF0-FF) + } + event = NextMIDIEvent(event, packetEnd); + } + pkt = reinterpret_cast<const MIDIPacket *>(packetEnd); + } + return noErr; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// AUMIDIBase::HandleMidiEvent +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +OSStatus AUMIDIBase::HandleMidiEvent(UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame) +{ + if (!mAUBaseInstance.IsInitialized()) return kAudioUnitErr_Uninitialized; + +#if CA_AUTO_MIDI_MAP +// 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 (mMapManager->HandleHotMapping (status, channel, data1, mAUBaseInstance)) { + mAUBaseInstance.PropertyChanged (kAudioUnitProperty_HotMapParameterMIDIMapping, kAudioUnitScope_Global, 0); + } + else { + mMapManager->FindParameterMapEventMatch(status, channel, data1, data2, inStartFrame, mAUBaseInstance); + } +#endif + + OSStatus result = noErr; + + switch(status) + { + case kMidiMessage_NoteOn: + if(data2) + { + result = HandleNoteOn(channel, data1, data2, inStartFrame); + } + else + { + // zero velocity translates to note off + result = HandleNoteOff(channel, data1, data2, inStartFrame); + } + break; + + case kMidiMessage_NoteOff: + result = HandleNoteOff(channel, data1, data2, inStartFrame); + break; + + default: + result = HandleNonNoteEvent (status, channel, data1, data2, inStartFrame); + break; + } + + return result; +} + +OSStatus AUMIDIBase::HandleNonNoteEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame) +{ + OSStatus result = noErr; + + switch (status) + { + case kMidiMessage_PitchWheel: + result = HandlePitchWheel(channel, data1, data2, inStartFrame); + break; + + case kMidiMessage_ProgramChange: + result = HandleProgramChange(channel, data1); + break; + + case kMidiMessage_ChannelPressure: + result = HandleChannelPressure(channel, data1, inStartFrame); + break; + + case kMidiMessage_ControlChange: + { + switch (data1) { + case kMidiController_AllNotesOff: + result = HandleAllNotesOff(channel); + break; + + case kMidiController_ResetAllControllers: + result = HandleResetAllControllers(channel); + break; + + case kMidiController_AllSoundOff: + result = HandleAllSoundOff(channel); + break; + + default: + result = HandleControlChange(channel, data1, data2, inStartFrame); + break; + } + break; + } + case kMidiMessage_PolyPressure: + result = HandlePolyPressure (channel, data1, data2, inStartFrame); + break; + } + return result; +} + +OSStatus AUMIDIBase::SysEx (const UInt8 * inData, + UInt32 inLength) +{ + if (!mAUBaseInstance.IsInitialized()) return kAudioUnitErr_Uninitialized; + + return HandleSysEx(inData, inLength ); +} + + + +#if TARGET_OS_MAC + #if __LP64__ + // comp instance, parameters in forward order + #define PARAM(_typ, _name, _index, _nparams) \ + _typ _name = *(_typ *)¶ms->params[_index + 1]; + #else + // parameters in reverse order, then comp instance + #define PARAM(_typ, _name, _index, _nparams) \ + _typ _name = *(_typ *)¶ms->params[_nparams - 1 - _index]; + #endif +#elif TARGET_OS_WIN32 + // (no comp instance), parameters in forward order + #define PARAM(_typ, _name, _index, _nparams) \ + _typ _name = *(_typ *)¶ms->params[_index]; +#endif + +#if !CA_USE_AUDIO_PLUGIN_ONLY +OSStatus AUMIDIBase::ComponentEntryDispatch( ComponentParameters * params, + AUMIDIBase * This) +{ + if (This == NULL) return kAudio_ParamError; + + OSStatus result; + + switch (params->what) { + case kMusicDeviceMIDIEventSelect: + { + PARAM(UInt32, pbinStatus, 0, 4); + PARAM(UInt32, pbinData1, 1, 4); + PARAM(UInt32, pbinData2, 2, 4); + PARAM(UInt32, pbinOffsetSampleFrame, 3, 4); + result = This->MIDIEvent(pbinStatus, pbinData1, pbinData2, pbinOffsetSampleFrame); + } + break; + case kMusicDeviceSysExSelect: + { + PARAM(const UInt8 *, pbinData, 0, 2); + PARAM(UInt32, pbinLength, 1, 2); + result = This->SysEx(pbinData, pbinLength); + } + break; + + default: + result = badComponentSelector; + break; + } + + return result; +} +#endif diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIBase.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIBase.h new file mode 100644 index 0000000000..40c6a77695 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIBase.h @@ -0,0 +1,213 @@ +/* + File: AUMIDIBase.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUMIDIBase_h__ +#define __AUMIDIBase_h__ + +#include "AUBase.h" + +#if CA_AUTO_MIDI_MAP + #include "CAAUMIDIMapManager.h" +#endif + +struct MIDIPacketList; + +// ________________________________________________________________________ +// MusicDeviceBase +// + /*! @class AUMIDIBase */ +class AUMIDIBase { +public: + // this is NOT a copy constructor! + /*! @ctor AUMIDIBase */ + AUMIDIBase(AUBase* inBase); + /*! @dtor ~AUMIDIBase */ + virtual ~AUMIDIBase(); + + /*! @method MIDIEvent */ + virtual OSStatus MIDIEvent( UInt32 inStatus, + UInt32 inData1, + UInt32 inData2, + UInt32 inOffsetSampleFrame) + { + UInt32 strippedStatus = inStatus & 0xf0; + UInt32 channel = inStatus & 0x0f; + + return HandleMidiEvent(strippedStatus, channel, inData1, inData2, inOffsetSampleFrame); + } + + /*! @method HandleMIDIPacketList */ + OSStatus HandleMIDIPacketList(const MIDIPacketList *pktlist); + + /*! @method SysEx */ + virtual OSStatus SysEx( const UInt8 * inData, + UInt32 inLength); + +#if TARGET_API_MAC_OSX + /*! @method DelegateGetPropertyInfo */ + virtual OSStatus DelegateGetPropertyInfo(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable); + + /*! @method DelegateGetProperty */ + virtual OSStatus DelegateGetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData); + + /*! @method DelegateSetProperty */ + virtual OSStatus DelegateSetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize); +#endif + +protected: + // MIDI dispatch + /*! @method HandleMidiEvent */ + virtual OSStatus HandleMidiEvent( UInt8 inStatus, + UInt8 inChannel, + UInt8 inData1, + UInt8 inData2, + UInt32 inStartFrame); + + /*! @method HandleNonNoteEvent */ + virtual OSStatus HandleNonNoteEvent ( UInt8 status, + UInt8 channel, + UInt8 data1, + UInt8 data2, + UInt32 inStartFrame); + +#if TARGET_API_MAC_OSX + /*! @method GetXMLNames */ + virtual OSStatus GetXMLNames(CFURLRef *outNameDocument) + { return kAudioUnitErr_InvalidProperty; } // if not overridden, it's unsupported +#endif + +// channel messages + /*! @method HandleNoteOn */ + virtual OSStatus HandleNoteOn( UInt8 inChannel, + UInt8 inNoteNumber, + UInt8 inVelocity, + UInt32 inStartFrame) { return noErr; } + + /*! @method HandleNoteOff */ + virtual OSStatus HandleNoteOff( UInt8 inChannel, + UInt8 inNoteNumber, + UInt8 inVelocity, + UInt32 inStartFrame) { return noErr; } + + /*! @method HandleControlChange */ + virtual OSStatus HandleControlChange( UInt8 inChannel, + UInt8 inController, + UInt8 inValue, + UInt32 inStartFrame) { return noErr; } + + /*! @method HandlePitchWheel */ + virtual OSStatus HandlePitchWheel( UInt8 inChannel, + UInt8 inPitch1, + UInt8 inPitch2, + UInt32 inStartFrame) { return noErr; } + + /*! @method HandleChannelPressure */ + virtual OSStatus HandleChannelPressure( UInt8 inChannel, + UInt8 inValue, + UInt32 inStartFrame) { return noErr; } + + /*! @method HandleProgramChange */ + virtual OSStatus HandleProgramChange( UInt8 inChannel, + UInt8 inValue) { return noErr; } + + /*! @method HandlePolyPressure */ + virtual OSStatus HandlePolyPressure( UInt8 inChannel, + UInt8 inKey, + UInt8 inValue, + UInt32 inStartFrame) { return noErr; } + + /*! @method HandleResetAllControllers */ + virtual OSStatus HandleResetAllControllers(UInt8 inChannel) { return noErr; } + + /*! @method HandleAllNotesOff */ + virtual OSStatus HandleAllNotesOff( UInt8 inChannel) { return noErr; } + + /*! @method HandleAllSoundOff */ + virtual OSStatus HandleAllSoundOff( UInt8 inChannel) { return noErr; } + + +//System messages + /*! @method HandleSysEx */ + virtual OSStatus HandleSysEx( const UInt8 * inData, + UInt32 inLength ) { return noErr; } + +#if CA_AUTO_MIDI_MAP + /* map manager */ + CAAUMIDIMapManager *GetMIDIMapManager() {return mMapManager;}; + +#endif + + +private: + /*! @var mAUBaseInstance */ + AUBase & mAUBaseInstance; + +#if CA_AUTO_MIDI_MAP + /* map manager */ + CAAUMIDIMapManager * mMapManager; +#endif + +public: +#if !CA_USE_AUDIO_PLUGIN_ONLY + // component dispatcher + /*! @method ComponentEntryDispatch */ + static OSStatus ComponentEntryDispatch( ComponentParameters *params, + AUMIDIBase *This); +#endif +}; + +#endif // __AUMIDIBase_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIEffectBase.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIEffectBase.cpp new file mode 100644 index 0000000000..dfe307c9eb --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIEffectBase.cpp @@ -0,0 +1,164 @@ +/* + File: AUMIDIEffectBase.cpp + Abstract: AUMIDIEffectBase.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUMIDIEffectBase.h" + +// compatibility with older OS SDK releases +typedef OSStatus +(*TEMP_MusicDeviceMIDIEventProc)( void * inComponentStorage, + UInt32 inStatus, + UInt32 inData1, + UInt32 inData2, + UInt32 inOffsetSampleFrame); + +static OSStatus AUMIDIEffectBaseMIDIEvent(void * inComponentStorage, + UInt32 inStatus, + UInt32 inData1, + UInt32 inData2, + UInt32 inOffsetSampleFrame); + +AUMIDIEffectBase::AUMIDIEffectBase( AudioComponentInstance inInstance, + bool inProcessesInPlace ) + : AUEffectBase(inInstance, inProcessesInPlace), + AUMIDIBase(this) +{ +} + +OSStatus AUMIDIEffectBase::GetPropertyInfo(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable) +{ + OSStatus result; + + 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; + +#if !CA_USE_AUDIO_PLUGIN_ONLY + if (inID == kAudioUnitProperty_FastDispatch) { + if (inElement == kMusicDeviceMIDIEventSelect) { + *(TEMP_MusicDeviceMIDIEventProc *)outData = AUMIDIEffectBaseMIDIEvent; + return noErr; + } + return kAudioUnitErr_InvalidElement; + } +#endif + + 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; +} + + +#if !TARGET_OS_IPHONE +OSStatus AUMIDIEffectBase::ComponentEntryDispatch(ComponentParameters * params, + AUMIDIEffectBase * This) +{ + if (This == NULL) return paramErr; + + OSStatus result; + + switch (params->what) { + case kMusicDeviceMIDIEventSelect: + case kMusicDeviceSysExSelect: + result = AUMIDIBase::ComponentEntryDispatch (params, This); + break; + default: + result = AUEffectBase::ComponentEntryDispatch(params, This); + break; + } + + return result; +} +#endif + +// fast dispatch +static OSStatus AUMIDIEffectBaseMIDIEvent(void * inComponentStorage, + UInt32 inStatus, + UInt32 inData1, + UInt32 inData2, + UInt32 inOffsetSampleFrame) +{ + OSStatus result = noErr; + try { + AUMIDIEffectBase *This = static_cast<AUMIDIEffectBase *>(inComponentStorage); + if (This == NULL) return paramErr; + result = This->AUMIDIBase::MIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame); + } + COMPONENT_CATCH + return result; +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIEffectBase.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIEffectBase.h new file mode 100644 index 0000000000..b38f506d6c --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUMIDIEffectBase.h @@ -0,0 +1,104 @@ +/* + File: AUMIDIEffectBase.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUMIDIEffectBase_h__ +#define __AUMIDIEffectBase_h__ + +#include "AUMIDIBase.h" +#include "AUEffectBase.h" + +// ________________________________________________________________________ +// AUMIDIEffectBase +// + /*! @class AUMIDIEffectBase */ +class AUMIDIEffectBase : public AUEffectBase, public AUMIDIBase { +public: + /*! @ctor AUMIDIEffectBase */ + AUMIDIEffectBase( AudioComponentInstance inInstance, + bool inProcessesInPlace = false ); + /*! @method MIDIEvent */ + virtual OSStatus MIDIEvent(UInt32 inStatus, + UInt32 inData1, + UInt32 inData2, + UInt32 inOffsetSampleFrame) + { + return AUMIDIBase::MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame); + } + + /*! @method SysEx */ + virtual OSStatus SysEx(const UInt8 * inData, + UInt32 inLength) + { + return AUMIDIBase::SysEx (inData, inLength); + } + + /*! @method GetPropertyInfo */ + virtual OSStatus GetPropertyInfo(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable); + + /*! @method GetProperty */ + virtual OSStatus GetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData); + /*! @method SetProperty */ + virtual OSStatus SetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize); +#if !TARGET_OS_IPHONE + // component dispatcher + /*! @method ComponentEntryDispatch */ + static OSStatus ComponentEntryDispatch( ComponentParameters * params, + AUMIDIEffectBase * This); +#endif +}; + +#endif // __AUMIDIEffectBase_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUOutputBase.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUOutputBase.cpp new file mode 100644 index 0000000000..591c43ab8c --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUOutputBase.cpp @@ -0,0 +1,76 @@ +/* + File: AUOutputBase.cpp + Abstract: AUOutputBase.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#if !CA_USE_AUDIO_PLUGIN_ONLY +#include "AUOutputBase.h" + +OSStatus AUOutputBase::ComponentEntryDispatch(ComponentParameters *params, AUOutputBase *This) +{ + if (This == NULL) return paramErr; + + OSStatus result; + + switch (params->what) { + case kAudioOutputUnitStartSelect: + { + result = This->Start(); + } + break; + + case kAudioOutputUnitStopSelect: + { + result = This->Stop(); + } + break; + + default: + result = badComponentSelector; + break; + } + + return result; +} +#endif diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUOutputBase.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUOutputBase.h new file mode 100644 index 0000000000..8e5399b911 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUOutputBase.h @@ -0,0 +1,82 @@ +/* + File: AUOutputBase.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUOutputBase_h__ +#define __AUOutputBase_h__ + +#include "AUBase.h" + +// ________________________________________________________________________ +// AUOutputBase +// this is now a mix-in rather than an AUBase subclass + + /*! @class AUOutputBase */ +class AUOutputBase { +public: + /*! @ctor AUOutputBase */ + AUOutputBase(AUBase *inBase) : mAUBaseInstance(*inBase) { } + virtual ~AUOutputBase() { } + + // additional component entry points + /*! @method Start */ + virtual OSStatus Start() = 0; + + /*! @method Stop */ + virtual OSStatus Stop() = 0; + +#if !CA_USE_AUDIO_PLUGIN_ONLY + // component dispatcher + /*! @method ComponentEntryDispatch */ + static OSStatus ComponentEntryDispatch( ComponentParameters * params, + AUOutputBase * This); +#endif + +private: + /*! @var mAUBaseInstance */ + AUBase & mAUBaseInstance; +}; + +#endif // __AUOutputBase_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUPannerBase.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUPannerBase.cpp new file mode 100644 index 0000000000..f1e2f248ab --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUPannerBase.cpp @@ -0,0 +1,706 @@ +/* + File: AUPannerBase.cpp + Abstract: AUPannerBase.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUPannerBase.h" +#include "CABundleLocker.h" +#include <AudioToolbox/AudioToolbox.h> +#include <Accelerate/Accelerate.h> + +static bool sLocalized = false; + +static CFStringRef kPanner_Azimuth_Name = CFSTR("azimuth"); +static CFStringRef kPanner_Elevation_Name = CFSTR("elevation"); +static CFStringRef kPanner_Distance_Name = CFSTR("distance"); + +static CFStringRef kPanner_CoordScale_Name = CFSTR("coordinate scale"); +static CFStringRef kPanner_RefDistance_Name = CFSTR("reference distance"); +static CFStringRef kPanner_Gain_Name = CFSTR("gain"); + +static Float32 kPannerParamDefault_Azimuth = 0.; +static Float32 kPannerParamDefault_Elevation = 0.; +static Float32 kPannerParamDefault_Distance = 1.; + +static Float32 kPannerParamDefault_CoordScale = 10.; +static Float32 kPannerParamDefault_RefDistance = 1.; +static Float32 kPannerParamDefault_Gain = 1.; + +//_____________________________________________________________________________ +// +AUPannerBase::AUPannerBase(AudioComponentInstance inAudioUnit) + : AUBase(inAudioUnit, 1, 1), mBypassEffect(false) +{ + { + CABundleLocker lock; + if (!sLocalized) + { + CFBundleRef bundle = CFBundleGetBundleWithIdentifier( CFSTR("com.apple.audio.units.Components") ); + if (bundle != NULL) + { + kPanner_Azimuth_Name = CFCopyLocalizedStringFromTableInBundle(kPanner_Azimuth_Name, CFSTR("AudioUnits"), bundle, CFSTR("")); + kPanner_Elevation_Name = CFCopyLocalizedStringFromTableInBundle(kPanner_Elevation_Name, CFSTR("AudioUnits"), bundle, CFSTR("")); + kPanner_Distance_Name = CFCopyLocalizedStringFromTableInBundle(kPanner_Distance_Name, CFSTR("AudioUnits"), bundle, CFSTR("")); + + kPanner_CoordScale_Name = CFCopyLocalizedStringFromTableInBundle(kPanner_CoordScale_Name, CFSTR("AudioUnits"), bundle, CFSTR("")); + kPanner_RefDistance_Name = CFCopyLocalizedStringFromTableInBundle(kPanner_RefDistance_Name, CFSTR("AudioUnits"), bundle, CFSTR("")); + kPanner_Gain_Name = CFCopyLocalizedStringFromTableInBundle(kPanner_Gain_Name, CFSTR("AudioUnits"), bundle, CFSTR("")); + + } + + sLocalized = true; + } + } + + CreateElements(); + + SetParameter(kPannerParam_Azimuth, kPannerParamDefault_Azimuth); + SetParameter(kPannerParam_Elevation, kPannerParamDefault_Elevation); + SetParameter(kPannerParam_Distance, kPannerParamDefault_Distance); + + SetParameter(kPannerParam_CoordScale, kPannerParamDefault_CoordScale); + SetParameter(kPannerParam_RefDistance, kPannerParamDefault_RefDistance); + SetParameter(kPannerParam_Gain, kPannerParamDefault_Gain); +} + +//_____________________________________________________________________________ +// +AUPannerBase::~AUPannerBase() +{ + Cleanup(); +} + +//_____________________________________________________________________________ +// +/*! @method Initialize */ +OSStatus AUPannerBase::Initialize() +{ + OSStatus err = noErr; + AllocBypassMatrix(); + err = UpdateBypassMatrix(); + return err; +} + +//_____________________________________________________________________________ +// +/*! @method AllocBypassMatrix */ +void AUPannerBase::AllocBypassMatrix() +{ + UInt32 inChannels = GetNumberOfInputChannels(); + UInt32 outChannels = GetNumberOfOutputChannels(); + mBypassMatrix.alloc(inChannels * outChannels, true); +} + +static AudioChannelLayoutTag DefaultTagForNumberOfChannels(UInt32 inNumberChannels) +{ + switch (inNumberChannels) { + case 1: return kAudioChannelLayoutTag_Mono; + case 2: return kAudioChannelLayoutTag_Stereo; + case 4: return kAudioChannelLayoutTag_Quadraphonic; + case 5: return kAudioChannelLayoutTag_AudioUnit_5_0; + case 6: return kAudioChannelLayoutTag_AudioUnit_6_0; + case 7: return kAudioChannelLayoutTag_AudioUnit_7_0; + case 8: return kAudioChannelLayoutTag_AudioUnit_8; + + default: return 0xFFFF0000 | inNumberChannels; + } +} + +//_____________________________________________________________________________ +// +/*! @method UpdateBypassMatrix */ +OSStatus AUPannerBase::SetDefaultChannelLayoutsIfNone() +{ + OSStatus err = noErr; + + // if layout has not been set, then guess layout from number of channels + UInt32 inChannels = GetNumberOfInputChannels(); + AudioChannelLayout inputLayoutSubstitute; + const AudioChannelLayout* inputLayout = &GetInputLayout(); + if (inputLayout == NULL || inputLayout->mChannelLayoutTag == 0) { + inputLayout = &inputLayoutSubstitute; + inputLayoutSubstitute.mNumberChannelDescriptions = 0; + inputLayoutSubstitute.mChannelBitmap = 0; + inputLayoutSubstitute.mChannelLayoutTag = DefaultTagForNumberOfChannels(inChannels); + + mInputLayout = &inputLayoutSubstitute; + err = SetAudioChannelLayout(kAudioUnitScope_Input, 0, &GetInputLayout()); + if (err) return err; + } + + // if layout has not been set, then guess layout from number of channels + UInt32 outChannels = GetNumberOfOutputChannels(); + AudioChannelLayout outputLayoutSubstitute; + const AudioChannelLayout* outputLayout = &GetOutputLayout(); + if (outputLayout == NULL || outputLayout->mChannelLayoutTag == 0) { + outputLayout = &outputLayoutSubstitute; + outputLayoutSubstitute.mNumberChannelDescriptions = 0; + outputLayoutSubstitute.mChannelBitmap = 0; + outputLayoutSubstitute.mChannelLayoutTag = DefaultTagForNumberOfChannels(outChannels); + + mOutputLayout = &outputLayoutSubstitute; + err = SetAudioChannelLayout(kAudioUnitScope_Output, 0, &GetOutputLayout()); + if (err) return err; + } + + return err; +} + + + +OSStatus AUPannerBase::UpdateBypassMatrix() +{ + OSStatus err = SetDefaultChannelLayoutsIfNone(); + if (err) return err; + + UInt32 inChannels = GetNumberOfInputChannels(); + UInt32 outChannels = GetNumberOfOutputChannels(); + + const AudioChannelLayout* inoutACL[2]; + + inoutACL[0] = &GetInputLayout(); + inoutACL[1] = &GetOutputLayout(); + + mBypassMatrix.alloc(inChannels * outChannels, true); + + UInt32 propSize = inChannels * outChannels * sizeof(Float32); + + err = AudioFormatGetProperty(kAudioFormatProperty_MatrixMixMap, sizeof(inoutACL), inoutACL, &propSize, mBypassMatrix()); + if (err) { + // if there is an error, use a diagonal matrix. + Float32* bypass = mBypassMatrix(); + for (UInt32 chan = 0; chan < std::min(inChannels, outChannels); ++chan) + { + float *amp = bypass + (chan * outChannels + chan); + *amp = 1.; + } + } + + return noErr; +} + +//_____________________________________________________________________________ +// +/*! @method Cleanup */ +void AUPannerBase::Cleanup() +{ + +} + + +//_____________________________________________________________________________ +// +/*! @method Reset */ +OSStatus AUPannerBase::Reset( AudioUnitScope inScope, + AudioUnitElement inElement) +{ + return AUBase::Reset(inScope, inElement); +} + +//_____________________________________________________________________________ +// +/*! @method GetParameterInfo */ +OSStatus AUPannerBase::GetParameterInfo( AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + AudioUnitParameterInfo &outParameterInfo ) +{ + OSStatus result = noErr; + + outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable + + kAudioUnitParameterFlag_IsReadable; + + if (inScope == kAudioUnitScope_Global) { + + switch(inParameterID) + { + case kPannerParam_Azimuth: + AUBase::FillInParameterName (outParameterInfo, kPanner_Azimuth_Name, false); + outParameterInfo.unit = kAudioUnitParameterUnit_Degrees; + outParameterInfo.minValue = -180.; + outParameterInfo.maxValue = 180; + outParameterInfo.defaultValue = kPannerParamDefault_Azimuth; + break; + + case kPannerParam_Elevation: + AUBase::FillInParameterName (outParameterInfo, kPanner_Elevation_Name, false); + outParameterInfo.unit = kAudioUnitParameterUnit_Degrees; + outParameterInfo.minValue = -90.; + outParameterInfo.maxValue = 90; + outParameterInfo.defaultValue = kPannerParamDefault_Elevation; + break; + + case kPannerParam_Distance: + AUBase::FillInParameterName (outParameterInfo, kPanner_Distance_Name, false); + outParameterInfo.unit = kAudioUnitParameterUnit_Generic; + outParameterInfo.minValue = 0.0; + outParameterInfo.maxValue = 1.; + outParameterInfo.defaultValue = kPannerParamDefault_Distance; + outParameterInfo.flags += kAudioUnitParameterFlag_IsHighResolution; + //outParameterInfo.flags += kAudioUnitParameterFlag_DisplayLogarithmic; + break; + + case kPannerParam_CoordScale: + AUBase::FillInParameterName (outParameterInfo, kPanner_CoordScale_Name, false); + outParameterInfo.unit = kAudioUnitParameterUnit_Meters; + outParameterInfo.minValue = 0.01; + outParameterInfo.maxValue = 1000.; + outParameterInfo.defaultValue = kPannerParamDefault_CoordScale; + outParameterInfo.flags += kAudioUnitParameterFlag_DisplayLogarithmic; + break; + + case kPannerParam_RefDistance: + AUBase::FillInParameterName (outParameterInfo, kPanner_RefDistance_Name, false); + outParameterInfo.unit = kAudioUnitParameterUnit_Meters; + outParameterInfo.minValue = 0.01; + outParameterInfo.maxValue = 1000.; + outParameterInfo.defaultValue = kPannerParamDefault_RefDistance; + outParameterInfo.flags += kAudioUnitParameterFlag_DisplayLogarithmic; + break; + + case kPannerParam_Gain: + AUBase::FillInParameterName (outParameterInfo, kPanner_Gain_Name, false); + outParameterInfo.unit = kAudioUnitParameterUnit_Generic; + outParameterInfo.minValue = 0.; + outParameterInfo.maxValue = 1.; + outParameterInfo.defaultValue = kPannerParamDefault_Gain; + break; + + default: + result = kAudioUnitErr_InvalidParameter; + break; + } + } else { + result = kAudioUnitErr_InvalidParameter; + } + + return result; +} + +//_____________________________________________________________________________ +// +OSStatus AUPannerBase::GetParameter( AudioUnitParameterID inParamID, + AudioUnitScope inScope, + AudioUnitElement inElement, + Float32 & outValue) +{ + if (inScope != kAudioUnitScope_Global) + return kAudioUnitErr_InvalidScope; + + outValue = Globals()->GetParameter(inParamID); + + return noErr; +} + + +//_____________________________________________________________________________ +// +OSStatus AUPannerBase::SetParameter( AudioUnitParameterID inParamID, + AudioUnitScope inScope, + AudioUnitElement inElement, + Float32 inValue, + UInt32 inBufferOffsetInFrames) +{ + if (inScope != kAudioUnitScope_Global) + return kAudioUnitErr_InvalidScope; + + Globals()->SetParameter(inParamID, inValue); + + return noErr; +} + + + +//_____________________________________________________________________________ +// +/*! @method GetPropertyInfo */ +OSStatus AUPannerBase::GetPropertyInfo (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable) +{ + OSStatus err = noErr; + switch (inID) { + case kAudioUnitProperty_BypassEffect: + if (inScope != kAudioUnitScope_Global) + return kAudioUnitErr_InvalidScope; + + outWritable = true; + outDataSize = sizeof (UInt32); + break; + default: + err = AUBase::GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable); + } + return err; +} + +//_____________________________________________________________________________ +// +/*! @method GetProperty */ +OSStatus AUPannerBase::GetProperty (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData) +{ + OSStatus err = noErr; + switch (inID) + { + case kAudioUnitProperty_BypassEffect: + if (inScope != kAudioUnitScope_Global) + return kAudioUnitErr_InvalidScope; + + *((UInt32*)outData) = (IsBypassEffect() ? 1 : 0); + break; + default: + err = AUBase::GetProperty(inID, inScope, inElement, outData); + } + return err; +} + +//_____________________________________________________________________________ +// +/*! @method SetProperty */ +OSStatus AUPannerBase::SetProperty(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize) +{ + switch (inID) + { + case kAudioUnitProperty_BypassEffect: + if (inDataSize < sizeof(UInt32)) + return kAudioUnitErr_InvalidPropertyValue; + bool tempNewSetting = *((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(0, 0); + SetBypassEffect (tempNewSetting); + } + return noErr; + } + return AUBase::SetProperty(inID, inScope, inElement, inData, inDataSize); +} + + +//_____________________________________________________________________________ +// +/*! @method StreamFormatWritable */ +bool AUPannerBase::StreamFormatWritable (AudioUnitScope scope, + AudioUnitElement element) +{ + return true; +} + +//_____________________________________________________________________________ +// +/*! @method ChangeStreamFormat */ +OSStatus AUPannerBase::ChangeStreamFormat ( + AudioUnitScope inScope, + AudioUnitElement inElement, + const CAStreamBasicDescription & inPrevFormat, + const CAStreamBasicDescription & inNewFormat) +{ + if (inScope == kAudioUnitScope_Input && !InputChannelConfigIsSupported(inNewFormat.NumberChannels())) + return kAudioUnitErr_FormatNotSupported; + + if (inScope == kAudioUnitScope_Output && !OutputChannelConfigIsSupported(inNewFormat.NumberChannels())) + return kAudioUnitErr_FormatNotSupported; + + if (inNewFormat.NumberChannels() != inPrevFormat.NumberChannels()) + RemoveAudioChannelLayout(inScope, inElement); + + return AUBase::ChangeStreamFormat(inScope, inElement, inPrevFormat, inNewFormat); +} + +//_____________________________________________________________________________ +// +/*! @method Render */ +OSStatus AUPannerBase::Render(AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inNumberFrames) +{ + if (IsBypassEffect()) + return BypassRender(ioActionFlags, inTimeStamp, inNumberFrames); + else + return PannerRender(ioActionFlags, inTimeStamp, inNumberFrames); +} + +//_____________________________________________________________________________ +// +/*! @method Render */ +OSStatus AUPannerBase::BypassRender(AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inNumberFrames) +{ + AudioUnitRenderActionFlags xflags = 0; + OSStatus result = PullInput(0, xflags, inTimeStamp, inNumberFrames); + if (result) return false; + bool isSilent = xflags & kAudioUnitRenderAction_OutputIsSilence; + + AudioBufferList& outputBufferList = GetOutput(0)->GetBufferList(); + AUBufferList::ZeroBuffer(outputBufferList); + + if (!isSilent) + { + UInt32 inChannels = GetNumberOfInputChannels(); + UInt32 outChannels = GetNumberOfOutputChannels(); + Float32* bypass = mBypassMatrix(); + for (UInt32 outChan = 0; outChan < outChannels; ++outChan) + { + float* outData = GetOutput(0)->GetChannelData(outChan); + + for (UInt32 inChan = 0; inChan < inChannels; ++inChan) + { + float* inData = GetInput(0)->GetChannelData(inChan); + float *amp = bypass + (inChan * outChannels + outChan); + vDSP_vsma(inData, 1, amp, outData, 1, outData, 1, inNumberFrames); + } + } + } + return noErr; +} + +//_____________________________________________________________________________ +// +UInt32 AUPannerBase::GetAudioChannelLayout( AudioUnitScope inScope, + AudioUnitElement inElement, + AudioChannelLayout * outLayoutPtr, + Boolean & outWritable) +{ + SetDefaultChannelLayoutsIfNone(); + + outWritable = true; + + CAAudioChannelLayout* caacl = NULL; + switch (inScope) + { + case kAudioUnitScope_Input: + caacl = &mInputLayout; + break; + case kAudioUnitScope_Output: + caacl = &mOutputLayout; + break; + default: + COMPONENT_THROW(kAudioUnitErr_InvalidScope); + } + + if (inElement != 0) + COMPONENT_THROW(kAudioUnitErr_InvalidElement); + + UInt32 size = caacl->IsValid() ? caacl->Size() : 0; + if (size > 0 && outLayoutPtr) + memcpy(outLayoutPtr, &caacl->Layout(), size); + + return size; +} + +//_____________________________________________________________________________ +// +OSStatus AUPannerBase::RemoveAudioChannelLayout( AudioUnitScope inScope, + AudioUnitElement inElement) +{ + CAAudioChannelLayout* caacl = NULL; + switch (inScope) + { + case kAudioUnitScope_Input: + caacl = &mInputLayout; + break; + case kAudioUnitScope_Output: + caacl = &mOutputLayout; + break; + default: + return kAudioUnitErr_InvalidScope; + } + + if (inElement != 0) + return kAudioUnitErr_InvalidElement; + + *caacl = NULL; + return noErr; +} + +//_____________________________________________________________________________ +// +OSStatus AUPannerBase::SetAudioChannelLayout( AudioUnitScope inScope, + AudioUnitElement inElement, + const AudioChannelLayout * inLayout) +{ + if (!inLayout) + return RemoveAudioChannelLayout(inScope, inElement); + + if (!ChannelLayoutTagIsSupported(inScope, inElement, inLayout->mChannelLayoutTag)) + return kAudioUnitErr_FormatNotSupported; + + CAAudioChannelLayout* caacl = NULL; + UInt32 currentChannels; + switch (inScope) + { + case kAudioUnitScope_Input: + caacl = &mInputLayout; + currentChannels = GetNumberOfInputChannels(); + break; + case kAudioUnitScope_Output: + caacl = &mOutputLayout; + currentChannels = GetNumberOfOutputChannels(); + break; + default: + return kAudioUnitErr_InvalidScope; + } + + if (inElement != 0) + return kAudioUnitErr_InvalidElement; + + UInt32 numChannelsInLayout = CAAudioChannelLayout::NumberChannels(*inLayout); + if (currentChannels != numChannelsInLayout) + return kAudioUnitErr_InvalidPropertyValue; + + *caacl = inLayout; + if (IsInitialized()) + UpdateBypassMatrix(); + + return noErr; +} + +//_____________________________________________________________________________ +// +UInt32 AUPannerBase::GetChannelLayoutTags( AudioUnitScope inScope, + AudioUnitElement inElement, + AudioChannelLayoutTag* outTags) +{ + switch (inScope) + { + case kAudioUnitScope_Input: + if (outTags) { + outTags[0] = kAudioChannelLayoutTag_Mono; + outTags[1] = kAudioChannelLayoutTag_Stereo; + outTags[2] = kAudioChannelLayoutTag_Ambisonic_B_Format; + } + return 3; + case kAudioUnitScope_Output: + if (outTags) { + outTags[0] = kAudioChannelLayoutTag_Stereo; + outTags[1] = kAudioChannelLayoutTag_Quadraphonic; + outTags[2] = kAudioChannelLayoutTag_AudioUnit_5_0; + outTags[3] = kAudioChannelLayoutTag_AudioUnit_6_0; + outTags[4] = kAudioChannelLayoutTag_AudioUnit_7_0; + outTags[5] = kAudioChannelLayoutTag_AudioUnit_7_0_Front; + outTags[6] = kAudioChannelLayoutTag_AudioUnit_8; + } + return 7; + default: { + OSStatus err = kAudioUnitErr_InvalidScope; + throw err; + } + } +} + + + +//_____________________________________________________________________________ +// +bool AUPannerBase::ChannelConfigIsSupported() +{ + UInt32 inChannels = GetNumberOfInputChannels(); + UInt32 outChannels = GetNumberOfOutputChannels(); + const AUChannelInfo* cinfo = NULL; + UInt32 numConfigs = SupportedNumChannels(&cinfo); + for (UInt32 i = 0; i < numConfigs; ++i) + { + if (cinfo[i].inChannels == (SInt16)inChannels && cinfo[i].outChannels == (SInt16)outChannels) + return true; + } + return false; +} + + +//_____________________________________________________________________________ +// +bool AUPannerBase::InputChannelConfigIsSupported(UInt32 inNumberChannels) +{ + const AUChannelInfo* cinfo = NULL; + UInt32 numConfigs = SupportedNumChannels(&cinfo); + for (UInt32 i = 0; i < numConfigs; ++i) + { + if (cinfo[i].inChannels == (SInt16)inNumberChannels) + return true; + } + return false; +} + +//_____________________________________________________________________________ +// +bool AUPannerBase::OutputChannelConfigIsSupported(UInt32 inNumberChannels) +{ + const AUChannelInfo* cinfo = NULL; + UInt32 numConfigs = SupportedNumChannels(&cinfo); + for (UInt32 i = 0; i < numConfigs; ++i) + { + if (cinfo[i].outChannels == (SInt16)inNumberChannels) + return true; + } + return false; +} + +//_____________________________________________________________________________ +// +bool AUPannerBase::ChannelLayoutTagIsSupported( AudioUnitScope inScope, + AudioUnitElement inElement, + AudioChannelLayoutTag inTag) +{ + UInt32 numTags = GetChannelLayoutTags(inScope, inElement, NULL); + CAAutoFree<AudioChannelLayoutTag> tags(numTags); + GetChannelLayoutTags(inScope, inElement, tags()); + + for (UInt32 i = 0; i < numTags; ++i) + { + if (tags[i] == inTag) + return true; + } + + return false; +} + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUPannerBase.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUPannerBase.h new file mode 100644 index 0000000000..3184830523 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/AUPannerBase.h @@ -0,0 +1,272 @@ +/* + File: AUPannerBase.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUPannerBase_h__ +#define __AUPannerBase_h__ + +#include "AUBase.h" +#include <math.h> +#include "CAAutoDisposer.h" +#include "CAAudioChannelLayout.h" + + +/*! @class AUPannerBase */ +class AUPannerBase : public AUBase +{ +public: +/*! @ctor AUPannerBase */ + AUPannerBase(AudioComponentInstance inAudioUnit); +/*! @dtor ~AUPannerBase */ + virtual ~AUPannerBase(); + + /*! @method Initialize */ + virtual OSStatus Initialize(); + + /*! @method Cleanup */ + virtual void Cleanup(); + + + /*! @method Reset */ + virtual OSStatus Reset( AudioUnitScope inScope, + AudioUnitElement inElement); + + /*! @method CanScheduleParameters */ + virtual bool CanScheduleParameters() const { return false; } + + /*! @method GetParameterInfo */ + virtual OSStatus GetParameterInfo( AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + AudioUnitParameterInfo &outParameterInfo ); + + + /*! @method GetPropertyInfo */ + virtual OSStatus GetPropertyInfo (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable); + + /*! @method GetProperty */ + virtual OSStatus GetProperty (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData); + + /*! @method SetProperty */ + virtual OSStatus SetProperty(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize); + + + /*! @method StreamFormatWritable */ + virtual bool StreamFormatWritable (AudioUnitScope scope, + AudioUnitElement element); + + /*! @method ChangeStreamFormat */ + virtual OSStatus ChangeStreamFormat ( + AudioUnitScope inScope, + AudioUnitElement inElement, + const CAStreamBasicDescription & inPrevFormat, + const CAStreamBasicDescription & inNewFormat); + + + /*! @method IsBypassEffect */ + // This is used for the property value - to reflect to the UI if an effect is bypassed + bool IsBypassEffect () { return mBypassEffect; } + + /*! @method SetBypassEffect */ + virtual void SetBypassEffect (bool inFlag) { mBypassEffect = inFlag; } + + + /*! @method Render */ + virtual OSStatus Render(AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inNumberFrames); + + /*! @method Render */ + virtual OSStatus PannerRender(AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inNumberFrames) = 0; + + /*! @method BypassRender */ + virtual OSStatus BypassRender(AudioUnitRenderActionFlags & ioActionFlags, + const AudioTimeStamp & inTimeStamp, + UInt32 inNumberFrames); + + + /*! @method GetAudioChannelLayout */ + virtual UInt32 GetAudioChannelLayout( AudioUnitScope inScope, + AudioUnitElement inElement, + AudioChannelLayout * outLayoutPtr, + Boolean & outWritable); + + /*! @method SetAudioChannelLayout */ + virtual OSStatus SetAudioChannelLayout( AudioUnitScope inScope, + AudioUnitElement inElement, + const AudioChannelLayout * inLayout); + + /*! @method RemoveAudioChannelLayout */ + virtual OSStatus RemoveAudioChannelLayout( AudioUnitScope inScope, + AudioUnitElement inElement); + + /*! @method GetChannelLayoutTags */ + virtual UInt32 GetChannelLayoutTags( AudioUnitScope inScope, + AudioUnitElement inElement, + AudioChannelLayoutTag* outTags); + + /*! @method GetNumberOfInputChannels */ + UInt32 GetNumberOfInputChannels() { return ((AUIOElement*)Inputs().SafeGetElement(0))->NumberChannels(); }; + /*! @method GetNumberOfOutputChannels */ + UInt32 GetNumberOfOutputChannels() { return ((AUIOElement*)Outputs().SafeGetElement(0))->NumberChannels(); } + + /*! @method GetParameter */ + virtual OSStatus GetParameter( AudioUnitParameterID inParamID, + AudioUnitScope inScope, + AudioUnitElement inElement, + Float32 & outValue); + + /*! @method SetParameter */ + virtual OSStatus SetParameter( AudioUnitParameterID inParamID, + AudioUnitScope inScope, + AudioUnitElement inElement, + Float32 inValue, + UInt32 inBufferOffsetInFrames); + + // convenience wrappers for accessing parameters in the global scope + /*! @method SetParameter */ + void SetParameter( UInt32 inParamID, + Float32 inValue) + { + OSStatus err = SetParameter(inParamID, kAudioUnitScope_Global, 0, inValue, 0); + if (err) throw err; + } + + /*! @method GetParameter */ + Float32 GetParameter( UInt32 inParamID ) + { + Float32 outValue = 0.; + OSStatus err = GetParameter(inParamID, kAudioUnitScope_Global, 0, outValue); + if (err) throw err; + return outValue; + } + + /*! @method InputChannelConfigIsSupported */ + bool InputChannelConfigIsSupported(UInt32 inNumberChannels); + /*! @method OutputChannelConfigIsSupported */ + bool OutputChannelConfigIsSupported(UInt32 inNumberChannels); + /*! @method ChannelConfigIsSupported */ + bool ChannelConfigIsSupported(); + + /*! @method SupportsTail */ + virtual bool SupportsTail () { return true; } + /*! @method GetTailTime */ + virtual Float64 GetTailTime() { return 0; } + + + /*! @method GetGain */ + Float32 GetGain() { return GetParameter(kPannerParam_Gain); } + /*! @method GetTailTime */ + Float32 GetAzimuth() { return GetParameter(kPannerParam_Azimuth); } + /*! @method GetElevation */ + Float32 GetElevation() { return GetParameter(kPannerParam_Elevation); } + /*! @method GetDistance */ + Float32 GetDistance() { return GetParameter(kPannerParam_Distance); } + /*! @method GetCoordScale */ + Float32 GetCoordScale() { return GetParameter(kPannerParam_CoordScale); } + /*! @method GetRefDistance */ + Float32 GetRefDistance() { return GetParameter(kPannerParam_RefDistance); } + + /*! @method SetGain */ + void SetGain(Float32 inValue) { SetParameter(kPannerParam_Gain, inValue); } + /*! @method SetAzimuth */ + void SetAzimuth(Float32 inValue) { SetParameter(kPannerParam_Azimuth, inValue); } + /*! @method SetElevation */ + void SetElevation(Float32 inValue) { SetParameter(kPannerParam_Elevation, inValue); } + /*! @method SetDistance */ + void SetDistance(Float32 inValue) { SetParameter(kPannerParam_Distance, inValue); } + /*! @method SetCoordScale */ + void SetCoordScale(Float32 inValue) { SetParameter(kPannerParam_CoordScale, inValue); } + /*! @method SetRefDistance */ + void SetRefDistance(Float32 inValue) { SetParameter(kPannerParam_RefDistance, inValue); } + +protected: + /*! @method ShouldBypassEffect */ + // 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(); } + + /*! @method AllocBypassMatrix */ + void AllocBypassMatrix(); + + /*! @method UpdateBypassMatrix */ + OSStatus UpdateBypassMatrix(); + + /*! @method SetDefaultChannelLayoutsIfNone */ + OSStatus SetDefaultChannelLayoutsIfNone(); + + /*! @method ChannelLayoutTagIsSupported */ + bool ChannelLayoutTagIsSupported( AudioUnitScope inScope, + AudioUnitElement inElement, + AudioChannelLayoutTag inTag); + + const AudioChannelLayout& GetInputLayout() const { return mInputLayout.Layout(); } + const AudioChannelLayout& GetOutputLayout() const { return mOutputLayout.Layout(); } + +private: + + /*! @var UpdateBypassMatrix */ + bool mBypassEffect; + /*! @var mBypassMatrix */ + CAAutoFree<Float32> mBypassMatrix; + /*! @var mInputLayout */ + CAAudioChannelLayout mInputLayout; + /*! @var mOutputLayout */ + CAAudioChannelLayout mOutputLayout; +}; + +#endif /* __AUPannerBase_h__ */ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/MusicDeviceBase.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/MusicDeviceBase.cpp new file mode 100644 index 0000000000..28e4d366bc --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/MusicDeviceBase.cpp @@ -0,0 +1,354 @@ +/* + File: MusicDeviceBase.cpp + Abstract: MusicDeviceBase.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "MusicDeviceBase.h" + +// compatibility with older OS SDK releases +typedef OSStatus +(*TEMP_MusicDeviceMIDIEventProc)( void * inComponentStorage, + UInt32 inStatus, + UInt32 inData1, + UInt32 inData2, + UInt32 inOffsetSampleFrame); + +typedef OSStatus +(*TEMP_MusicDeviceStartNoteProc)( void * inComponentStorage, + MusicDeviceInstrumentID inInstrument, + MusicDeviceGroupID inGroupID, + NoteInstanceID * outNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams * inParams); + +typedef OSStatus +(*TEMP_MusicDeviceStopNoteProc)(void * inComponentStorage, + MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame); + +#if !CA_USE_AUDIO_PLUGIN_ONLY + +static OSStatus MusicDeviceBaseMIDIEvent(void * inComponentStorage, + UInt32 inStatus, + UInt32 inData1, + UInt32 inData2, + UInt32 inOffsetSampleFrame); + +static OSStatus MusicDeviceBaseStartNote( void * inComponentStorage, + MusicDeviceInstrumentID inInstrument, + MusicDeviceGroupID inGroupID, + NoteInstanceID * outNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams * inParams); + +static OSStatus MusicDeviceBaseStopNote(void * inComponentStorage, + MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame); + +#endif + +MusicDeviceBase::MusicDeviceBase(AudioComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups) + : AUBase(inInstance, numInputs, numOutputs, numGroups), + AUMIDIBase(this) +{ +} + +OSStatus MusicDeviceBase::GetPropertyInfo(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable) +{ + OSStatus result; + + switch (inID) + { +#if !TARGET_OS_IPHONE + case kMusicDeviceProperty_InstrumentCount: + if (inScope != kAudioUnitScope_Global) return kAudioUnitErr_InvalidScope; + outDataSize = sizeof(UInt32); + outWritable = false; + result = noErr; + break; +#endif + 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; + + switch (inID) + { +#if !CA_USE_AUDIO_PLUGIN_ONLY + case kAudioUnitProperty_FastDispatch: + if (!IsCMgrObject()) return kAudioUnitErr_InvalidProperty; + if (inElement == kMusicDeviceMIDIEventSelect) { + *(TEMP_MusicDeviceMIDIEventProc *)outData = MusicDeviceBaseMIDIEvent; + return noErr; + } + else if (inElement == kMusicDeviceStartNoteSelect) { + *(TEMP_MusicDeviceStartNoteProc *)outData = MusicDeviceBaseStartNote; + return noErr; + } + else if (inElement == kMusicDeviceStopNoteSelect) { + *(TEMP_MusicDeviceStopNoteProc *)outData = MusicDeviceBaseStopNote; + return noErr; + } + return kAudioUnitErr_InvalidElement; +#endif + +#if !TARGET_OS_IPHONE + case kMusicDeviceProperty_InstrumentCount: + if (inScope != kAudioUnitScope_Global) return kAudioUnitErr_InvalidScope; + return GetInstrumentCount (*(UInt32*)outData); +#endif + 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) +{ + MusicDeviceNoteParams params; + params.argCount = 2; + params.mPitch = inNoteNumber; + params.mVelocity = inVelocity; + return StartNote (kMusicNoteEvent_UseGroupInstrument, inChannel, NULL, 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 == NULL || outNoteInstanceID == NULL) return kAudio_ParamError; + + if (!IsInitialized()) return kAudioUnitErr_Uninitialized; + + return StartNote (inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, *inParams); +} + +#if TARGET_OS_MAC + #if __LP64__ + // comp instance, parameters in forward order + #define PARAM(_typ, _name, _index, _nparams) \ + _typ _name = *(_typ *)¶ms->params[_index + 1]; + #else + // parameters in reverse order, then comp instance + #define PARAM(_typ, _name, _index, _nparams) \ + _typ _name = *(_typ *)¶ms->params[_nparams - 1 - _index]; + #endif +#elif TARGET_OS_WIN32 + // (no comp instance), parameters in forward order + #define PARAM(_typ, _name, _index, _nparams) \ + _typ _name = *(_typ *)¶ms->params[_index]; +#endif + +#if !CA_USE_AUDIO_PLUGIN_ONLY +OSStatus MusicDeviceBase::ComponentEntryDispatch( ComponentParameters * params, + MusicDeviceBase * This) +{ + if (This == NULL) return kAudio_ParamError; + + OSStatus result; + + switch (params->what) { + case kMusicDeviceMIDIEventSelect: + case kMusicDeviceSysExSelect: + { + result = AUMIDIBase::ComponentEntryDispatch (params, This); + } + break; + case kMusicDevicePrepareInstrumentSelect: + { + PARAM(MusicDeviceInstrumentID, inInstrument, 0, 1); + result = This->PrepareInstrument(inInstrument); + } + break; + case kMusicDeviceReleaseInstrumentSelect: + { + PARAM(MusicDeviceInstrumentID, inInstrument, 0, 1); + result = This->ReleaseInstrument(inInstrument); + } + break; + case kMusicDeviceStartNoteSelect: + { + PARAM(MusicDeviceInstrumentID, pbinInstrument, 0, 5); + PARAM(MusicDeviceGroupID, pbinGroupID, 1, 5); + PARAM(NoteInstanceID *, pboutNoteInstanceID, 2, 5); + PARAM(UInt32, pbinOffsetSampleFrame, 3, 5); + PARAM(const MusicDeviceNoteParams *, pbinParams, 4, 5); + result = This->HandleStartNoteMessage(pbinInstrument, pbinGroupID, pboutNoteInstanceID, pbinOffsetSampleFrame, pbinParams); + } + break; + case kMusicDeviceStopNoteSelect: + { + PARAM(MusicDeviceGroupID, pbinGroupID, 0, 3); + PARAM(NoteInstanceID, pbinNoteInstanceID, 1, 3); + PARAM(UInt32, pbinOffsetSampleFrame, 2, 3); + result = This->StopNote(pbinGroupID, pbinNoteInstanceID, pbinOffsetSampleFrame); + } + break; + + default: + result = AUBase::ComponentEntryDispatch(params, This); + break; + } + + return result; +} +#endif + +#if !CA_USE_AUDIO_PLUGIN_ONLY + +// fast dispatch +static OSStatus MusicDeviceBaseMIDIEvent(void * inComponentStorage, + UInt32 inStatus, + UInt32 inData1, + UInt32 inData2, + UInt32 inOffsetSampleFrame) +{ + OSStatus result = noErr; + try { + MusicDeviceBase *This = static_cast<MusicDeviceBase *>(inComponentStorage); + if (This == NULL) return kAudio_ParamError; + result = This->MIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame); + } + COMPONENT_CATCH + return result; +} + +OSStatus MusicDeviceBaseStartNote( void * inComponentStorage, + MusicDeviceInstrumentID inInstrument, + MusicDeviceGroupID inGroupID, + NoteInstanceID * outNoteInstanceID, + UInt32 inOffsetSampleFrame, + const MusicDeviceNoteParams * inParams) +{ + OSStatus result = noErr; + try { + if (inParams == NULL || outNoteInstanceID == NULL) return kAudio_ParamError; + MusicDeviceBase *This = static_cast<MusicDeviceBase *>(inComponentStorage); + if (This == NULL) return kAudio_ParamError; + result = This->StartNote(inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, *inParams); + } + COMPONENT_CATCH + return result; +} + +OSStatus MusicDeviceBaseStopNote(void * inComponentStorage, + MusicDeviceGroupID inGroupID, + NoteInstanceID inNoteInstanceID, + UInt32 inOffsetSampleFrame) +{ + OSStatus result = noErr; + try { + MusicDeviceBase *This = static_cast<MusicDeviceBase *>(inComponentStorage); + if (This == NULL) return kAudio_ParamError; + result = This->StopNote(inGroupID, inNoteInstanceID, inOffsetSampleFrame); + } + COMPONENT_CATCH + return result; +} + +#endif diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/MusicDeviceBase.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/MusicDeviceBase.h new file mode 100644 index 0000000000..4850b8f0cb --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/OtherBases/MusicDeviceBase.h @@ -0,0 +1,126 @@ +/* + File: MusicDeviceBase.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __MusicDeviceBase_h__ +#define __MusicDeviceBase_h__ + +#include "AUMIDIBase.h" + +// ________________________________________________________________________ +// MusicDeviceBase +// + +/*! @class MusicDeviceBase */ +class MusicDeviceBase : public AUBase, public AUMIDIBase { +public: + /*! @ctor MusicDeviceBase */ + MusicDeviceBase( AudioComponentInstance inInstance, + UInt32 numInputs, + UInt32 numOutputs, + UInt32 numGroups = 0); + + + virtual OSStatus MIDIEvent( UInt32 inStatus, + UInt32 inData1, + UInt32 inData2, + UInt32 inOffsetSampleFrame) + { + return AUMIDIBase::MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame); + } + + /*! @method SysEx */ + virtual OSStatus SysEx( const UInt8 * inData, + UInt32 inLength) + { + return AUMIDIBase::SysEx (inData, inLength); + } + + /*! @method GetPropertyInfo */ + virtual OSStatus GetPropertyInfo(AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32 & outDataSize, + Boolean & outWritable); + + /*! @method GetProperty */ + virtual OSStatus GetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void * outData); + + /*! @method SetProperty */ + virtual OSStatus SetProperty( AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void * inData, + UInt32 inDataSize); + + /*! @method HandleNoteOn */ + virtual OSStatus HandleNoteOn( UInt8 inChannel, + UInt8 inNoteNumber, + UInt8 inVelocity, + UInt32 inStartFrame); + + /*! @method HandleNoteOff */ + virtual OSStatus HandleNoteOff( UInt8 inChannel, + UInt8 inNoteNumber, + UInt8 inVelocity, + UInt32 inStartFrame); + + /*! @method GetInstrumentCount */ + virtual OSStatus GetInstrumentCount ( UInt32 &outInstCount) const; + +#if !CA_USE_AUDIO_PLUGIN_ONLY + // component dispatcher + /*! @method ComponentEntryDispatch */ + static OSStatus ComponentEntryDispatch( ComponentParameters * params, + MusicDeviceBase * This); +#endif +private: + OSStatus HandleStartNoteMessage (MusicDeviceInstrumentID inInstrument, MusicDeviceGroupID inGroupID, NoteInstanceID *outNoteInstanceID, UInt32 inOffsetSampleFrame, const MusicDeviceNoteParams *inParams); +}; + +#endif // __MusicDeviceBase_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBaseHelper.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBaseHelper.cpp new file mode 100644 index 0000000000..2316807138 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBaseHelper.cpp @@ -0,0 +1,125 @@ +/* + File: AUBaseHelper.cpp + Abstract: AUBaseHelper.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUBaseHelper.h" + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include <AudioUnit/AudioUnitProperties.h> +#else + #include <AudioUnitProperties.h> +#endif + +OSStatus GetFileRefPath (CFDictionaryRef parent, CFStringRef frKey, CFStringRef * fPath) +{ + static CFStringRef kFRString = CFSTR (kAUPresetExternalFileRefs); + + const void* frVal = CFDictionaryGetValue(parent, kFRString); + if (!frVal) return kAudioUnitErr_InvalidPropertyValue; + + const void* frString = CFDictionaryGetValue ((CFDictionaryRef)frVal, frKey); + if (!frString) return kAudioUnitErr_InvalidPropertyValue; + + if (fPath) + *fPath = (CFStringRef)frString; + + return noErr; +} + +CFMutableDictionaryRef CreateFileRefDict (CFStringRef fKey, CFStringRef fPath, CFMutableDictionaryRef fileRefDict) +{ + if (!fileRefDict) + fileRefDict = CFDictionaryCreateMutable (NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFDictionarySetValue (fileRefDict, fKey, fPath); + + return fileRefDict; +} + +#if TARGET_OS_MAC +// check if the URL can be accessed for reading/writing. Returns 0 if yes, or the error value. +int AccessURLAsset(const CFURLRef inURL, int mode) +{ + char path[PATH_MAX]; + if (CFURLGetFileSystemRepresentation(inURL, TRUE, (UInt8 *)path, PATH_MAX) == FALSE) + return kAudio_FileNotFoundError; + // check whether we have access + int ret = access(path, mode); +// syslog(LOG_CRIT, "access() error is %d for \"%s\".\n", ret, path); + if (ret == 0) return 0; + switch (errno) { + case EACCES: + case EPERM: + return -54; /*permission denied error*/ + case ENOENT: + case ENOTDIR: + case ELOOP: + return kAudio_FileNotFoundError; + default: + return errno; + } +} +#endif + +#if DEBUG +//_____________________________________________________________________________ +// +void PrintAUParamEvent (AudioUnitParameterEvent& event, FILE* f) +{ + bool isRamp = event.eventType == kParameterEvent_Ramped; + fprintf (f, "\tParamID=%ld,Scope=%ld,Element=%ld\n", (long)event.parameter, (long)event.scope, (long)event.element); + fprintf (f, "\tEvent Type:%s,", (isRamp ? "ramp" : "immediate")); + if (isRamp) + fprintf (f, "start=%ld,dur=%ld,startValue=%f,endValue=%f\n", + (long)event.eventValues.ramp.startBufferOffset, (long)event.eventValues.ramp.durationInFrames, + event.eventValues.ramp.startValue, event.eventValues.ramp.endValue); + else + fprintf (f, "start=%ld,value=%f\n", + (long)event.eventValues.immediate.bufferOffset, + event.eventValues.immediate.value); + fprintf (f, "- - - - - - - - - - - - - - - -\n"); +} +#endif + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBaseHelper.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBaseHelper.h new file mode 100644 index 0000000000..310a8df620 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBaseHelper.h @@ -0,0 +1,75 @@ +/* + File: AUBaseHelper.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUBaseHelper_h__ +#define __AUBaseHelper_h__ + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include <CoreFoundation/CoreFoundation.h> + #include <AudioUnit/AUComponent.h> +#else + #include <CoreFoundation.h> + #include <AUComponent.h> +#endif + +#include "AUBase.h" + +// helpers for dealing with the file-references dictionary in an AUPreset +OSStatus GetFileRefPath (CFDictionaryRef parent, CFStringRef frKey, CFStringRef * fPath); + +// if fileRefDict is NULL, this call creates one +// if not NULL, then the key value is added to it +CFMutableDictionaryRef CreateFileRefDict (CFStringRef fKey, CFStringRef fPath, CFMutableDictionaryRef fileRefDict); + +int AccessURLAsset(const CFURLRef inURL, int mode); + +#if DEBUG + void PrintAUParamEvent (AudioUnitParameterEvent& event, FILE* f); +#endif + + + +#endif // __AUBaseHelper_h__
\ No newline at end of file diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBuffer.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBuffer.cpp new file mode 100644 index 0000000000..36bb41fa58 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBuffer.cpp @@ -0,0 +1,219 @@ +/* + File: AUBuffer.cpp + Abstract: AUBuffer.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUBuffer.h" +#include <stdlib.h> + +AUBufferList::~AUBufferList() +{ + Deallocate(); + if (mPtrs) + free(mPtrs); +} + +// 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) + throw std::bad_alloc(); + + return a * b + c; +} + +void AUBufferList::Allocate(const CAStreamBasicDescription &format, UInt32 nFrames) +{ + UInt32 nStreams; + if (format.IsInterleaved()) { + nStreams = 1; + } else { + nStreams = format.mChannelsPerFrame; + } + + // careful -- the I/O thread could be running! + if (nStreams > mAllocatedStreams) { + size_t theHeaderSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); + mPtrs = (AudioBufferList *)CA_realloc(mPtrs, + SafeMultiplyAddUInt32(nStreams, sizeof(AudioBuffer), theHeaderSize)); + mAllocatedStreams = nStreams; + } + UInt32 bytesPerStream = SafeMultiplyAddUInt32(nFrames, format.mBytesPerFrame, 0xF) & ~0xF; + UInt32 nBytes = SafeMultiplyAddUInt32(nStreams, bytesPerStream, 0); + if (nBytes > mAllocatedBytes) { + if (mExternalMemory) { + mExternalMemory = false; + mMemory = NULL; + } + mMemory = (Byte *)CA_realloc(mMemory, nBytes); + mAllocatedBytes = nBytes; + } + mAllocatedFrames = nFrames; + mPtrState = kPtrsInvalid; +} + +void AUBufferList::Deallocate() +{ + mAllocatedStreams = 0; + mAllocatedFrames = 0; + mAllocatedBytes = 0; +// this causes a world of hurt if someone upstream disconnects during I/O (SysSoundGraph) +/* if (mPtrs) { + printf("deallocating bufferlist %08X\n", int(mPtrs)); + free(mPtrs); + mPtrs = NULL; + } */ + if (mMemory) { + if (mExternalMemory) + mExternalMemory = false; + else + free(mMemory); + mMemory = NULL; + } + mPtrState = kPtrsInvalid; +} + +AudioBufferList & AUBufferList::PrepareBuffer(const CAStreamBasicDescription &format, UInt32 nFrames) +{ + if (nFrames > mAllocatedFrames) + COMPONENT_THROW(kAudioUnitErr_TooManyFramesToProcess); + + UInt32 nStreams; + UInt32 channelsPerStream; + if (format.IsInterleaved()) { + nStreams = 1; + channelsPerStream = format.mChannelsPerFrame; + } else { + nStreams = format.mChannelsPerFrame; + channelsPerStream = 1; + if (nStreams > mAllocatedStreams) + COMPONENT_THROW(kAudioUnitErr_FormatNotSupported); + } + + AudioBufferList *abl = mPtrs; + abl->mNumberBuffers = nStreams; + AudioBuffer *buf = abl->mBuffers; + Byte *mem = mMemory; + UInt32 streamInterval = (mAllocatedFrames * format.mBytesPerFrame + 0xF) & ~0xF; + UInt32 bytesPerBuffer = nFrames * format.mBytesPerFrame; + for ( ; nStreams--; ++buf) { + buf->mNumberChannels = channelsPerStream; + buf->mData = mem; + buf->mDataByteSize = bytesPerBuffer; + mem += streamInterval; + } + if (UInt32(mem - mMemory) > mAllocatedBytes) + COMPONENT_THROW(kAudioUnitErr_TooManyFramesToProcess); + mPtrState = kPtrsToMyMemory; + return *mPtrs; +} + +AudioBufferList & AUBufferList::PrepareNullBuffer(const CAStreamBasicDescription &format, UInt32 nFrames) +{ + UInt32 nStreams; + UInt32 channelsPerStream; + if (format.IsInterleaved()) { + nStreams = 1; + channelsPerStream = format.mChannelsPerFrame; + } else { + nStreams = format.mChannelsPerFrame; + channelsPerStream = 1; + if (nStreams > mAllocatedStreams) + COMPONENT_THROW(kAudioUnitErr_FormatNotSupported); + } + AudioBufferList *abl = mPtrs; + abl->mNumberBuffers = nStreams; + AudioBuffer *buf = abl->mBuffers; + UInt32 bytesPerBuffer = nFrames * format.mBytesPerFrame; + for ( ; nStreams--; ++buf) { + buf->mNumberChannels = channelsPerStream; + buf->mData = NULL; + buf->mDataByteSize = bytesPerBuffer; + } + mPtrState = kPtrsToExternalMemory; + return *mPtrs; +} + +// this should NOT be called while I/O is in process +void AUBufferList::UseExternalBuffer(const CAStreamBasicDescription &format, const AudioUnitExternalBuffer &buf) +{ + UInt32 alignedSize = buf.size & ~0xF; + if (mMemory != NULL && alignedSize >= mAllocatedBytes) { + // don't accept the buffer if we already have one and it's big enough + // if we don't already have one, we don't need one + Byte *oldMemory = mMemory; + mMemory = buf.buffer; + mAllocatedBytes = alignedSize; + // from Allocate(): nBytes = nStreams * nFrames * format.mBytesPerFrame; + // thus: nFrames = nBytes / (nStreams * format.mBytesPerFrame) + mAllocatedFrames = mAllocatedBytes / (format.NumberChannelStreams() * format.mBytesPerFrame); + mExternalMemory = true; + free(oldMemory); + } +} + +#if DEBUG +void AUBufferList::PrintBuffer(const char *label, int subscript, const AudioBufferList &abl, UInt32 nFrames, bool asFloats) +{ + printf(" %s [%d] 0x%08lX:\n", label, subscript, long(&abl)); + const AudioBuffer *buf = abl.mBuffers; + for (UInt32 i = 0; i < abl.mNumberBuffers; ++buf, ++i) { + printf(" [%2d] %5dbytes %dch @ %p: ", (int)i, (int)buf->mDataByteSize, (int)buf->mNumberChannels, buf->mData); + if (buf->mData != NULL) { + UInt32 nSamples = nFrames * buf->mNumberChannels; + for (UInt32 j = 0; j < nSamples; ++j) { + if (nSamples > 16 && (j % 16) == 0) + printf("\n\t"); + if (asFloats) + printf(" %6.3f", ((float *)buf->mData)[j]); + else + printf(" %08X", (unsigned)((UInt32 *)buf->mData)[j]); + } + } + printf("\n"); + } +} +#endif diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBuffer.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBuffer.h new file mode 100644 index 0000000000..64ea4c3971 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUBuffer.h @@ -0,0 +1,267 @@ +/* + File: AUBuffer.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUBuffer_h__ +#define __AUBuffer_h__ + +#include <TargetConditionals.h> +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include <AudioUnit/AudioUnit.h> +#else + #include <AudioUnit.h> +#endif + +#include <string.h> +#include "CAStreamBasicDescription.h" +#include "CAAutoDisposer.h" +#include "CADebugMacros.h" + +// make this usable outside the stricter context of AudiUnits +#ifndef COMPONENT_THROW + #define COMPONENT_THROW(err) \ + do { DebugMessage(#err); throw static_cast<OSStatus>(err); } while (0) +#endif + + + /*! @class AUBufferList */ +class AUBufferList { + enum EPtrState { + kPtrsInvalid, + kPtrsToMyMemory, + kPtrsToExternalMemory + }; +public: + /*! @ctor AUBufferList */ + AUBufferList() : mPtrState(kPtrsInvalid), mExternalMemory(false), mPtrs(NULL), mMemory(NULL), + mAllocatedStreams(0), mAllocatedFrames(0), mAllocatedBytes(0) { } + /*! @dtor ~AUBufferList */ + ~AUBufferList(); + + /*! @method PrepareBuffer */ + AudioBufferList & PrepareBuffer(const CAStreamBasicDescription &format, UInt32 nFrames); + /*! @method PrepareNullBuffer */ + AudioBufferList & PrepareNullBuffer(const CAStreamBasicDescription &format, UInt32 nFrames); + + /*! @method SetBufferList */ + AudioBufferList & SetBufferList(const AudioBufferList &abl) { + if (mAllocatedStreams < abl.mNumberBuffers) + COMPONENT_THROW(-1); + mPtrState = kPtrsToExternalMemory; + memcpy(mPtrs, &abl, (char *)&abl.mBuffers[abl.mNumberBuffers] - (char *)&abl); + return *mPtrs; + } + + /*! @method SetBuffer */ + void SetBuffer(UInt32 index, const AudioBuffer &ab) { + if (mPtrState == kPtrsInvalid || index >= mPtrs->mNumberBuffers) + COMPONENT_THROW(-1); + mPtrState = kPtrsToExternalMemory; + mPtrs->mBuffers[index] = ab; + } + + /*! @method InvalidateBufferList */ + void InvalidateBufferList() { mPtrState = kPtrsInvalid; } + + /*! @method GetBufferList */ + AudioBufferList & GetBufferList() const { + if (mPtrState == kPtrsInvalid) + COMPONENT_THROW(-1); + return *mPtrs; + } + + /*! @method CopyBufferListTo */ + void CopyBufferListTo(AudioBufferList &abl) const { + if (mPtrState == kPtrsInvalid) + COMPONENT_THROW(-1); + memcpy(&abl, mPtrs, (char *)&abl.mBuffers[abl.mNumberBuffers] - (char *)&abl); + } + + /*! @method CopyBufferContentsTo */ + void CopyBufferContentsTo(AudioBufferList &abl) const { + if (mPtrState == kPtrsInvalid) + COMPONENT_THROW(-1); + const AudioBuffer *srcbuf = mPtrs->mBuffers; + AudioBuffer *destbuf = abl.mBuffers; + + for (UInt32 i = 0; i < abl.mNumberBuffers; ++i, ++srcbuf, ++destbuf) { + if (i >= mPtrs->mNumberBuffers) // duplicate last source to additional outputs [4341137] + --srcbuf; + if (destbuf->mData != srcbuf->mData) + memmove(destbuf->mData, srcbuf->mData, srcbuf->mDataByteSize); + destbuf->mDataByteSize = srcbuf->mDataByteSize; + } + } + + /*! @method Allocate */ + void Allocate(const CAStreamBasicDescription &format, UInt32 nFrames); + /*! @method Deallocate */ + void Deallocate(); + + /*! @method UseExternalBuffer */ + void UseExternalBuffer(const CAStreamBasicDescription &format, const AudioUnitExternalBuffer &buf); + + // AudioBufferList utilities + /*! @method ZeroBuffer */ + static void ZeroBuffer(AudioBufferList &abl) { + AudioBuffer *buf = abl.mBuffers; + for (UInt32 i = abl.mNumberBuffers ; i--; ++buf) + memset(buf->mData, 0, buf->mDataByteSize); + } +#if DEBUG + /*! @method PrintBuffer */ + static void PrintBuffer(const char *label, int subscript, const AudioBufferList &abl, UInt32 nFrames = 8, bool asFloats = true); +#endif + + /*! @method GetAllocatedFrames */ + UInt32 GetAllocatedFrames() const { return mAllocatedFrames; } + +private: + /*! @ctor AUBufferList */ + AUBufferList(AUBufferList &) { } // prohibit copy constructor + + /*! @var mPtrState */ + EPtrState mPtrState; + /*! @var mExternalMemory */ + bool mExternalMemory; + /*! @var mPtrs */ + AudioBufferList * mPtrs; + /*! @var mMemory */ + Byte * mMemory; + /*! @var mAllocatedStreams */ + UInt32 mAllocatedStreams; + /*! @var mAllocatedFrames */ + UInt32 mAllocatedFrames; + /*! @var mAllocatedBytes */ + UInt32 mAllocatedBytes; +}; + + +// Allocates an array of samples (type T), to be optimally aligned for the processor + /*! @class TAUBuffer */ +template <class T> +class TAUBuffer { +public: + enum { + kAlignInterval = 0x10, + kAlignMask = kAlignInterval - 1 + }; + + /*! @ctor TAUBuffer.0 */ + TAUBuffer() : mMemObject(NULL), mAlignedBuffer(NULL), mBufferSizeBytes(0) + { + } + + /*! @ctor TAUBuffer.1 */ + TAUBuffer(UInt32 numElems, UInt32 numChannels) : mMemObject(NULL), mAlignedBuffer(NULL), + mBufferSizeBytes(0) + { + Allocate(numElems, numChannels); + } + + /*! @dtor ~TAUBuffer */ + ~TAUBuffer() + { + Deallocate(); + } + + /*! @method Allocate */ + void Allocate(UInt32 numElems) // can also re-allocate + { + UInt32 reqSize = numElems * sizeof(T); + + if (mMemObject != NULL && reqSize == mBufferSizeBytes) + return; // already allocated + + mBufferSizeBytes = reqSize; + mMemObject = CA_realloc(mMemObject, reqSize); + UInt32 misalign = (uintptr_t)mMemObject & kAlignMask; + if (misalign) { + mMemObject = CA_realloc(mMemObject, reqSize + kAlignMask); + mAlignedBuffer = (T *)((char *)mMemObject + kAlignInterval - misalign); + } else + mAlignedBuffer = (T *)mMemObject; + } + + /*! @method Deallocate */ + void Deallocate() + { + if (mMemObject == NULL) return; // so this method has no effect if we're using + // an external buffer + + free(mMemObject); + mMemObject = NULL; + mAlignedBuffer = NULL; + mBufferSizeBytes = 0; + } + + /*! @method AllocateClear */ + void AllocateClear(UInt32 numElems) // can also re-allocate + { + Allocate(numElems); + Clear(); + } + + /*! @method Clear */ + void Clear() + { + memset(mAlignedBuffer, 0, mBufferSizeBytes); + } + + // accessors + + /*! @method operator T *()@ */ + operator T *() { return mAlignedBuffer; } + +private: + /*! @var mMemObject */ + void * mMemObject; // null when using an external buffer + /*! @var mAlignedBuffer */ + T * mAlignedBuffer; // always valid once allocated + /*! @var mBufferSizeBytes */ + UInt32 mBufferSizeBytes; +}; + +#endif // __AUBuffer_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUInputFormatConverter.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUInputFormatConverter.h new file mode 100644 index 0000000000..f628a915e1 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUInputFormatConverter.h @@ -0,0 +1,155 @@ +/* + File: AUInputFormatConverter.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUInputFormatConverter_h__ +#define __AUInputFormatConverter_h__ + +#include "FormatConverterClient.h" +#include "AUTimestampGenerator.h" + +// ____________________________________________________________________________ +// AUInputFormatConverter +// +// Subclass of FormatConverterClient that applies a format conversion +// to an input of an AudioUnit. + /*! @class AUInputFormatConverter */ +class AUInputFormatConverter : public FormatConverterClient { +public: + /*! @ctor AUInputFormatConverter */ + AUInputFormatConverter(AUBase *hostAU, int inputBus) : + mHost(hostAU), + mHostBus(inputBus), + mPreviousSilentFrames(0x1000) + { +#if DEBUG + mTimestampGenerator.mVerbosity = 0; + strcpy(mTimestampGenerator.mDebugName, "AUConverter"); +#endif + } + + // need to subsequently call Initialize, with the desired formats + + /*! @dtor ~AUInputFormatConverter */ + ~AUInputFormatConverter() + { + } + + virtual OSStatus Initialize(const AudioStreamBasicDescription &src, const AudioStreamBasicDescription &dest) + { + OSStatus err = FormatConverterClient::Initialize(src, dest); + if (err) return err; + mIsPCMToPCM = (src.mFormatID == kAudioFormatLinearPCM) && (dest.mFormatID == kAudioFormatLinearPCM); + mHasSRC = (fnonzero(src.mSampleRate) && fnonzero(dest.mSampleRate) && fnotequal(src.mSampleRate, dest.mSampleRate)); + return ca_noErr; + } + + virtual OSStatus Reset() + { + mPreviousSilentFrames = 0x1000; + mTimestampGenerator.Reset(); + return FormatConverterClient::Reset(); + } + + void SetStartInputTimeAtZero(bool b) + { + mTimestampGenerator.SetStartInputAtZero(b); + } + + /*! @method FillComplexBuffer */ + OSStatus AUFillComplexBuffer(const AudioTimeStamp & inTimeStamp, + UInt32 & ioOutputDataPacketSize, + AudioBufferList & outOutputData, + AudioStreamPacketDescription* outPacketDescription, + bool& outSilence) + { + mTimestampGenerator.AddOutputTime(inTimeStamp, ioOutputDataPacketSize, mOutputFormat.mSampleRate); + mSilentOutput = true; + OSStatus err = FillComplexBuffer(ioOutputDataPacketSize, outOutputData, outPacketDescription); + if (mSilentOutput) { + if (!mIsPCMToPCM || (mHasSRC && mPreviousSilentFrames < 32)) + mSilentOutput = false; + mPreviousSilentFrames += ioOutputDataPacketSize; + } else + mPreviousSilentFrames = 0; + outSilence = mSilentOutput; + return err; + } + + /*! @method FormatConverterInputProc */ + virtual OSStatus FormatConverterInputProc( + UInt32 & ioNumberDataPackets, + AudioBufferList & ioData, + AudioStreamPacketDescription** outDataPacketDescription) + { + OSStatus err = ca_noErr; + + AudioUnitRenderActionFlags actionFlags = 0; + AUInputElement *input = mHost->GetInput(mHostBus); + *ioNumberDataPackets = std::min(*ioNumberDataPackets, This->mHost->GetMaxFramesPerSlice()); + const AudioTimeStamp &inputTime = mTimestampGenerator.GenerateInputTime(ioNumberDataPackets, mInputFormat.mSampleRate); + err = input->PullInput(actionFlags, inputTime, mHostBus, ioNumberDataPackets); + if (!err) { + input->CopyBufferListTo(ioData); + if (!(actionFlags & kAudioUnitRenderAction_OutputIsSilence)) + mSilentOutput = false; + } + return err; + } + +protected: + /*! @var mHost */ + AUBase * mHost; + /*! @var mHostBus */ + int mHostBus; + + AUTimestampGenerator mTimestampGenerator; + bool mIsPCMToPCM; + bool mHasSRC; + bool mSilentOutput; + UInt32 mPreviousSilentFrames; +}; + +#endif // __AUInputFormatConverter_h__ diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUMIDIDefs.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUMIDIDefs.h new file mode 100644 index 0000000000..e83c0e7e8d --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUMIDIDefs.h @@ -0,0 +1,138 @@ +/* + File: AUMIDIDefs.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUMIDIDefs_h__ +#define __AUMIDIDefs_h__ + +#if !defined(__TMidiMessage) /* DAS HACK */ +enum +{ + kMidiMessage_NoteOff = 0x80, + kMidiMessage_NoteOn = 0x90, + kMidiMessage_PolyPressure = 0xA0, + kMidiMessage_ControlChange = 0xB0, + kMidiMessage_ProgramChange = 0xC0, + kMidiMessage_ChannelPressure = 0xD0, + kMidiMessage_PitchWheel = 0xE0, + kMidiMessage_SysEx = 0xF0, + kMidiMessage_SysEx_End = 0xF7, + kMidiMessage_MetaEvent = 0xFF +}; +#endif + +enum +{ + kMidiController_BankSelect = 0, + kMidiController_ModWheel = 1, + kMidiController_Breath = 2, + kMidiController_Foot = 4, + kMidiController_PortamentoTime = 5, + kMidiController_DataEntry = 6, + kMidiController_Volume = 7, + kMidiController_Balance = 8, + kMidiController_Pan = 10, + kMidiController_Expression = 11, + + // these controls have a (0-63) == off, (64-127) == on + kMidiController_Sustain = 64, //hold1 + kMidiController_Portamento = 65, + kMidiController_Sostenuto = 66, + kMidiController_Soft = 67, + kMidiController_LegatoPedal = 68, + kMidiController_Hold2Pedal = 69, + kMidiController_FilterResonance = 71, + kMidiController_ReleaseTime = 72, + kMidiController_AttackTime = 73, + kMidiController_Brightness = 74, + kMidiController_DecayTime = 75, + kMidiController_VibratoRate = 76, + kMidiController_VibratoDepth = 77, + kMidiController_VibratoDelay = 78, + + // these controls have a 0-127 range and in MIDI they have no LSB (so fractional values are lost in MIDI) + kMidiController_ReverbLevel = 91, + kMidiController_ChorusLevel = 93, + + kMidiController_RPN_LSB = 100, + kMidiController_RPN_MSB = 101, + + kMidiController_AllSoundOff = 120, + kMidiController_ResetAllControllers = 121, + kMidiController_AllNotesOff = 123, + kMidiController_OmniModeOff = 124, + kMidiController_OmniModeOn = 125, + kMidiController_MonoModeOn = 126, + kMidiController_MonoModeOff = 127 +}; + +// RPN values +enum +{ + kMidiControllerValue_RPNPitchBendSensitivity = 0, + kMidiControllerValue_RPNChannelFineTuning = 1, + kMidiControllerValue_RPNChannelCoarseTuning = 2, + kMidiControllerValue_RPNModDepthRange = 5, + kMidiControllerValue_RPNNull = 0x3fff //! 0x7f/0x7f +}; + +// GM2 Sound Bank Constants +enum +{ + kGM2MelodicBank = 0x7900, + kGM2PercussionBank = 0x7800, + kGSPercussionBank = 0x7f00, + kXGSFXBank = 0x7E00, + kXGPercussionBank = kGSPercussionBank, + kBankMSBMask = 0xff00 +}; + +enum +{ + kMSBController_MidPoint = 0x40 +}; + +#endif // __AUMIDIDefs_h__ + diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUSilentTimeout.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUSilentTimeout.h new file mode 100644 index 0000000000..9fbd3e1f23 --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUSilentTimeout.h @@ -0,0 +1,93 @@ +/* + File: AUSilentTimeout.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUSilentTimeout +#define __AUSilentTimeout + +class AUSilentTimeout +{ +public: + AUSilentTimeout() + : mTimeoutCounter(0), + mResetTimer(true) + {}; + + void Process(UInt32 inFramesToProcess, UInt32 inTimeoutLimit, bool &ioSilence ) + { + if(ioSilence ) + { + if(mResetTimer ) + { + mTimeoutCounter = inTimeoutLimit; + mResetTimer = false; + } + + if(mTimeoutCounter > 0 ) + { + mTimeoutCounter -= inFramesToProcess; + ioSilence = false; + } + } + else + { + // signal to reset the next time we receive silence + mResetTimer = true; + } + } + + void Reset() + { + mResetTimer = true; + }; + + + +private: + SInt32 mTimeoutCounter; + bool mResetTimer; +}; + +#endif // __AUSilentTimeout diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUTimestampGenerator.cpp b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUTimestampGenerator.cpp new file mode 100644 index 0000000000..7c57a81ede --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUTimestampGenerator.cpp @@ -0,0 +1,203 @@ +/* + File: AUTimestampGenerator.cpp + Abstract: AUTimestampGenerator.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "AUTimestampGenerator.h" +#include "CAMath.h" + +#if DEBUG +static double DebugHostTime(const AudioTimeStamp &ts) +{ + static UInt64 baseHostTime = 0; + if (!(ts.mFlags & kAudioTimeStampHostTimeValid)) + return -1.; + if (baseHostTime == 0) + baseHostTime = ts.mHostTime; + return double(SInt64(ts.mHostTime) - SInt64(baseHostTime)) * CAHostTimeBase::GetInverseFrequency(); +} +#endif + +void AUTimestampGenerator::AddOutputTime(const AudioTimeStamp &inTimeStamp, Float64 expectedDeltaFrames, double outputSampleRate, double rateScalarAdj) +{ + mState.mRateScalarAdj = rateScalarAdj; + mState.mLastOutputTime = mState.mCurrentOutputTime; + mState.mInputSampleTimeForOutputPull = mState.mNextInputSampleTime; + mState.mCurrentOutputTime = inTimeStamp; + + if (mState.mBypassed) + return; + + if (mState.mHostTimeDiscontinuityCorrection && !(mState.mCurrentOutputTime.mFlags & kAudioTimeStampHostTimeValid) && (mState.mLastOutputTime.mFlags & kAudioTimeStampHostTimeValid)) { + // no host time here but we had one last time, interpolate one + double rateScalar = (mState.mCurrentOutputTime.mFlags & kAudioTimeStampRateScalarValid) ? mState.mCurrentOutputTime.mRateScalar : 1.0; + Float64 deltaSamples = mState.mCurrentOutputTime.mSampleTime - mState.mLastOutputTime.mSampleTime; + mState.mCurrentOutputTime.mHostTime = mState.mLastOutputTime.mHostTime + + UInt64(CAHostTimeBase::GetFrequency() * deltaSamples * rateScalar / outputSampleRate); + mState.mCurrentOutputTime.mFlags |= kAudioTimeStampHostTimeValid; +#if DEBUG + if (mVerbosity > 1) + printf("synthesized host time: %.3f (%.3f + %.f smp @ %.f Hz, rs %.3f\n", DebugHostTime(mState.mCurrentOutputTime), DebugHostTime(mState.mLastOutputTime), deltaSamples, outputSampleRate, rateScalar); +#endif + } + // copy rate scalar + if (rateScalarAdj != 1.0) { + if (mState.mCurrentOutputTime.mFlags & kAudioTimeStampRateScalarValid) + mState.mCurrentOutputTime.mRateScalar *= rateScalarAdj; + else { + mState.mCurrentOutputTime.mRateScalar = rateScalarAdj; + mState.mCurrentOutputTime.mFlags |= kAudioTimeStampRateScalarValid; + } + } + + if (mFirstTime) { + mFirstTime = false; + mState.mDiscontinuous = false; + mState.mDiscontinuityDeltaSamples = 0.; + if (!mState.mStartInputAtZero) { + mState.mNextInputSampleTime = mState.mCurrentOutputTime.mSampleTime; + mState.mInputSampleTimeForOutputPull = mState.mNextInputSampleTime; + } + } else { + mState.mDiscontinuous = fnotequal(mState.mCurrentOutputTime.mSampleTime, mState.mNextOutputSampleTime); + mState.mDiscontinuityDeltaSamples = mState.mCurrentOutputTime.mSampleTime - mState.mNextOutputSampleTime; + // time should never go backwards... + if (mState.mDiscontinuityDeltaSamples < 0.) + mState.mDiscontinuityDeltaSamples = 0.; +#if DEBUG + if (mVerbosity > 1) + if (mState.mDiscontinuous) + printf("%-20.20s: *** DISCONTINUOUS, got " TSGFMT ", expected " TSGFMT "\n", mDebugName, (SInt64)mState.mCurrentOutputTime.mSampleTime, (SInt64)mState.mNextOutputSampleTime); +#endif + } + mState.mNextOutputSampleTime = mState.mCurrentOutputTime.mSampleTime + expectedDeltaFrames; +} + +const AudioTimeStamp & AUTimestampGenerator::GenerateInputTime(Float64 framesToAdvance, double inputSampleRate, bool advanceHostTime) +{ + if (mState.mBypassed) + return mState.mCurrentOutputTime; + + double inputSampleTime; + + mState.mCurrentInputTime.mFlags = kAudioTimeStampSampleTimeValid; + double rateScalar = 1.0; + + // propagate rate scalar + if (mState.mCurrentOutputTime.mFlags & kAudioTimeStampRateScalarValid) { + mState.mCurrentInputTime.mFlags |= kAudioTimeStampRateScalarValid; + mState.mCurrentInputTime.mRateScalar = rateScalar = mState.mCurrentOutputTime.mRateScalar; + } + + // propagate host time and sample time + if (mState.mCurrentOutputTime.mFlags & kAudioTimeStampHostTimeValid) { + mState.mCurrentInputTime.mFlags |= kAudioTimeStampHostTimeValid; + if (advanceHostTime) { + Float64 deltaSamples = mState.mNextInputSampleTime - mState.mInputSampleTimeForOutputPull; + Float64 deltaSeconds = (deltaSamples / inputSampleRate) * mState.mRateScalarAdj; + UInt64 deltaHostTime = (UInt64)(deltaSeconds * CAHostTimeBase::GetFrequency()); + mState.mCurrentInputTime.mHostTime = mState.mCurrentOutputTime.mHostTime + deltaHostTime; + } else { + mState.mCurrentInputTime.mHostTime = mState.mCurrentOutputTime.mHostTime; + } + if (mState.mHostTimeDiscontinuityCorrection && mState.mDiscontinuous && (mState.mLastOutputTime.mFlags & kAudioTimeStampHostTimeValid)) { + // we had a discontinuous output time, need to resync by interpolating + // a sample time that is appropriate to the host time + UInt64 deltaHostTime = mState.mCurrentOutputTime.mHostTime - mState.mLastOutputTime.mHostTime; + double deltaSeconds = double(deltaHostTime) * CAHostTimeBase::GetInverseFrequency(); + // samples/second * seconds = samples + double deltaSamples = floor(inputSampleRate / rateScalar * deltaSeconds + 0.5); + double lastInputSampleTime = mState.mCurrentInputTime.mSampleTime; + inputSampleTime = lastInputSampleTime + deltaSamples; +#if DEBUG + if (mVerbosity > 1) + printf("%-20.20s: adjusted input time: " TSGFMT " -> " TSGFMT " (SR=%.3f, rs=%.3f)\n", mDebugName, (SInt64)lastInputSampleTime, (SInt64)inputSampleTime, inputSampleRate, rateScalar); +#endif + mState.mDiscontinuous = false; + } else { + inputSampleTime = mState.mNextInputSampleTime; + } + } else { + // we don't know the host time, so we can't do much + inputSampleTime = mState.mNextInputSampleTime; + } + + if (!mState.mHostTimeDiscontinuityCorrection && fnonzero(mState.mDiscontinuityDeltaSamples)) + { + // we had a discontinuous output time, need to resync by propagating the + // detected discontinuity, taking the rate scalar adjustment into account + inputSampleTime += floor(mState.mDiscontinuityDeltaSamples / mState.mRateScalarAdj + 0.5); + +#if DEBUG + if (mVerbosity > 1) + printf("%-20.20s: adjusted input time: %.0f -> %.0f (SR=%.3f, rs=%.3f, delta=%.0f)\n", mDebugName, mState.mNextInputSampleTime, inputSampleTime, inputSampleRate, mState.mRateScalarAdj, mState.mDiscontinuityDeltaSamples); +#endif + + mState.mDiscontinuityDeltaSamples = 0.; + } + + + // propagate word clock + if (mState.mCurrentOutputTime.mFlags & kAudioTimeStampWordClockTimeValid) { + mState.mCurrentInputTime.mFlags |= kAudioTimeStampWordClockTimeValid; + mState.mCurrentInputTime.mWordClockTime = mState.mCurrentOutputTime.mWordClockTime; + } + + // propagate SMPTE time + if (mState.mCurrentOutputTime.mFlags & kAudioTimeStampSMPTETimeValid) { + mState.mCurrentInputTime.mFlags |= kAudioTimeStampSMPTETimeValid; + mState.mCurrentInputTime.mSMPTETime = mState.mCurrentOutputTime.mSMPTETime; + } + + // store the input sample time and expected next input time + mState.mCurrentInputTime.mSampleTime = inputSampleTime; + mState.mNextInputSampleTime = inputSampleTime + framesToAdvance; + +#if DEBUG + if (mVerbosity > 0) { + printf("%-20.20s: out = " TSGFMT " (%10.3fs) in = " TSGFMT " (%10.3fs) delta = " TSGFMT " advance = " TSGFMT "\n", mDebugName, (SInt64)mState.mCurrentOutputTime.mSampleTime, DebugHostTime(mState.mCurrentOutputTime), (SInt64)inputSampleTime, DebugHostTime(mState.mCurrentInputTime), (SInt64)(mState.mCurrentOutputTime.mSampleTime - inputSampleTime), (SInt64)framesToAdvance); + } +#endif + return mState.mCurrentInputTime; +} diff --git a/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUTimestampGenerator.h b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUTimestampGenerator.h new file mode 100644 index 0000000000..cfd8456dfb --- /dev/null +++ b/libs/appleutility/CoreAudio/AudioUnits/AUPublic/Utility/AUTimestampGenerator.h @@ -0,0 +1,163 @@ +/* + File: AUTimestampGenerator.h + Abstract: Part of CoreAudio Utility Classes + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#ifndef __AUTimestampGenerator_h__ +#define __AUTimestampGenerator_h__ + +#include <math.h> +#include "CAHostTimeBase.h" +#include <stdio.h> + +#define TSGFMT "0x%10qx" +//#define TSGFMT "%10qd" + +// This class generates a continuously increasing series of timestamps based +// on a series of potentially discontinuous timestamps (as can be delivered from +// CoreAudio in the event of an overload or major engine change). +// N.B.: "output" = downstream (source) timestamp +// "input" = upstream (derived) timestamp +class AUTimestampGenerator { +public: + AUTimestampGenerator(bool hostTimeDiscontinuityCorrection = false) + { + mState.mStartInputAtZero = true; + mState.mBypassed = false; + mState.mHostTimeDiscontinuityCorrection = hostTimeDiscontinuityCorrection; +#if DEBUG + mVerbosity = 0; + snprintf(mDebugName, sizeof(mDebugName), "tsg @ %p", this); +#endif + // CAHostTimeBase should be used instead of the calls in <CoreAudio/HostTime.h> + // we make this call here to ensure that this is initialized, otherwise the first time + // you do actually call CAHostTimeBase to do work, can be on the render thread, and lead to unwanted VM faults + CAHostTimeBase::GetFrequency(); + Reset(); + } + + void SetStartInputAtZero(bool b) { mState.mStartInputAtZero = b; } + bool GetStartInputAtZero() const { return mState.mStartInputAtZero; } + + // bypassing is intended for a narrow special case. the upstream sample time will always be the same as the downstream time. + void SetBypassed(bool b) { mState.mBypassed = b; } + bool GetBypassed() const { return mState.mBypassed; } + + // Call this to reset the timeline. + void Reset() + { + mState.mCurrentInputTime.mSampleTime = 0.; + mState.mNextInputSampleTime = 0.; + mState.mCurrentOutputTime.mSampleTime = 0.; + mState.mNextOutputSampleTime = 0.; + mState.mLastOutputTime.mFlags = 0; + mState.mRateScalarAdj = 1.; + + mFirstTime = true; +#if DEBUG + if (mVerbosity) + printf("%-20.20s: Reset\n", mDebugName); +#endif + } + + // Call this once per render cycle with the downstream timestamp. + // expectedDeltaFrames is the expected difference between the current and NEXT + // downstream timestamps. + // sampleRate is the OUTPUT sample rate. + void AddOutputTime(const AudioTimeStamp &inTimeStamp, Float64 expectedDeltaFrames, double outputSampleRate, double rateScalarAdj=1.0); + + // Call this once per render cycle to obtain the upstream timestamp. + // framesToAdvance is the number of frames the input timeline is to be + // advanced during this render cycle. + // sampleRate is the INPUT sample rate. + const AudioTimeStamp & GenerateInputTime(Float64 framesToAdvance, double inputSampleRate, bool advanceHostTime = false); + + // this can be called to override the setting of the next input sample time in GenerateInputTime + void Advance(Float64 framesToAdvance) + { +#if DEBUG + if (mVerbosity > 1) + printf("%-20.20s: ADVANCE in = " TSGFMT " advance = " TSGFMT "\n", mDebugName, (SInt64)mState.mCurrentInputTime.mSampleTime, (SInt64)framesToAdvance); +#endif + mState.mNextInputSampleTime = mState.mCurrentInputTime.mSampleTime + framesToAdvance; + } + + struct State { + AudioTimeStamp mCurrentInputTime; + Float64 mNextInputSampleTime; + Float64 mNextOutputSampleTime; + Float64 mInputSampleTimeForOutputPull; + + AudioTimeStamp mLastOutputTime; + AudioTimeStamp mCurrentOutputTime; + + bool mStartInputAtZero; // if true, input timeline starts at 0, else it starts + // synced with the output timeline + bool mDiscontinuous; + bool mBypassed; + Float64 mDiscontinuityDeltaSamples; + + double mRateScalarAdj; + + bool mHostTimeDiscontinuityCorrection; // If true, propagate timestamp discontinuities using host time. + }; + + void GetState(State& outState) const { outState = mState; } + void SetState(State const& inState) { mState = inState; mFirstTime = false; } + +private: + + struct State mState; + + bool mFirstTime; + +#if DEBUG +public: + int mVerbosity; + char mDebugName[64]; +#endif +}; + + +#endif // __AUTimestampGenerator_h__ |