mirror of
https://github.com/morgan9e/VolumeControl
synced 2026-04-14 16:24:07 +09:00
Initial commit
This commit is contained in:
BIN
driver/DeviceIcon.icns
Normal file
BIN
driver/DeviceIcon.icns
Normal file
Binary file not shown.
40
driver/Info.plist
Normal file
40
driver/Info.plist
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>VolumeControl</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.volumecontrol.Driver</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>VolumeControl</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.4.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
<key>CFPlugInFactories</key>
|
||||
<dict>
|
||||
<key>C957AD43-DACA-4A40-8850-3BA8CE28FAF9</key>
|
||||
<string>VC_Create</string>
|
||||
</dict>
|
||||
<key>CFPlugInTypes</key>
|
||||
<dict>
|
||||
<key>443ABAB8-E7B3-491A-B985-BEB9187030DB</key>
|
||||
<array>
|
||||
<string>C957AD43-DACA-4A40-8850-3BA8CE28FAF9</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2016-2024 Background Music contributors</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
317
driver/PublicUtility/CAAtomic.h
Normal file
317
driver/PublicUtility/CAAtomic.h
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
File: CAAtomic.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.
|
||||
|
||||
*/
|
||||
/*
|
||||
This file implements all Atomic operations using Interlocked functions specified in
|
||||
Winbase.h
|
||||
NOTE: According to Microsoft documentation, all Interlocked functions generates a
|
||||
full barrier.
|
||||
On Windows:
|
||||
As the Interlocked functions returns the Old value, Extra checks and operations
|
||||
are made after the atomic operation to return value consistent with OSX counterparts.
|
||||
*/
|
||||
#ifndef __CAAtomic_h__
|
||||
#define __CAAtomic_h__
|
||||
|
||||
#if TARGET_OS_WIN32
|
||||
#include <windows.h>
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_InterlockedOr)
|
||||
#pragma intrinsic(_InterlockedAnd)
|
||||
#else
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
#include <libkern/OSAtomic.h>
|
||||
#endif
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
inline void CAMemoryBarrier()
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
MemoryBarrier();
|
||||
#else
|
||||
OSMemoryBarrier();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicAdd32Barrier(SInt32 theAmt, volatile SInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
long lRetVal = InterlockedExchangeAdd((volatile long*)theValue, theAmt);
|
||||
// InterlockedExchangeAdd returns the original value which differs from OSX version.
|
||||
// At this point the addition would have occured and hence returning the new value
|
||||
// to keep it sync with OSX.
|
||||
return lRetVal + theAmt;
|
||||
#else
|
||||
return OSAtomicAdd32Barrier(theAmt, (volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicOr32Barrier(UInt32 theMask, volatile UInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
// InterlockedAnd macro is not defined in x86 platform, and hence using the intrinsic
|
||||
// function instead.
|
||||
long j = _InterlockedOr((volatile long*)theValue, theMask);
|
||||
// _InterlockedOr returns the original value which differs from OSX version.
|
||||
// Returning the new value similar to OSX
|
||||
return (SInt32)(j | theMask);
|
||||
#else
|
||||
return OSAtomicOr32Barrier(theMask, (volatile uint32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicAnd32Barrier(UInt32 theMask, volatile UInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
// InterlockedAnd macro is not defined in x86 platform, and hence using the intrinsic
|
||||
// function instead.
|
||||
long j = _InterlockedAnd((volatile long*)theValue, theMask);
|
||||
// _InterlockedAnd returns the original value which differs from OSX version.
|
||||
// Returning the new value similar to OSX
|
||||
return (SInt32)(j & theMask);
|
||||
#else
|
||||
return OSAtomicAnd32Barrier(theMask, (volatile uint32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool CAAtomicCompareAndSwap32Barrier(SInt32 oldValue, SInt32 newValue, volatile SInt32 *theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
// InterlockedCompareExchange returns the old value. But we need to return bool value.
|
||||
long lRetVal = InterlockedCompareExchange((volatile long*)theValue, newValue, oldValue);
|
||||
// Hence we check if the new value is set and if it is we return true else false.
|
||||
// If theValue is equal to oldValue then the swap happens. Otherwise swap doesn't happen.
|
||||
return (oldValue == lRetVal);
|
||||
#else
|
||||
return OSAtomicCompareAndSwap32Barrier(oldValue, newValue, (volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
inline SInt32 CAAtomicIncrement32(volatile SInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
return (SInt32)InterlockedIncrement((volatile long*)theValue);
|
||||
#else
|
||||
return OSAtomicIncrement32((volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicDecrement32(volatile SInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
return (SInt32)InterlockedDecrement((volatile long*)theValue);
|
||||
#else
|
||||
return OSAtomicDecrement32((volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicIncrement32Barrier(volatile SInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
return CAAtomicIncrement32(theValue);
|
||||
#else
|
||||
return OSAtomicIncrement32Barrier((volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicDecrement32Barrier(volatile SInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
return CAAtomicDecrement32(theValue);
|
||||
#else
|
||||
return OSAtomicDecrement32Barrier((volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool CAAtomicTestAndClearBarrier(int bitToClear, void* theAddress)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
BOOL bOldVal = InterlockedBitTestAndReset((long*)theAddress, bitToClear);
|
||||
return (bOldVal ? true : false);
|
||||
#else
|
||||
return OSAtomicTestAndClearBarrier(bitToClear, (volatile void *)theAddress);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool CAAtomicTestAndClear(int bitToClear, void* theAddress)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
BOOL bOldVal = CAAtomicTestAndClearBarrier(bitToClear, (long*)theAddress);
|
||||
return (bOldVal ? true : false);
|
||||
#else
|
||||
return OSAtomicTestAndClear(bitToClear, (volatile void *)theAddress);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool CAAtomicTestAndSetBarrier(int bitToSet, void* theAddress)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
BOOL bOldVal = InterlockedBitTestAndSet((long*)theAddress, bitToSet);
|
||||
return (bOldVal ? true : false);
|
||||
#else
|
||||
return OSAtomicTestAndSetBarrier(bitToSet, (volatile void *)theAddress);
|
||||
#endif
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// int32_t flavors -- for C++ only since we can't overload in C
|
||||
// CFBase.h defines SInt32 as signed int which is similar to int32_t. If CFBase.h is included, then
|
||||
// this will generate redefinition error. But on Mac, CFBase.h, still includes MacTypes.h where
|
||||
// SInt32 is defined as signed long so this would work there.
|
||||
// So in order to fix the redefinition errors, we define these functions only if MacTypes.h is included.
|
||||
#if defined(__cplusplus) && defined(__MACTYPES__) && !__LP64__
|
||||
inline int32_t CAAtomicAdd32Barrier(int32_t theAmt, volatile int32_t* theValue)
|
||||
{
|
||||
return CAAtomicAdd32Barrier(theAmt, (volatile SInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicOr32Barrier(uint32_t theMask, volatile uint32_t* theValue)
|
||||
{
|
||||
return CAAtomicOr32Barrier(theMask, (volatile UInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicAnd32Barrier(uint32_t theMask, volatile uint32_t* theValue)
|
||||
{
|
||||
return CAAtomicAnd32Barrier(theMask, (volatile UInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline bool CAAtomicCompareAndSwap32Barrier(int32_t oldValue, int32_t newValue, volatile int32_t *theValue)
|
||||
{
|
||||
return CAAtomicCompareAndSwap32Barrier(oldValue, newValue, (volatile SInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicIncrement32(volatile int32_t* theValue)
|
||||
{
|
||||
return CAAtomicIncrement32((volatile SInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicDecrement32(volatile int32_t* theValue)
|
||||
{
|
||||
return CAAtomicDecrement32((volatile SInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicIncrement32Barrier(volatile int32_t* theValue)
|
||||
{
|
||||
return CAAtomicIncrement32Barrier((volatile SInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicDecrement32Barrier(volatile int32_t* theValue)
|
||||
{
|
||||
return CAAtomicDecrement32Barrier((volatile SInt32 *)theValue);
|
||||
}
|
||||
#endif // __cplusplus && !__LP64__
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
|
||||
#if __LP64__
|
||||
inline bool CAAtomicCompareAndSwap64Barrier( int64_t __oldValue, int64_t __newValue, volatile int64_t *__theValue )
|
||||
{
|
||||
return OSAtomicCompareAndSwap64Barrier(__oldValue, __newValue, __theValue);
|
||||
}
|
||||
#endif
|
||||
|
||||
inline bool CAAtomicCompareAndSwapPtrBarrier(void *__oldValue, void *__newValue, volatile void ** __theValue)
|
||||
{
|
||||
#if __LP64__
|
||||
return CAAtomicCompareAndSwap64Barrier((int64_t)__oldValue, (int64_t)__newValue, (int64_t *)__theValue);
|
||||
#else
|
||||
return CAAtomicCompareAndSwap32Barrier((int32_t)__oldValue, (int32_t)__newValue, (int32_t *)__theValue);
|
||||
#endif
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
/* Spinlocks. These use memory barriers as required to synchronize access to shared
|
||||
* memory protected by the lock. The lock operation spins, but employs various strategies
|
||||
* to back off if the lock is held, making it immune to most priority-inversion livelocks.
|
||||
* The try operation immediately returns false if the lock was held, true if it took the
|
||||
* lock. The convention is that unlocked is zero, locked is nonzero.
|
||||
*/
|
||||
#define CA_SPINLOCK_INIT 0
|
||||
|
||||
typedef int32_t CASpinLock;
|
||||
|
||||
bool CASpinLockTry( volatile CASpinLock *__lock );
|
||||
void CASpinLockLock( volatile CASpinLock *__lock );
|
||||
void CASpinLockUnlock( volatile CASpinLock *__lock );
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
|
||||
inline void CASpinLockLock( volatile CASpinLock *__lock )
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
OSSpinLockLock(__lock);
|
||||
#else
|
||||
while (CAAtomicTestAndSetBarrier(0, (void*)__lock))
|
||||
usleep(1000); // ???
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void CASpinLockUnlock( volatile CASpinLock *__lock )
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
OSSpinLockUnlock(__lock);
|
||||
#else
|
||||
CAAtomicTestAndClearBarrier(0, (void*)__lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool CASpinLockTry( volatile CASpinLock *__lock )
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
return OSSpinLockTry(__lock);
|
||||
#else
|
||||
return (CAAtomicTestAndSetBarrier(0, (void*)__lock) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
|
||||
#endif // __CAAtomic_h__
|
||||
242
driver/PublicUtility/CAAtomicStack.h
Normal file
242
driver/PublicUtility/CAAtomicStack.h
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
File: CAAtomicStack.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 __CAAtomicStack_h__
|
||||
#define __CAAtomicStack_h__
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <libkern/OSAtomic.h>
|
||||
#else
|
||||
#include <CAAtomic.h>
|
||||
#endif
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_4
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
// linked list LIFO or FIFO (pop_all_reversed) stack, elements are pushed and popped atomically
|
||||
// class T must implement T *& next().
|
||||
template <class T>
|
||||
class TAtomicStack {
|
||||
public:
|
||||
TAtomicStack() : mHead(NULL) { }
|
||||
|
||||
// non-atomic routines, for use when initializing/deinitializing, operate NON-atomically
|
||||
void push_NA(T *item)
|
||||
{
|
||||
item->next() = mHead;
|
||||
mHead = item;
|
||||
}
|
||||
|
||||
T * pop_NA()
|
||||
{
|
||||
T *result = mHead;
|
||||
if (result)
|
||||
mHead = result->next();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool empty() const { return mHead == NULL; }
|
||||
|
||||
T * head() { return mHead; }
|
||||
|
||||
// atomic routines
|
||||
void push_atomic(T *item)
|
||||
{
|
||||
T *head_;
|
||||
do {
|
||||
head_ = mHead;
|
||||
item->next() = head_;
|
||||
} while (!compare_and_swap(head_, item, &mHead));
|
||||
}
|
||||
|
||||
void push_multiple_atomic(T *item)
|
||||
// pushes entire linked list headed by item
|
||||
{
|
||||
T *head_, *p = item, *tail;
|
||||
// find the last one -- when done, it will be linked to head
|
||||
do {
|
||||
tail = p;
|
||||
p = p->next();
|
||||
} while (p);
|
||||
do {
|
||||
head_ = mHead;
|
||||
tail->next() = head_;
|
||||
} while (!compare_and_swap(head_, item, &mHead));
|
||||
}
|
||||
|
||||
T * pop_atomic_single_reader()
|
||||
// this may only be used when only one thread may potentially pop from the stack.
|
||||
// if multiple threads may pop, this suffers from the ABA problem.
|
||||
// <rdar://problem/4606346> TAtomicStack suffers from the ABA problem
|
||||
{
|
||||
T *result;
|
||||
do {
|
||||
if ((result = mHead) == NULL)
|
||||
break;
|
||||
} while (!compare_and_swap(result, result->next(), &mHead));
|
||||
return result;
|
||||
}
|
||||
|
||||
T * pop_atomic()
|
||||
// This is inefficient for large linked lists.
|
||||
// prefer pop_all() to a series of calls to pop_atomic.
|
||||
// push_multiple_atomic has to traverse the entire list.
|
||||
{
|
||||
T *result = pop_all();
|
||||
if (result) {
|
||||
T *next = result->next();
|
||||
if (next)
|
||||
// push all the remaining items back onto the stack
|
||||
push_multiple_atomic(next);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
T * pop_all()
|
||||
{
|
||||
T *result;
|
||||
do {
|
||||
if ((result = mHead) == NULL)
|
||||
break;
|
||||
} while (!compare_and_swap(result, NULL, &mHead));
|
||||
return result;
|
||||
}
|
||||
|
||||
T* pop_all_reversed()
|
||||
{
|
||||
TAtomicStack<T> reversed;
|
||||
T *p = pop_all(), *next;
|
||||
while (p != NULL) {
|
||||
next = p->next();
|
||||
reversed.push_NA(p);
|
||||
p = next;
|
||||
}
|
||||
return reversed.mHead;
|
||||
}
|
||||
|
||||
static bool compare_and_swap(T *oldvalue, T *newvalue, T **pvalue)
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
#if TARGET_OS_MAC
|
||||
#if __LP64__
|
||||
return ::OSAtomicCompareAndSwap64Barrier(int64_t(oldvalue), int64_t(newvalue), (int64_t *)pvalue);
|
||||
#elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
|
||||
return ::OSAtomicCompareAndSwap32Barrier(int32_t(oldvalue), int32_t(newvalue), (int32_t *)pvalue);
|
||||
#else
|
||||
return ::CompareAndSwap(UInt32(oldvalue), UInt32(newvalue), (UInt32 *)pvalue);
|
||||
#endif
|
||||
#else
|
||||
//return ::CompareAndSwap(UInt32(oldvalue), UInt32(newvalue), (UInt32 *)pvalue);
|
||||
return CAAtomicCompareAndSwap32Barrier(SInt32(oldvalue), SInt32(newvalue), (SInt32*)pvalue);
|
||||
#endif
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
protected:
|
||||
T * mHead;
|
||||
};
|
||||
|
||||
#if ((MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) && !TARGET_OS_WIN32)
|
||||
#include <libkern/OSAtomic.h>
|
||||
|
||||
class CAAtomicStack {
|
||||
public:
|
||||
CAAtomicStack(size_t nextPtrOffset) : mNextPtrOffset(nextPtrOffset) {
|
||||
/*OSQueueHead h = OS_ATOMIC_QUEUE_INIT; mHead = h;*/
|
||||
mHead.opaque1 = 0; mHead.opaque2 = 0;
|
||||
}
|
||||
// a subset of the above
|
||||
void push_atomic(void *p) { OSAtomicEnqueue(&mHead, p, mNextPtrOffset); }
|
||||
void push_NA(void *p) { push_atomic(p); }
|
||||
|
||||
void * pop_atomic() { return OSAtomicDequeue(&mHead, mNextPtrOffset); }
|
||||
void * pop_atomic_single_reader() { return pop_atomic(); }
|
||||
void * pop_NA() { return pop_atomic(); }
|
||||
|
||||
private:
|
||||
OSQueueHead mHead;
|
||||
size_t mNextPtrOffset;
|
||||
};
|
||||
|
||||
// a more efficient subset of TAtomicStack using OSQueue.
|
||||
template <class T>
|
||||
class TAtomicStack2 {
|
||||
public:
|
||||
TAtomicStack2() {
|
||||
/*OSQueueHead h = OS_ATOMIC_QUEUE_INIT; mHead = h;*/
|
||||
mHead.opaque1 = 0; mHead.opaque2 = 0;
|
||||
mNextPtrOffset = -1;
|
||||
}
|
||||
void push_atomic(T *item) {
|
||||
if (mNextPtrOffset < 0) {
|
||||
T **pnext = &item->next(); // hack around offsetof not working with C++
|
||||
mNextPtrOffset = (Byte *)pnext - (Byte *)item;
|
||||
}
|
||||
OSAtomicEnqueue(&mHead, item, mNextPtrOffset);
|
||||
}
|
||||
void push_NA(T *item) { push_atomic(item); }
|
||||
|
||||
T * pop_atomic() { return (T *)OSAtomicDequeue(&mHead, mNextPtrOffset); }
|
||||
T * pop_atomic_single_reader() { return pop_atomic(); }
|
||||
T * pop_NA() { return pop_atomic(); }
|
||||
|
||||
// caution: do not try to implement pop_all_reversed here. the writer could add new elements
|
||||
// while the reader is trying to pop old ones!
|
||||
|
||||
private:
|
||||
OSQueueHead mHead;
|
||||
ssize_t mNextPtrOffset;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#define TAtomicStack2 TAtomicStack
|
||||
|
||||
#endif // MAC_OS_X_VERSION_MAX_ALLOWED && !TARGET_OS_WIN32
|
||||
|
||||
#endif // __CAAtomicStack_h__
|
||||
508
driver/PublicUtility/CAAutoDisposer.h
Normal file
508
driver/PublicUtility/CAAutoDisposer.h
Normal file
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
File: CAAutoDisposer.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.
|
||||
|
||||
*/
|
||||
#if !defined(__CAPtr_h__)
|
||||
#define __CAPtr_h__
|
||||
|
||||
#include <stdlib.h> // for malloc
|
||||
#include <new> // for bad_alloc
|
||||
#include <string.h> // for memset
|
||||
|
||||
inline void* CA_malloc(size_t size)
|
||||
{
|
||||
void* p = malloc(size);
|
||||
if (!p && size) throw std::bad_alloc();
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void* CA_realloc(void* old, size_t size)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
void* p = realloc(old, size);
|
||||
#else
|
||||
void* p = reallocf(old, size); // reallocf ensures the old pointer is freed if memory is full (p is NULL).
|
||||
#endif
|
||||
if (!p && size) throw std::bad_alloc();
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifndef UINTPTR_MAX
|
||||
#if __LP64__
|
||||
#define UINTPTR_MAX 18446744073709551615ULL
|
||||
#else
|
||||
#define UINTPTR_MAX 4294967295U
|
||||
#endif
|
||||
#endif
|
||||
|
||||
inline void* CA_calloc(size_t n, size_t size)
|
||||
{
|
||||
// ensure that multiplication will not overflow
|
||||
if (n && UINTPTR_MAX / n < size) throw std::bad_alloc();
|
||||
|
||||
size_t nsize = n*size;
|
||||
void* p = malloc(nsize);
|
||||
if (!p && nsize) throw std::bad_alloc();
|
||||
|
||||
memset(p, 0, nsize);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
// helper class for automatic conversions
|
||||
template <typename T>
|
||||
struct CAPtrRef
|
||||
{
|
||||
T* ptr_;
|
||||
|
||||
explicit CAPtrRef(T* ptr) : ptr_(ptr) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class CAAutoFree
|
||||
{
|
||||
private:
|
||||
T* ptr_;
|
||||
|
||||
public:
|
||||
|
||||
CAAutoFree() : ptr_(0) {}
|
||||
|
||||
explicit CAAutoFree(T* ptr) : ptr_(ptr) {}
|
||||
|
||||
template<typename U>
|
||||
CAAutoFree(CAAutoFree<U>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
// C++ std says: a template constructor is never a copy constructor
|
||||
CAAutoFree(CAAutoFree<T>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
CAAutoFree(size_t n, bool clear = false)
|
||||
// this becomes an ambiguous call if n == 0
|
||||
: ptr_(0)
|
||||
{
|
||||
size_t maxItems = ~size_t(0) / sizeof(T);
|
||||
if (n > maxItems)
|
||||
throw std::bad_alloc();
|
||||
|
||||
ptr_ = static_cast<T*>(clear ? CA_calloc(n, sizeof(T)) : CA_malloc(n * sizeof(T)));
|
||||
}
|
||||
|
||||
~CAAutoFree() { free(); }
|
||||
|
||||
void alloc(size_t numItems, bool clear = false)
|
||||
{
|
||||
size_t maxItems = ~size_t(0) / sizeof(T);
|
||||
if (numItems > maxItems) throw std::bad_alloc();
|
||||
|
||||
free();
|
||||
ptr_ = static_cast<T*>(clear ? CA_calloc(numItems, sizeof(T)) : CA_malloc(numItems * sizeof(T)));
|
||||
}
|
||||
|
||||
void allocBytes(size_t numBytes, bool clear = false)
|
||||
{
|
||||
free();
|
||||
ptr_ = static_cast<T*>(clear ? CA_calloc(1, numBytes) : CA_malloc(numBytes));
|
||||
}
|
||||
|
||||
void reallocBytes(size_t numBytes)
|
||||
{
|
||||
ptr_ = static_cast<T*>(CA_realloc(ptr_, numBytes));
|
||||
}
|
||||
|
||||
void reallocItems(size_t numItems)
|
||||
{
|
||||
size_t maxItems = ~size_t(0) / sizeof(T);
|
||||
if (numItems > maxItems) throw std::bad_alloc();
|
||||
|
||||
ptr_ = static_cast<T*>(CA_realloc(ptr_, numItems * sizeof(T)));
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
CAAutoFree& operator=(CAAutoFree<U>& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoFree& operator=(CAAutoFree& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoFree& operator=(T* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
CAAutoFree& operator=(U* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() const { return *ptr_; }
|
||||
T* operator->() const { return ptr_; }
|
||||
|
||||
T* operator()() const { return ptr_; }
|
||||
T* get() const { return ptr_; }
|
||||
operator T*() const { return ptr_; }
|
||||
|
||||
bool operator==(CAAutoFree const& that) const { return ptr_ == that.ptr_; }
|
||||
bool operator!=(CAAutoFree const& that) const { return ptr_ != that.ptr_; }
|
||||
bool operator==(T* ptr) const { return ptr_ == ptr; }
|
||||
bool operator!=(T* ptr) const { return ptr_ != ptr; }
|
||||
|
||||
T* release()
|
||||
{
|
||||
// release ownership
|
||||
T* result = ptr_;
|
||||
ptr_ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void set(T* ptr)
|
||||
{
|
||||
if (ptr != ptr_)
|
||||
{
|
||||
::free(ptr_);
|
||||
ptr_ = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void free()
|
||||
{
|
||||
set(0);
|
||||
}
|
||||
|
||||
|
||||
// automatic conversions to allow assignment from results of functions.
|
||||
// hard to explain. see auto_ptr implementation and/or Josuttis' STL book.
|
||||
CAAutoFree(CAPtrRef<T> ref) : ptr_(ref.ptr_) { }
|
||||
|
||||
CAAutoFree& operator=(CAPtrRef<T> ref)
|
||||
{
|
||||
set(ref.ptr_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
operator CAPtrRef<U>()
|
||||
{ return CAPtrRef<U>(release()); }
|
||||
|
||||
template<typename U>
|
||||
operator CAAutoFree<U>()
|
||||
{ return CAAutoFree<U>(release()); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
class CAAutoDelete
|
||||
{
|
||||
private:
|
||||
T* ptr_;
|
||||
|
||||
public:
|
||||
CAAutoDelete() : ptr_(0) {}
|
||||
|
||||
explicit CAAutoDelete(T* ptr) : ptr_(ptr) {}
|
||||
|
||||
template<typename U>
|
||||
CAAutoDelete(CAAutoDelete<U>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
// C++ std says: a template constructor is never a copy constructor
|
||||
CAAutoDelete(CAAutoDelete<T>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
~CAAutoDelete() { free(); }
|
||||
|
||||
template <typename U>
|
||||
CAAutoDelete& operator=(CAAutoDelete<U>& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoDelete& operator=(CAAutoDelete& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoDelete& operator=(T* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
CAAutoDelete& operator=(U* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() const { return *ptr_; }
|
||||
T* operator->() const { return ptr_; }
|
||||
|
||||
T* operator()() const { return ptr_; }
|
||||
T* get() const { return ptr_; }
|
||||
operator T*() const { return ptr_; }
|
||||
|
||||
bool operator==(CAAutoDelete const& that) const { return ptr_ == that.ptr_; }
|
||||
bool operator!=(CAAutoDelete const& that) const { return ptr_ != that.ptr_; }
|
||||
bool operator==(T* ptr) const { return ptr_ == ptr; }
|
||||
bool operator!=(T* ptr) const { return ptr_ != ptr; }
|
||||
|
||||
T* release()
|
||||
{
|
||||
// release ownership
|
||||
T* result = ptr_;
|
||||
ptr_ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void set(T* ptr)
|
||||
{
|
||||
if (ptr != ptr_)
|
||||
{
|
||||
delete ptr_;
|
||||
ptr_ = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void free()
|
||||
{
|
||||
set(0);
|
||||
}
|
||||
|
||||
|
||||
// automatic conversions to allow assignment from results of functions.
|
||||
// hard to explain. see auto_ptr implementation and/or Josuttis' STL book.
|
||||
CAAutoDelete(CAPtrRef<T> ref) : ptr_(ref.ptr_) { }
|
||||
|
||||
CAAutoDelete& operator=(CAPtrRef<T> ref)
|
||||
{
|
||||
set(ref.ptr_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
operator CAPtrRef<U>()
|
||||
{ return CAPtrRef<U>(release()); }
|
||||
|
||||
template<typename U>
|
||||
operator CAAutoFree<U>()
|
||||
{ return CAAutoFree<U>(release()); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
class CAAutoArrayDelete
|
||||
{
|
||||
private:
|
||||
T* ptr_;
|
||||
|
||||
public:
|
||||
CAAutoArrayDelete() : ptr_(0) {}
|
||||
|
||||
explicit CAAutoArrayDelete(T* ptr) : ptr_(ptr) {}
|
||||
|
||||
template<typename U>
|
||||
CAAutoArrayDelete(CAAutoArrayDelete<U>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
// C++ std says: a template constructor is never a copy constructor
|
||||
CAAutoArrayDelete(CAAutoArrayDelete<T>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
// this becomes an ambiguous call if n == 0
|
||||
CAAutoArrayDelete(size_t n) : ptr_(new T[n]) {}
|
||||
|
||||
~CAAutoArrayDelete() { free(); }
|
||||
|
||||
void alloc(size_t numItems)
|
||||
{
|
||||
free();
|
||||
ptr_ = new T [numItems];
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
CAAutoArrayDelete& operator=(CAAutoArrayDelete<U>& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoArrayDelete& operator=(CAAutoArrayDelete& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoArrayDelete& operator=(T* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
CAAutoArrayDelete& operator=(U* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() const { return *ptr_; }
|
||||
T* operator->() const { return ptr_; }
|
||||
|
||||
T* operator()() const { return ptr_; }
|
||||
T* get() const { return ptr_; }
|
||||
operator T*() const { return ptr_; }
|
||||
|
||||
bool operator==(CAAutoArrayDelete const& that) const { return ptr_ == that.ptr_; }
|
||||
bool operator!=(CAAutoArrayDelete const& that) const { return ptr_ != that.ptr_; }
|
||||
bool operator==(T* ptr) const { return ptr_ == ptr; }
|
||||
bool operator!=(T* ptr) const { return ptr_ != ptr; }
|
||||
|
||||
T* release()
|
||||
{
|
||||
// release ownership
|
||||
T* result = ptr_;
|
||||
ptr_ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void set(T* ptr)
|
||||
{
|
||||
if (ptr != ptr_)
|
||||
{
|
||||
delete [] ptr_;
|
||||
ptr_ = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void free()
|
||||
{
|
||||
set(0);
|
||||
}
|
||||
|
||||
|
||||
// automatic conversions to allow assignment from results of functions.
|
||||
// hard to explain. see auto_ptr implementation and/or Josuttis' STL book.
|
||||
CAAutoArrayDelete(CAPtrRef<T> ref) : ptr_(ref.ptr_) { }
|
||||
|
||||
CAAutoArrayDelete& operator=(CAPtrRef<T> ref)
|
||||
{
|
||||
set(ref.ptr_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
operator CAPtrRef<U>()
|
||||
{ return CAPtrRef<U>(release()); }
|
||||
|
||||
template<typename U>
|
||||
operator CAAutoArrayDelete<U>()
|
||||
{ return CAAutoFree<U>(release()); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// convenience function
|
||||
template <typename T>
|
||||
void free(CAAutoFree<T>& p)
|
||||
{
|
||||
p.free();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if 0
|
||||
// example program showing ownership transfer
|
||||
|
||||
CAAutoFree<char> source()
|
||||
{
|
||||
// source allocates and returns ownership to the caller.
|
||||
const char* str = "this is a test";
|
||||
size_t size = strlen(str) + 1;
|
||||
CAAutoFree<char> captr(size, false);
|
||||
strlcpy(captr(), str, size);
|
||||
printf("source %08X %08X '%s'\n", &captr, captr(), captr());
|
||||
return captr;
|
||||
}
|
||||
|
||||
void user(CAAutoFree<char> const& captr)
|
||||
{
|
||||
// passed by const reference. user can access the pointer but does not take ownership.
|
||||
printf("user: %08X %08X '%s'\n", &captr, captr(), captr());
|
||||
}
|
||||
|
||||
void sink(CAAutoFree<char> captr)
|
||||
{
|
||||
// passed by value. sink takes ownership and frees the pointer on return.
|
||||
printf("sink: %08X %08X '%s'\n", &captr, captr(), captr());
|
||||
}
|
||||
|
||||
|
||||
int main (int argc, char * const argv[])
|
||||
{
|
||||
|
||||
CAAutoFree<char> captr(source());
|
||||
printf("main captr A %08X %08X\n", &captr, captr());
|
||||
user(captr);
|
||||
sink(captr);
|
||||
printf("main captr B %08X %08X\n", &captr, captr());
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
206
driver/PublicUtility/CABitOperations.h
Normal file
206
driver/PublicUtility/CABitOperations.h
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
File: CABitOperations.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 _CABitOperations_h_
|
||||
#define _CABitOperations_h_
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
//#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacTypes.h>
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
#else
|
||||
// #include <MacTypes.h>
|
||||
#include "CFBase.h"
|
||||
#endif
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
// return whether a number is a power of two
|
||||
inline UInt32 IsPowerOfTwo(UInt32 x)
|
||||
{
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
// count the leading zeros in a word
|
||||
// Metrowerks Codewarrior. powerpc native count leading zeros instruction:
|
||||
// I think it's safe to remove this ...
|
||||
//#define CountLeadingZeroes(x) ((int)__cntlzw((unsigned int)x))
|
||||
|
||||
inline UInt32 CountLeadingZeroes(UInt32 arg)
|
||||
{
|
||||
// GNUC / LLVM have a builtin
|
||||
#if defined(__GNUC__) || defined(__llvm___)
|
||||
#if (TARGET_CPU_X86 || TARGET_CPU_X86_64)
|
||||
if (arg == 0) return 32;
|
||||
#endif // TARGET_CPU_X86 || TARGET_CPU_X86_64
|
||||
return __builtin_clz(arg);
|
||||
#elif TARGET_OS_WIN32
|
||||
UInt32 tmp;
|
||||
__asm{
|
||||
bsr eax, arg
|
||||
mov ecx, 63
|
||||
cmovz eax, ecx
|
||||
xor eax, 31
|
||||
mov tmp, eax // this moves the result in tmp to return.
|
||||
}
|
||||
return tmp;
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif // defined(__GNUC__)
|
||||
}
|
||||
// Alias (with different spelling)
|
||||
#define CountLeadingZeros CountLeadingZeroes
|
||||
|
||||
inline UInt32 CountLeadingZeroesLong(UInt64 arg)
|
||||
{
|
||||
// GNUC / LLVM have a builtin
|
||||
#if defined(__GNUC__) || defined(__llvm___)
|
||||
#if (TARGET_CPU_X86 || TARGET_CPU_X86_64)
|
||||
if (arg == 0) return 64;
|
||||
#endif // TARGET_CPU_X86 || TARGET_CPU_X86_64
|
||||
return __builtin_clzll(arg);
|
||||
#elif TARGET_OS_WIN32
|
||||
UInt32 x = CountLeadingZeroes((UInt32)(arg >> 32));
|
||||
if(x < 32)
|
||||
return x;
|
||||
else
|
||||
return 32+CountLeadingZeroes((UInt32)arg);
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif // defined(__GNUC__)
|
||||
}
|
||||
#define CountLeadingZerosLong CountLeadingZeroesLong
|
||||
|
||||
// count trailing zeroes
|
||||
inline UInt32 CountTrailingZeroes(UInt32 x)
|
||||
{
|
||||
return 32 - CountLeadingZeroes(~x & (x-1));
|
||||
}
|
||||
|
||||
// count leading ones
|
||||
inline UInt32 CountLeadingOnes(UInt32 x)
|
||||
{
|
||||
return CountLeadingZeroes(~x);
|
||||
}
|
||||
|
||||
// count trailing ones
|
||||
inline UInt32 CountTrailingOnes(UInt32 x)
|
||||
{
|
||||
return 32 - CountLeadingZeroes(x & (~x-1));
|
||||
}
|
||||
|
||||
// number of bits required to represent x.
|
||||
inline UInt32 NumBits(UInt32 x)
|
||||
{
|
||||
return 32 - CountLeadingZeroes(x);
|
||||
}
|
||||
|
||||
// base 2 log of next power of two greater or equal to x
|
||||
inline UInt32 Log2Ceil(UInt32 x)
|
||||
{
|
||||
return 32 - CountLeadingZeroes(x - 1);
|
||||
}
|
||||
|
||||
// base 2 log of next power of two less or equal to x
|
||||
inline UInt32 Log2Floor(UInt32 x)
|
||||
{
|
||||
return 32 - CountLeadingZeroes(x) - 1;
|
||||
}
|
||||
|
||||
// next power of two greater or equal to x
|
||||
inline UInt32 NextPowerOfTwo(UInt32 x)
|
||||
{
|
||||
return 1 << Log2Ceil(x);
|
||||
}
|
||||
|
||||
// counting the one bits in a word
|
||||
inline UInt32 CountOnes(UInt32 x)
|
||||
{
|
||||
// secret magic algorithm for counting bits in a word.
|
||||
x = x - ((x >> 1) & 0x55555555);
|
||||
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
|
||||
return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
|
||||
}
|
||||
|
||||
// counting the zero bits in a word
|
||||
inline UInt32 CountZeroes(UInt32 x)
|
||||
{
|
||||
return CountOnes(~x);
|
||||
}
|
||||
|
||||
// return the bit position (0..31) of the least significant bit
|
||||
inline UInt32 LSBitPos(UInt32 x)
|
||||
{
|
||||
return CountTrailingZeroes(x & -(SInt32)x);
|
||||
}
|
||||
|
||||
// isolate the least significant bit
|
||||
inline UInt32 LSBit(UInt32 x)
|
||||
{
|
||||
return x & -(SInt32)x;
|
||||
}
|
||||
|
||||
// return the bit position (0..31) of the most significant bit
|
||||
inline UInt32 MSBitPos(UInt32 x)
|
||||
{
|
||||
return 31 - CountLeadingZeroes(x);
|
||||
}
|
||||
|
||||
// isolate the most significant bit
|
||||
inline UInt32 MSBit(UInt32 x)
|
||||
{
|
||||
return 1 << MSBitPos(x);
|
||||
}
|
||||
|
||||
// Division optimized for power of 2 denominators
|
||||
inline UInt32 DivInt(UInt32 numerator, UInt32 denominator)
|
||||
{
|
||||
if(IsPowerOfTwo(denominator))
|
||||
return numerator >> (31 - CountLeadingZeroes(denominator));
|
||||
else
|
||||
return numerator/denominator;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
821
driver/PublicUtility/CACFArray.cpp
Normal file
821
driver/PublicUtility/CACFArray.cpp
Normal file
@@ -0,0 +1,821 @@
|
||||
/*
|
||||
File: CACFArray.cpp
|
||||
Abstract: CACFArray.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.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CACFArray.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CACFDictionary.h"
|
||||
#include "CACFNumber.h"
|
||||
#include "CACFString.h"
|
||||
|
||||
//=============================================================================
|
||||
// CACFArray
|
||||
//=============================================================================
|
||||
|
||||
bool CACFArray::HasItem(const void* inItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
if(mCFArray != NULL)
|
||||
{
|
||||
CFRange theRange = { 0, CFArrayGetCount(mCFArray)};
|
||||
theAnswer = CFArrayContainsValue(mCFArray, theRange, inItem);
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetIndexOfItem(const void* inItem, UInt32& outIndex) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
outIndex = 0;
|
||||
if(mCFArray != NULL)
|
||||
{
|
||||
CFRange theRange = { 0, CFArrayGetCount(mCFArray)};
|
||||
CFIndex theIndex = CFArrayGetFirstIndexOfValue(mCFArray, theRange, inItem);
|
||||
if(theIndex != -1)
|
||||
{
|
||||
theAnswer = true;
|
||||
outIndex = ToUInt32(theIndex);
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetBool(UInt32 inIndex, bool& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inIndex, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFBooleanGetTypeID()))
|
||||
{
|
||||
outValue = CFBooleanGetValue(static_cast<CFBooleanRef>(theValue));
|
||||
theAnswer = true;
|
||||
}
|
||||
else if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
SInt32 theNumericValue = 0;
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &theNumericValue);
|
||||
outValue = theNumericValue != 0;
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetSInt32(UInt32 inIndex, SInt32& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberSInt32Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetUInt32(UInt32 inIndex, UInt32& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberSInt32Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetSInt64(UInt32 inIndex, SInt64& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberSInt64Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetUInt64(UInt32 inIndex, UInt64& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberSInt64Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetFloat32(UInt32 inIndex, Float32& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberFloat32Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetFloat64(UInt32 inIndex, Float64& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberFloat64Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::Get4CC(UInt32 inIndex, UInt32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inIndex, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
else if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
CFStringRef theString = static_cast<CFStringRef>(theValue);
|
||||
if(CFStringGetLength(theString) == 4)
|
||||
{
|
||||
char theCString[5];
|
||||
CFStringGetCString(theString, theCString, 5, kCFStringEncodingASCII);
|
||||
outValue = CFSwapInt32BigToHost(*reinterpret_cast<UInt32*>(theCString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetString(UInt32 inIndex, CFStringRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFStringGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFStringRef>(theItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetArray(UInt32 inIndex, CFArrayRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFArrayGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFArrayRef>(theItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetDictionary(UInt32 inIndex, CFDictionaryRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFDictionaryGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFDictionaryRef>(theItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetData(UInt32 inIndex, CFDataRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFDataGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFDataRef>(theItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetUUID(UInt32 inIndex, CFUUIDRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFUUIDGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFUUIDRef>(theItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetCFType(UInt32 inIndex, CFTypeRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && (inIndex < GetNumberItems()))
|
||||
{
|
||||
outItem = CFArrayGetValueAtIndex(mCFArray, static_cast<CFIndex>(inIndex));
|
||||
theAnswer = outItem != NULL;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CACFArray::GetCACFString(UInt32 inIndex, CACFString& outItem) const
|
||||
{
|
||||
outItem = static_cast<CFStringRef>(NULL);
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFStringGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFStringRef>(theItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CACFArray::GetCACFArray(UInt32 inIndex, CACFArray& outItem) const
|
||||
{
|
||||
outItem = static_cast<CFArrayRef>(NULL);
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFArrayGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFArrayRef>(theItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CACFArray::GetCACFDictionary(UInt32 inIndex, CACFDictionary& outItem) const
|
||||
{
|
||||
outItem = static_cast<CFDictionaryRef>(NULL);
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFDictionaryGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFDictionaryRef>(theItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CACFArray::AppendBool(bool inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFBoolean theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFBoolean());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendSInt32(SInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendUInt32(UInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendSInt64(SInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendUInt64(UInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendFloat32(Float32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendFloat64(Float64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendString(const CFStringRef inItem)
|
||||
{
|
||||
return AppendCFType(inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::AppendArray(const CFArrayRef inItem)
|
||||
{
|
||||
return AppendCFType(inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::AppendDictionary(const CFDictionaryRef inItem)
|
||||
{
|
||||
return AppendCFType(inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::AppendData(const CFDataRef inItem)
|
||||
{
|
||||
return AppendCFType(inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::AppendCFType(const CFTypeRef inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CFArrayAppendValue(mCFArray, inItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertBool(UInt32 inIndex, bool inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFBoolean theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFBoolean());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertSInt32(UInt32 inIndex, SInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertUInt32(UInt32 inIndex, UInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertSInt64(UInt32 inIndex, SInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertUInt64(UInt32 inIndex, UInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertFloat32(UInt32 inIndex, Float32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertFloat64(UInt32 inIndex, Float64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertString(UInt32 inIndex, const CFStringRef inItem)
|
||||
{
|
||||
return InsertCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::InsertArray(UInt32 inIndex, const CFArrayRef inItem)
|
||||
{
|
||||
return InsertCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::InsertDictionary(UInt32 inIndex, const CFDictionaryRef inItem)
|
||||
{
|
||||
return InsertCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::InsertData(UInt32 inIndex, const CFDataRef inItem)
|
||||
{
|
||||
return InsertCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::InsertCFType(UInt32 inIndex, const CFTypeRef inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
if(inIndex < GetNumberItems())
|
||||
{
|
||||
CFArrayInsertValueAtIndex(mCFArray, static_cast<CFIndex>(inIndex), inItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
CFArrayAppendValue(mCFArray, inItem);
|
||||
}
|
||||
theAnswer = true;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetBool(UInt32 inIndex, bool inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFBoolean theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFBoolean());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetSInt32(UInt32 inIndex, SInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetUInt32(UInt32 inIndex, UInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetSInt64(UInt32 inIndex, SInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetUInt64(UInt32 inIndex, UInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetFloat32(UInt32 inIndex, Float32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetFloat64(UInt32 inIndex, Float64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetString(UInt32 inIndex, const CFStringRef inItem)
|
||||
{
|
||||
return SetCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::SetArray(UInt32 inIndex, const CFArrayRef inItem)
|
||||
{
|
||||
return SetCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::SetDictionary(UInt32 inIndex, const CFDictionaryRef inItem)
|
||||
{
|
||||
return SetCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::SetData(UInt32 inIndex, const CFDataRef inItem)
|
||||
{
|
||||
return SetCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::SetCFType(UInt32 inIndex, const CFTypeRef inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CFArraySetValueAtIndex(mCFArray, static_cast<CFIndex>(inIndex), inItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
195
driver/PublicUtility/CACFArray.h
Normal file
195
driver/PublicUtility/CACFArray.h
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
File: CACFArray.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.
|
||||
|
||||
*/
|
||||
#if !defined(__CACFArray_h__)
|
||||
#define __CACFArray_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
// System Includes
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#include <CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
//=============================================================================
|
||||
// Types
|
||||
//=============================================================================
|
||||
|
||||
class CACFDictionary;
|
||||
class CACFString;
|
||||
|
||||
//=============================================================================
|
||||
// CACFArray
|
||||
//=============================================================================
|
||||
|
||||
class CACFArray
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CACFArray() : mCFArray(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)), mRelease(true), mMutable(true) {}
|
||||
explicit CACFArray(bool inRelease) : mCFArray(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)), mRelease(inRelease), mMutable(true) {}
|
||||
CACFArray(UInt32 inMaxNumberItems, bool inRelease) : mCFArray(CFArrayCreateMutable(NULL, static_cast<CFIndex>(inMaxNumberItems), &kCFTypeArrayCallBacks)), mRelease(inRelease), mMutable(true) {}
|
||||
CACFArray(CFArrayRef inCFArray, bool inRelease) : mCFArray(const_cast<CFMutableArrayRef>(inCFArray)), mRelease(inRelease), mMutable(false) {}
|
||||
CACFArray(CFMutableArrayRef inCFArray, bool inRelease) : mCFArray(inCFArray), mRelease(inRelease), mMutable(true) {}
|
||||
CACFArray(const CACFArray& inArray) : mCFArray(inArray.mCFArray), mRelease(inArray.mRelease), mMutable(inArray.mMutable) { Retain(); }
|
||||
CACFArray& operator=(const CACFArray& inArray) { Release(); mCFArray = inArray.mCFArray; mRelease = inArray.mRelease; mMutable = inArray.mMutable; Retain(); return *this; }
|
||||
CACFArray& operator=(CFArrayRef inCFArray) { Release(); mCFArray = const_cast<CFMutableArrayRef>(inCFArray); mMutable = false; Retain(); return *this; }
|
||||
CACFArray& operator=(CFMutableArrayRef inCFArray) { Release(); mCFArray = inCFArray; mMutable = true; Retain(); return *this; }
|
||||
~CACFArray() { Release(); }
|
||||
|
||||
private:
|
||||
void Retain() { if(mRelease && (mCFArray != NULL)) { CFRetain(mCFArray); } }
|
||||
void Release() { if(mRelease && (mCFArray != NULL)) { CFRelease(mCFArray); } }
|
||||
|
||||
// Attributes
|
||||
public:
|
||||
bool IsValid() const { return mCFArray != NULL; }
|
||||
bool IsMutable() const { return mMutable; }
|
||||
bool CanModify() const { return mMutable && (mCFArray != NULL); }
|
||||
|
||||
bool WillRelease() const { return mRelease; }
|
||||
void ShouldRelease(bool inRelease) { mRelease = inRelease; }
|
||||
|
||||
CFTypeID GetTypeID() const { return CFGetTypeID(mCFArray); }
|
||||
|
||||
CFArrayRef GetCFArray() const { return mCFArray; }
|
||||
CFArrayRef CopyCFArray() const { if(mCFArray != NULL) { CFRetain(mCFArray); } return mCFArray; }
|
||||
|
||||
CFMutableArrayRef GetCFMutableArray() const { return mCFArray; }
|
||||
CFMutableArrayRef CopyCFMutableArray() const { if(mCFArray != NULL) { CFRetain(mCFArray); } return mCFArray; }
|
||||
CFPropertyListRef AsPropertyList() const { return mCFArray; }
|
||||
|
||||
void SetCFMutableArrayFromCopy(CFArrayRef inArray, bool inRelease = true) { Release(); mCFArray = CFArrayCreateMutableCopy(NULL, 0, inArray); mMutable = true; mRelease = inRelease; }
|
||||
|
||||
// Item Operations
|
||||
public:
|
||||
UInt32 GetNumberItems() const { UInt32 theAnswer = 0; if(mCFArray != NULL) { theAnswer = ToUInt32(CFArrayGetCount(mCFArray)); } return theAnswer; }
|
||||
bool HasItem(const void* inItem) const;
|
||||
void RemoveItem(const void* inItem) { UInt32 theIndex; if(CanModify() && GetIndexOfItem(inItem, theIndex)) { RemoveItemAtIndex(theIndex); } }
|
||||
bool GetIndexOfItem(const void* inItem, UInt32& outIndex) const;
|
||||
void RemoveItemAtIndex(UInt32 inIndex) { if(CanModify()) { CFArrayRemoveValueAtIndex(mCFArray, static_cast<CFIndex>(inIndex)); } }
|
||||
void Clear() { if(CanModify()) { CFArrayRemoveAllValues(mCFArray); } }
|
||||
void Sort(CFComparatorFunction inCompareFunction) { if(CanModify()) { CFRange theRange = { 0, CFArrayGetCount(mCFArray) }; CFArraySortValues(mCFArray, theRange, inCompareFunction, NULL); } }
|
||||
void SortNumbers() { Sort((CFComparatorFunction)CFNumberCompare); }
|
||||
void SortStrings() { Sort((CFComparatorFunction)CFStringCompare); }
|
||||
|
||||
bool GetBool(UInt32 inIndex, bool& outValue) const;
|
||||
bool GetSInt32(UInt32 inIndex, SInt32& outItem) const;
|
||||
bool GetUInt32(UInt32 inIndex, UInt32& outItem) const;
|
||||
bool GetSInt64(UInt32 inIndex, SInt64& outItem) const;
|
||||
bool GetUInt64(UInt32 inIndex, UInt64& outItem) const;
|
||||
bool GetFloat32(UInt32 inIndex, Float32& outItem) const;
|
||||
bool GetFloat64(UInt32 inIndex, Float64& outItem) const;
|
||||
bool Get4CC(UInt32 inIndex, UInt32& outValue) const;
|
||||
bool GetString(UInt32 inIndex, CFStringRef& outItem) const;
|
||||
bool GetArray(UInt32 inIndex, CFArrayRef& outItem) const;
|
||||
bool GetDictionary(UInt32 inIndex, CFDictionaryRef& outItem) const;
|
||||
bool GetData(UInt32 inIndex, CFDataRef& outItem) const;
|
||||
bool GetUUID(UInt32 inIndex, CFUUIDRef& outItem) const;
|
||||
bool GetCFType(UInt32 inIndex, CFTypeRef& outItem) const;
|
||||
|
||||
void GetCACFString(UInt32 inIndex, CACFString& outItem) const;
|
||||
void GetCACFArray(UInt32 inIndex, CACFArray& outItem) const;
|
||||
void GetCACFDictionary(UInt32 inIndex, CACFDictionary& outItem) const;
|
||||
|
||||
bool AppendBool(bool inItem);
|
||||
bool AppendSInt32(SInt32 inItem);
|
||||
bool AppendUInt32(UInt32 inItem);
|
||||
bool AppendSInt64(SInt64 inItem);
|
||||
bool AppendUInt64(UInt64 inItem);
|
||||
bool AppendFloat32(Float32 inItem);
|
||||
bool AppendFloat64(Float64 inItem);
|
||||
bool AppendString(const CFStringRef inItem);
|
||||
bool AppendArray(const CFArrayRef inItem);
|
||||
bool AppendDictionary(const CFDictionaryRef inItem);
|
||||
bool AppendData(const CFDataRef inItem);
|
||||
bool AppendCFType(const CFTypeRef inItem);
|
||||
|
||||
bool InsertBool(UInt32 inIndex, bool inItem);
|
||||
bool InsertSInt32(UInt32 inIndex, SInt32 inItem);
|
||||
bool InsertUInt32(UInt32 inIndex, UInt32 inItem);
|
||||
bool InsertSInt64(UInt32 inIndex, SInt64 inItem);
|
||||
bool InsertUInt64(UInt32 inIndex, UInt64 inItem);
|
||||
bool InsertFloat32(UInt32 inIndex, Float32 inItem);
|
||||
bool InsertFloat64(UInt32 inIndex, Float64 inItem);
|
||||
bool InsertString(UInt32 inIndex, const CFStringRef inItem);
|
||||
bool InsertArray(UInt32 inIndex, const CFArrayRef inItem);
|
||||
bool InsertDictionary(UInt32 inIndex, const CFDictionaryRef inItem);
|
||||
bool InsertData(UInt32 inIndex, const CFDataRef inItem);
|
||||
bool InsertCFType(UInt32 inIndex, const CFTypeRef inItem);
|
||||
|
||||
bool SetBool(UInt32 inIndex, bool inItem);
|
||||
bool SetSInt32(UInt32 inIndex, SInt32 inItem);
|
||||
bool SetUInt32(UInt32 inIndex, UInt32 inItem);
|
||||
bool SetSInt64(UInt32 inIndex, SInt64 inItem);
|
||||
bool SetUInt64(UInt32 inIndex, UInt64 inItem);
|
||||
bool SetFloat32(UInt32 inIndex, Float32 inItem);
|
||||
bool SetFloat64(UInt32 inIndex, Float64 inItem);
|
||||
bool SetString(UInt32 inIndex, const CFStringRef inItem);
|
||||
bool SetArray(UInt32 inIndex, const CFArrayRef inItem);
|
||||
bool SetDictionary(UInt32 inIndex, const CFDictionaryRef inItem);
|
||||
bool SetData(UInt32 inIndex, const CFDataRef inItem);
|
||||
bool SetCFType(UInt32 inIndex, const CFTypeRef inItem);
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
CFMutableArrayRef mCFArray;
|
||||
bool mRelease;
|
||||
bool mMutable;
|
||||
|
||||
CACFArray(const void*); // prevent accidental instantiation with a pointer via bool constructor
|
||||
};
|
||||
|
||||
#endif
|
||||
581
driver/PublicUtility/CACFDictionary.cpp
Normal file
581
driver/PublicUtility/CACFDictionary.cpp
Normal file
@@ -0,0 +1,581 @@
|
||||
/*
|
||||
File: CACFDictionary.cpp
|
||||
Abstract: CACFDictionary.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.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CACFDictionary.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CACFArray.h"
|
||||
#include "CACFNumber.h"
|
||||
#include "CACFString.h"
|
||||
|
||||
//=============================================================================
|
||||
// CACFDictionary
|
||||
//=============================================================================
|
||||
|
||||
bool CACFDictionary::HasKey(const CFStringRef inKey) const
|
||||
{
|
||||
return CFDictionaryContainsKey(mCFDictionary, inKey) != 0;
|
||||
}
|
||||
|
||||
UInt32 CACFDictionary::Size () const
|
||||
{
|
||||
return mCFDictionary ? ToUInt32(CFDictionaryGetCount(mCFDictionary)) : 0;
|
||||
}
|
||||
|
||||
void CACFDictionary::GetKeys (const void **keys) const
|
||||
{
|
||||
CFDictionaryGetKeysAndValues(mCFDictionary, keys, NULL);
|
||||
}
|
||||
|
||||
void CACFDictionary::GetKeysAndValues (const void **keys, const void **values) const
|
||||
{
|
||||
CFDictionaryGetKeysAndValues(mCFDictionary, keys, values);
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetBool(const CFStringRef inKey, bool& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFBooleanGetTypeID()))
|
||||
{
|
||||
outValue = CFBooleanGetValue(static_cast<CFBooleanRef>(theValue));
|
||||
theAnswer = true;
|
||||
}
|
||||
else if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
SInt32 theNumericValue = 0;
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &theNumericValue);
|
||||
outValue = theNumericValue != 0;
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetSInt32(const CFStringRef inKey, SInt32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetUInt32(const CFStringRef inKey, UInt32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetSInt64(const CFStringRef inKey, SInt64& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt64Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetUInt64(const CFStringRef inKey, UInt64& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt64Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetFloat32FromString(const CFStringRef inKey, Float32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<Float32>(CFStringGetDoubleValue(static_cast<CFStringRef>(theValue)));
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetUInt32FromString(const CFStringRef inKey, UInt32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
outValue = CFStringGetIntValue(static_cast<CFStringRef>(theValue));
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetFloat32(const CFStringRef inKey, Float32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberFloat32Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetFloat64(const CFStringRef inKey, Float64& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberFloat64Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetFixed32(const CFStringRef inKey, Float32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
SInt32 theFixed32 = 0;
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &theFixed32);
|
||||
|
||||
// this is a 16.16 value so convert it to a float
|
||||
Float32 theSign = theFixed32 < 0 ? -1.0f : 1.0f;
|
||||
theFixed32 *= (SInt32)theSign;
|
||||
Float32 theWholePart = (theFixed32 & 0x7FFF0000) >> 16;
|
||||
Float32 theFractPart = theFixed32 & 0x0000FFFF;
|
||||
theFractPart /= 65536.0f;
|
||||
outValue = theSign * (theWholePart + theFractPart);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetFixed64(const CFStringRef inKey, Float64& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
SInt64 theFixed64 = 0;
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt64Type, &theFixed64);
|
||||
outValue = static_cast<Float64>(theFixed64 >> 32);
|
||||
outValue += static_cast<Float64>(theFixed64 & 0x00000000FFFFFFFFLL) / static_cast<Float64>(0x0000000100000000LL);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::Get4CC(const CFStringRef inKey, UInt32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
else if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
CFStringRef theString = static_cast<CFStringRef>(theValue);
|
||||
if(CFStringGetLength(theString) == 4)
|
||||
{
|
||||
char theCString[5];
|
||||
CFStringGetCString(theString, theCString, 5, kCFStringEncodingASCII);
|
||||
outValue = CFSwapInt32BigToHost(*reinterpret_cast<UInt32*>(theCString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetString(const CFStringRef inKey, CFStringRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFStringRef>(theValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetArray(const CFStringRef inKey, CFArrayRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFArrayGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFArrayRef>(theValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetDictionary(const CFStringRef inKey, CFDictionaryRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFDictionaryGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFDictionaryRef>(theValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetData(const CFStringRef inKey, CFDataRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFDataGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFDataRef>(theValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetCFType(const CFStringRef inKey, CFTypeRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if(mCFDictionary != NULL)
|
||||
{
|
||||
outValue = CFDictionaryGetValue(mCFDictionary, inKey);
|
||||
theAnswer = (outValue != NULL);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetURL(const CFStringRef inKey, CFURLRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFURLGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFURLRef>(theValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetCFTypeWithCStringKey(const char* inKey, CFTypeRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if(mCFDictionary != NULL)
|
||||
{
|
||||
CACFString theKey(inKey);
|
||||
if(theKey.IsValid())
|
||||
{
|
||||
theAnswer = GetCFType(theKey.GetCFString(), outValue);
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CACFDictionary::GetCACFString(const CFStringRef inKey, CACFString& outValue) const
|
||||
{
|
||||
outValue = static_cast<CFStringRef>(NULL);
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFStringRef>(theValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CACFDictionary::GetCACFArray(const CFStringRef inKey, CACFArray& outValue) const
|
||||
{
|
||||
outValue = static_cast<CFArrayRef>(NULL);
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFArrayGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFArrayRef>(theValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CACFDictionary::GetCACFDictionary(const CFStringRef inKey, CACFDictionary& outValue) const
|
||||
{
|
||||
outValue = static_cast<CFDictionaryRef>(NULL);
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFDictionaryGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFDictionaryRef>(theValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddBool(const CFStringRef inKey, bool inValue)
|
||||
{
|
||||
CACFBoolean theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFBoolean());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddSInt32(const CFStringRef inKey, SInt32 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddUInt32(const CFStringRef inKey, UInt32 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddSInt64(const CFStringRef inKey, SInt64 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddUInt64(const CFStringRef inKey, UInt64 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddFloat32(const CFStringRef inKey, Float32 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddFloat64(const CFStringRef inKey, Float64 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddNumber(const CFStringRef inKey, const CFNumberRef inValue)
|
||||
{
|
||||
return AddCFType(inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddString(const CFStringRef inKey, const CFStringRef inValue)
|
||||
{
|
||||
return AddCFType(inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddArray(const CFStringRef inKey, const CFArrayRef inValue)
|
||||
{
|
||||
return AddCFType(inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddDictionary(const CFStringRef inKey, const CFDictionaryRef inValue)
|
||||
{
|
||||
return AddCFType(inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddData(const CFStringRef inKey, const CFDataRef inValue)
|
||||
{
|
||||
return AddCFType(inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddURL(const CFStringRef inKey, const CFURLRef inValue)
|
||||
{
|
||||
return AddCFType (inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddCFTypeWithCStringKey(const char* inKey, const CFTypeRef inValue)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if (inKey)
|
||||
{
|
||||
CACFString theKey(inKey);
|
||||
if(theKey.IsValid())
|
||||
{
|
||||
theAnswer = AddCFType(theKey.GetCFString(), inValue);
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddCString(const CFStringRef inKey, const char* inValue)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if (inValue)
|
||||
{
|
||||
CACFString theValue(inValue);
|
||||
if(theValue.IsValid())
|
||||
{
|
||||
theAnswer = AddCFType(inKey, theValue.GetCFString());
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddCFType(const CFStringRef inKey, const CFTypeRef inValue)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if(mMutable && (mCFDictionary != NULL) && inValue)
|
||||
{
|
||||
CFDictionarySetValue(mCFDictionary, inKey, inValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
176
driver/PublicUtility/CACFDictionary.h
Normal file
176
driver/PublicUtility/CACFDictionary.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
File: CACFDictionary.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.
|
||||
|
||||
*/
|
||||
#if !defined(__CACFDictionary_h__)
|
||||
#define __CACFDictionary_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
// System Includes
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#else
|
||||
#include <CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Types
|
||||
//=============================================================================
|
||||
|
||||
class CACFArray;
|
||||
class CACFString;
|
||||
|
||||
//=============================================================================
|
||||
// CACFDictionary
|
||||
//=============================================================================
|
||||
|
||||
class CACFDictionary
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CACFDictionary() : mCFDictionary(CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)), mRelease(true), mMutable(true) {}
|
||||
explicit CACFDictionary(bool inRelease) : mCFDictionary(CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)), mRelease(inRelease), mMutable(true) {}
|
||||
CACFDictionary(CFDictionaryRef inCFDictionary, bool inRelease) : mCFDictionary(const_cast<CFMutableDictionaryRef>(inCFDictionary)), mRelease(inRelease), mMutable(false) {}
|
||||
CACFDictionary(CFMutableDictionaryRef inCFDictionary, bool inRelease) : mCFDictionary(inCFDictionary), mRelease(inRelease), mMutable(true) {}
|
||||
CACFDictionary(const CACFDictionary& inDictionary) : mCFDictionary(inDictionary.mCFDictionary), mRelease(inDictionary.mRelease), mMutable(inDictionary.mMutable) { Retain(); }
|
||||
CACFDictionary& operator=(const CACFDictionary& inDictionary) { Release(); mCFDictionary = inDictionary.mCFDictionary; mRelease = inDictionary.mRelease; mMutable = inDictionary.mMutable; Retain(); return *this; }
|
||||
CACFDictionary& operator=(CFDictionaryRef inDictionary) { Release(); mCFDictionary = const_cast<CFMutableDictionaryRef>(inDictionary); mMutable = false; Retain(); return *this; }
|
||||
CACFDictionary& operator=(CFMutableDictionaryRef inDictionary) { Release(); mCFDictionary = inDictionary; mMutable = true; Retain(); return *this; }
|
||||
~CACFDictionary() { Release(); }
|
||||
|
||||
private:
|
||||
void Retain() { if(mRelease && (mCFDictionary != NULL)) { CFRetain(mCFDictionary); } }
|
||||
void Release() { if(mRelease && (mCFDictionary != NULL)) { CFRelease(mCFDictionary); } }
|
||||
|
||||
// Attributes
|
||||
public:
|
||||
bool IsValid() const { return mCFDictionary != NULL; }
|
||||
bool IsMutable() const { return mMutable;}
|
||||
bool CanModify() const { return mMutable && (mCFDictionary != NULL); }
|
||||
|
||||
bool WillRelease() const { return mRelease; }
|
||||
void ShouldRelease(bool inRelease) { mRelease = inRelease; }
|
||||
|
||||
CFDictionaryRef GetDict() const { return mCFDictionary; }
|
||||
CFDictionaryRef GetCFDictionary() const { return mCFDictionary; }
|
||||
CFDictionaryRef CopyCFDictionary() const { if(mCFDictionary != NULL) { CFRetain(mCFDictionary); } return mCFDictionary; }
|
||||
|
||||
CFMutableDictionaryRef GetMutableDict() { return mCFDictionary; }
|
||||
CFMutableDictionaryRef GetCFMutableDictionary() const { return mCFDictionary; }
|
||||
CFMutableDictionaryRef CopyCFMutableDictionary() const { if(mCFDictionary != NULL) { CFRetain(mCFDictionary); } return mCFDictionary; }
|
||||
void SetCFMutableDictionaryFromCopy(CFDictionaryRef inDictionary, bool inRelease = true) { Release(); mCFDictionary = CFDictionaryCreateMutableCopy(NULL, 0, inDictionary); mMutable = true; mRelease = inRelease; }
|
||||
void SetCFMutableDictionaryToEmpty(bool inRelease = true) { Release(); mCFDictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); mMutable = true; mRelease = inRelease; }
|
||||
|
||||
CFPropertyListRef AsPropertyList() const { return mCFDictionary; }
|
||||
OSStatus GetDictIfMutable(CFMutableDictionaryRef& outDict) const { OSStatus theAnswer = -1; if(mMutable) { outDict = mCFDictionary; theAnswer = 0; } return theAnswer; }
|
||||
|
||||
// Item Operations
|
||||
public:
|
||||
bool HasKey(const CFStringRef inKey) const;
|
||||
UInt32 Size() const;
|
||||
void GetKeys(const void** keys) const;
|
||||
void GetKeysAndValues (const void **keys, const void **values) const;
|
||||
|
||||
bool GetBool(const CFStringRef inKey, bool& outValue) const;
|
||||
bool GetSInt32(const CFStringRef inKey, SInt32& outValue) const;
|
||||
bool GetUInt32(const CFStringRef inKey, UInt32& outValue) const;
|
||||
bool GetUInt32FromString(const CFStringRef inKey, UInt32& outValue) const;
|
||||
bool GetSInt64(const CFStringRef inKey, SInt64& outValue) const;
|
||||
bool GetUInt64(const CFStringRef inKey, UInt64& outValue) const;
|
||||
bool GetFloat32(const CFStringRef inKey, Float32& outValue) const;
|
||||
bool GetFloat32FromString(const CFStringRef inKey, Float32& outValue) const;
|
||||
bool GetFloat64(const CFStringRef inKey, Float64& outValue) const;
|
||||
bool GetFixed32(const CFStringRef inKey, Float32& outValue) const;
|
||||
bool GetFixed64(const CFStringRef inKey, Float64& outValue) const;
|
||||
bool Get4CC(const CFStringRef inKey, UInt32& outValue) const;
|
||||
bool GetString(const CFStringRef inKey, CFStringRef& outValue) const;
|
||||
bool GetArray(const CFStringRef inKey, CFArrayRef& outValue) const;
|
||||
bool GetDictionary(const CFStringRef inKey, CFDictionaryRef& outValue) const;
|
||||
bool GetData(const CFStringRef inKey, CFDataRef& outValue) const;
|
||||
bool GetCFType(const CFStringRef inKey, CFTypeRef& outValue) const;
|
||||
bool GetURL(const CFStringRef inKey, CFURLRef& outValue) const;
|
||||
bool GetCFTypeWithCStringKey(const char* inKey, CFTypeRef& outValue) const;
|
||||
|
||||
void GetCACFString(const CFStringRef inKey, CACFString& outItem) const;
|
||||
void GetCACFArray(const CFStringRef inKey, CACFArray& outItem) const;
|
||||
void GetCACFDictionary(const CFStringRef inKey, CACFDictionary& outItem) const;
|
||||
|
||||
bool AddBool(const CFStringRef inKey, bool inValue);
|
||||
bool AddSInt32(const CFStringRef inKey, SInt32 inValue);
|
||||
bool AddUInt32(const CFStringRef inKey, UInt32 inValue);
|
||||
bool AddSInt64(const CFStringRef inKey, SInt64 inValue);
|
||||
bool AddUInt64(const CFStringRef inKey, UInt64 inValue);
|
||||
bool AddFloat32(const CFStringRef inKey, Float32 inValue);
|
||||
bool AddFloat64(const CFStringRef inKey, Float64 inValue);
|
||||
bool AddNumber(const CFStringRef inKey, const CFNumberRef inValue);
|
||||
bool AddString(const CFStringRef inKey, const CFStringRef inValue);
|
||||
bool AddArray(const CFStringRef inKey, const CFArrayRef inValue);
|
||||
bool AddDictionary(const CFStringRef inKey, const CFDictionaryRef inValue);
|
||||
bool AddData(const CFStringRef inKey, const CFDataRef inValue);
|
||||
bool AddCFType(const CFStringRef inKey, const CFTypeRef inValue);
|
||||
bool AddURL(const CFStringRef inKey, const CFURLRef inValue);
|
||||
|
||||
bool AddCFTypeWithCStringKey(const char* inKey, const CFTypeRef inValue);
|
||||
bool AddCString(const CFStringRef inKey, const char* inValue);
|
||||
|
||||
void RemoveKey(const CFStringRef inKey) { if(CanModify()) { CFDictionaryRemoveValue(mCFDictionary, inKey); } }
|
||||
void Clear() { if(CanModify()) { CFDictionaryRemoveAllValues(mCFDictionary); } }
|
||||
|
||||
void Show() { CFShow(mCFDictionary); }
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
CFMutableDictionaryRef mCFDictionary;
|
||||
bool mRelease;
|
||||
bool mMutable;
|
||||
|
||||
CACFDictionary(const void*); // prevent accidental instantiation with a pointer via bool constructor
|
||||
};
|
||||
|
||||
#endif //__CACFDictionary_h__
|
||||
83
driver/PublicUtility/CACFNumber.cpp
Normal file
83
driver/PublicUtility/CACFNumber.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
File: CACFNumber.cpp
|
||||
Abstract: CACFNumber.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.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#include "CACFNumber.h"
|
||||
|
||||
//=============================================================================
|
||||
// CACFNumber
|
||||
//=============================================================================
|
||||
|
||||
Float32 CACFNumber::GetFixed32() const
|
||||
{
|
||||
SInt32 theFixedValue = GetSInt32();
|
||||
|
||||
// this is a 16.16 value so convert it to a float
|
||||
Float32 theSign = theFixedValue < 0 ? -1.0f : 1.0f;
|
||||
theFixedValue *= (SInt32)theSign;
|
||||
Float32 theWholePart = (theFixedValue & 0x7FFF0000) >> 16;
|
||||
Float32 theFractPart = theFixedValue & 0x0000FFFF;
|
||||
theFractPart /= 65536.0f;
|
||||
|
||||
return theSign * (theWholePart + theFractPart);
|
||||
}
|
||||
|
||||
Float64 CACFNumber::GetFixed64() const
|
||||
{
|
||||
SInt64 theFixedValue = GetSInt64();
|
||||
|
||||
// this is a 32.32 value so convert it to a double
|
||||
Float64 theSign = theFixedValue < 0 ? -1.0 : 1.0;
|
||||
theFixedValue *= (SInt64)theSign;
|
||||
Float64 theWholePart = (theFixedValue & 0x7FFFFFFF00000000LL) >> 32;
|
||||
Float64 theFractPart = theFixedValue & 0x00000000FFFFFFFFLL;
|
||||
theFractPart /= 4294967296.0;
|
||||
|
||||
return theSign * (theWholePart + theFractPart);
|
||||
}
|
||||
151
driver/PublicUtility/CACFNumber.h
Normal file
151
driver/PublicUtility/CACFNumber.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
File: CACFNumber.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.
|
||||
|
||||
*/
|
||||
#if !defined(__CACFNumber_h__)
|
||||
#define __CACFNumber_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <CoreFoundation/CFNumber.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#include <CFNumber.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CACFBoolean
|
||||
//=============================================================================
|
||||
|
||||
class CACFBoolean
|
||||
{
|
||||
// Construction/Destruction
|
||||
public:
|
||||
explicit CACFBoolean(CFBooleanRef inCFBoolean) : mCFBoolean(inCFBoolean), mWillRelease(true) {}
|
||||
CACFBoolean(CFBooleanRef inCFBoolean, bool inWillRelease) : mCFBoolean(inCFBoolean), mWillRelease(inWillRelease) {}
|
||||
explicit CACFBoolean(bool inValue) : mCFBoolean(inValue ? kCFBooleanTrue : kCFBooleanFalse), mWillRelease(true) { Retain(); }
|
||||
~CACFBoolean() { Release(); }
|
||||
CACFBoolean(const CACFBoolean& inBoolean) : mCFBoolean(inBoolean.mCFBoolean), mWillRelease(inBoolean.mWillRelease) { Retain(); }
|
||||
CACFBoolean& operator=(const CACFBoolean& inBoolean) { Release(); mCFBoolean = inBoolean.mCFBoolean; mWillRelease = inBoolean.mWillRelease; Retain(); return *this; }
|
||||
CACFBoolean& operator=(CFBooleanRef inCFBoolean) { Release(); mCFBoolean = inCFBoolean; mWillRelease = true; return *this; }
|
||||
|
||||
private:
|
||||
void Retain() { if(mWillRelease && (mCFBoolean != NULL)) { CFRetain(mCFBoolean); } }
|
||||
void Release() { if(mWillRelease && (mCFBoolean != NULL)) { CFRelease(mCFBoolean); } }
|
||||
|
||||
CFBooleanRef mCFBoolean;
|
||||
bool mWillRelease;
|
||||
|
||||
// Operations
|
||||
public:
|
||||
void AllowRelease() { mWillRelease = true; }
|
||||
void DontAllowRelease() { mWillRelease = false; }
|
||||
bool IsValid() { return mCFBoolean != NULL; }
|
||||
|
||||
// Value Access
|
||||
public:
|
||||
CFBooleanRef GetCFBoolean() const { return mCFBoolean; }
|
||||
CFBooleanRef CopyCFBoolean() const { if(mCFBoolean != NULL) { CFRetain(mCFBoolean); } return mCFBoolean; }
|
||||
|
||||
bool GetBoolean() const { bool theAnswer = false; if(mCFBoolean != NULL) { theAnswer = CFEqual(mCFBoolean, kCFBooleanTrue); } return theAnswer; }
|
||||
|
||||
CACFBoolean(const void*); // prevent accidental instantiation with a pointer via bool constructor
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// CACFNumber
|
||||
//=============================================================================
|
||||
|
||||
class CACFNumber
|
||||
{
|
||||
// Construction/Destruction
|
||||
public:
|
||||
explicit CACFNumber(CFNumberRef inCFNumber) : mCFNumber(inCFNumber), mWillRelease(true) {}
|
||||
CACFNumber(CFNumberRef inCFNumber, bool inWillRelease) : mCFNumber(inCFNumber), mWillRelease(inWillRelease) {}
|
||||
CACFNumber(SInt32 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberSInt32Type, &inValue)), mWillRelease(true) {}
|
||||
CACFNumber(UInt32 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberSInt32Type, &inValue)), mWillRelease(true) {}
|
||||
CACFNumber(SInt64 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberSInt64Type, &inValue)), mWillRelease(true) {}
|
||||
CACFNumber(UInt64 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberSInt64Type, &inValue)), mWillRelease(true) {}
|
||||
CACFNumber(Float32 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberFloat32Type, &inValue)), mWillRelease(true) {}
|
||||
CACFNumber(Float64 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberFloat64Type, &inValue)), mWillRelease(true) {}
|
||||
~CACFNumber() { Release(); }
|
||||
CACFNumber(const CACFNumber& inNumber) : mCFNumber(inNumber.mCFNumber), mWillRelease(inNumber.mWillRelease) { Retain(); }
|
||||
CACFNumber& operator=(const CACFNumber& inNumber) { Release(); mCFNumber = inNumber.mCFNumber; mWillRelease = inNumber.mWillRelease; Retain(); return *this; }
|
||||
CACFNumber& operator=(CFNumberRef inCFNumber) { Release(); mCFNumber = inCFNumber; mWillRelease = true; return *this; }
|
||||
|
||||
private:
|
||||
void Retain() { if(mWillRelease && (mCFNumber != NULL)) { CFRetain(mCFNumber); } }
|
||||
void Release() { if(mWillRelease && (mCFNumber != NULL)) { CFRelease(mCFNumber); } }
|
||||
|
||||
CFNumberRef mCFNumber;
|
||||
bool mWillRelease;
|
||||
|
||||
// Operations
|
||||
public:
|
||||
void AllowRelease() { mWillRelease = true; }
|
||||
void DontAllowRelease() { mWillRelease = false; }
|
||||
bool IsValid() const { return mCFNumber != NULL; }
|
||||
|
||||
// Value Access
|
||||
public:
|
||||
CFNumberRef GetCFNumber() const { return mCFNumber; }
|
||||
CFNumberRef CopyCFNumber() const { if(mCFNumber != NULL) { CFRetain(mCFNumber); } return mCFNumber; }
|
||||
|
||||
SInt8 GetSInt8() const { SInt8 theAnswer = 0; if(mCFNumber != NULL) { CFNumberGetValue(mCFNumber, kCFNumberSInt8Type, &theAnswer); } return theAnswer; }
|
||||
SInt32 GetSInt32() const { SInt32 theAnswer = 0; if(mCFNumber != NULL) { CFNumberGetValue(mCFNumber, kCFNumberSInt32Type, &theAnswer); } return theAnswer; }
|
||||
UInt32 GetUInt32() const { UInt32 theAnswer = 0; if(mCFNumber != NULL) { CFNumberGetValue(mCFNumber, kCFNumberSInt32Type, &theAnswer); } return theAnswer; }
|
||||
Float32 GetFloat32() const { Float32 theAnswer = 0.0f; if(mCFNumber != NULL) { CFNumberGetValue(mCFNumber, kCFNumberFloat32Type, &theAnswer); } return theAnswer; }
|
||||
Float32 GetFixed32() const;
|
||||
Float64 GetFixed64() const;
|
||||
SInt64 GetSInt64() const { SInt64 theAnswer = 0; if(mCFNumber != NULL) { CFNumberGetValue(mCFNumber, kCFNumberSInt64Type, &theAnswer); } return theAnswer; }
|
||||
|
||||
CACFNumber(const void*); // prevent accidental instantiation with a pointer via bool constructor
|
||||
};
|
||||
|
||||
#endif
|
||||
110
driver/PublicUtility/CACFString.cpp
Normal file
110
driver/PublicUtility/CACFString.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
File: CACFString.cpp
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#include "CACFString.h"
|
||||
|
||||
//=============================================================================
|
||||
// CACFString
|
||||
//=============================================================================
|
||||
|
||||
UInt32 CACFString::GetStringByteLength(CFStringRef inCFString, CFStringEncoding inEncoding)
|
||||
{
|
||||
CFIndex theAnswer = 0;
|
||||
|
||||
if(inCFString != NULL)
|
||||
{
|
||||
CFRange theRange = { 0, CFStringGetLength(inCFString) };
|
||||
CFStringGetBytes(inCFString, theRange, inEncoding, 0, false, NULL, 0x7FFFFFFF, &theAnswer);
|
||||
}
|
||||
|
||||
return UInt32(theAnswer);
|
||||
}
|
||||
|
||||
void CACFString::GetCString(CFStringRef inCFString, char* outString, UInt32& ioStringSize, CFStringEncoding inEncoding)
|
||||
{
|
||||
if(ioStringSize > 0)
|
||||
{
|
||||
if(inCFString != NULL)
|
||||
{
|
||||
CFIndex theLength = 0;
|
||||
CFRange theRange = { 0, CFStringGetLength(inCFString) };
|
||||
CFStringGetBytes(inCFString, theRange, inEncoding, 0, false, (UInt8*)outString, static_cast<CFIndex>(ioStringSize - 1), &theLength);
|
||||
outString[theLength] = 0;
|
||||
ioStringSize = ToUInt32(theLength) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
outString[0] = 0;
|
||||
ioStringSize = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CACFString::GetUnicodeString(CFStringRef inCFString, UInt16* outString, UInt32& ioStringSize)
|
||||
{
|
||||
if(ioStringSize > 0)
|
||||
{
|
||||
if(inCFString != NULL)
|
||||
{
|
||||
CFRange theStringRange = { 0, CFStringGetLength(inCFString) };
|
||||
if(static_cast<UInt32>(theStringRange.length) > ioStringSize)
|
||||
{
|
||||
theStringRange.length = static_cast<CFIndex>(ioStringSize);
|
||||
}
|
||||
CFStringGetCharacters(inCFString, theStringRange, outString);
|
||||
ioStringSize = ToUInt32(theStringRange.length);
|
||||
}
|
||||
else
|
||||
{
|
||||
outString[0] = 0;
|
||||
ioStringSize = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
180
driver/PublicUtility/CACFString.h
Normal file
180
driver/PublicUtility/CACFString.h
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
File: CACFString.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CACFString_h__)
|
||||
#define __CACFString_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <CoreFoundation/CFString.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#include <CFString.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CACFString
|
||||
//
|
||||
// Notes
|
||||
// - Using the AssignWithoutRetain() method will fool the static analyzer into thinking that the
|
||||
// CFString being assigned will be leaked. This is because the static analyzer is not smart
|
||||
// enough to understand that the destructor will release the object.
|
||||
//=============================================================================
|
||||
|
||||
class CACFString
|
||||
{
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CACFString() : mCFString(NULL), mWillRelease(true) {}
|
||||
CACFString(CFStringRef inCFString, bool inWillRelease = true) : mCFString(inCFString), mWillRelease(inWillRelease) {}
|
||||
CACFString(const char* inCString, bool inWillRelease = true) : mCFString(CFStringCreateWithCString(NULL, inCString, kCFStringEncodingASCII)), mWillRelease(inWillRelease) {}
|
||||
CACFString(const char* inCString, CFStringEncoding inCStringEncoding, bool inWillRelease = true) : mCFString(CFStringCreateWithCString(NULL, inCString, inCStringEncoding)), mWillRelease(inWillRelease) {}
|
||||
~CACFString() { Release(); }
|
||||
CACFString(const CACFString& inString) : mCFString(inString.mCFString), mWillRelease(inString.mWillRelease) { Retain(); }
|
||||
CACFString& operator=(const CACFString& inString) { if (inString.mCFString != mCFString) { Release(); mCFString = inString.mCFString; mWillRelease = inString.mWillRelease; Retain(); } return *this; }
|
||||
CACFString& operator=(CFStringRef inCFString) { if (inCFString != mCFString) { Release(); mCFString = inCFString; } mWillRelease = true; Retain(); return *this; }
|
||||
void AssignWithoutRetain(CFStringRef inCFString) { if (inCFString != mCFString) { Release(); mCFString = inCFString; } mWillRelease = true; }
|
||||
|
||||
private:
|
||||
void Retain() { if(mWillRelease && (mCFString != NULL)) { CFRetain(mCFString); } }
|
||||
void Release() { if(mWillRelease && (mCFString != NULL)) { CFRelease(mCFString); } }
|
||||
|
||||
CFStringRef mCFString;
|
||||
bool mWillRelease;
|
||||
|
||||
// Operations
|
||||
public:
|
||||
void AllowRelease() { mWillRelease = true; }
|
||||
void DontAllowRelease() { mWillRelease = false; }
|
||||
bool IsValid() const { return mCFString != NULL; }
|
||||
bool IsEqualTo(CFStringRef inString) const { bool theAnswer = false; if(mCFString != NULL) { theAnswer = CFStringCompare(inString, mCFString, 0) == kCFCompareEqualTo; } return theAnswer; }
|
||||
bool StartsWith(CFStringRef inString) const { bool theAnswer = false; if(mCFString != NULL) { theAnswer = CFStringHasPrefix(mCFString, inString); } return theAnswer; }
|
||||
bool EndsWith(CFStringRef inString) const { bool theAnswer = false; if(mCFString != NULL) { theAnswer = CFStringHasSuffix(mCFString, inString); } return theAnswer; }
|
||||
|
||||
// Value Access
|
||||
public:
|
||||
CFStringRef GetCFString() const { return mCFString; }
|
||||
CFStringRef CopyCFString() const { if(mCFString != NULL) { CFRetain(mCFString); } return mCFString; }
|
||||
const CFStringRef* GetPointerToStorage() const { return &mCFString; }
|
||||
CFStringRef& GetStorage() { Release(); mWillRelease = true; return mCFString; }
|
||||
UInt32 GetLength() const { UInt32 theAnswer = 0; if(mCFString != NULL) { theAnswer = ToUInt32(CFStringGetLength(mCFString)); } return theAnswer; }
|
||||
UInt32 GetByteLength(CFStringEncoding inEncoding = kCFStringEncodingUTF8) const { UInt32 theAnswer = 0; if(mCFString != NULL) { theAnswer = GetStringByteLength(mCFString, inEncoding); } return theAnswer; }
|
||||
void GetCString(char* outString, UInt32& ioStringSize, CFStringEncoding inEncoding = kCFStringEncodingUTF8) const { GetCString(mCFString, outString, ioStringSize, inEncoding); }
|
||||
void GetUnicodeString(UInt16* outString, UInt32& ioStringSize) const { GetUnicodeString(mCFString, outString, ioStringSize); }
|
||||
SInt32 GetAsInteger() { return GetAsInteger(mCFString); }
|
||||
Float64 GetAsFloat64() { return GetAsFloat64(mCFString); }
|
||||
|
||||
static UInt32 GetStringLength(CFStringRef inCFString) { UInt32 theAnswer = 0; if(inCFString != NULL) { theAnswer = ToUInt32(CFStringGetLength(inCFString)); } return theAnswer; }
|
||||
static UInt32 GetStringByteLength(CFStringRef inCFString, CFStringEncoding inEncoding = kCFStringEncodingUTF8);
|
||||
static void GetCString(CFStringRef inCFString, char* outString, UInt32& ioStringSize, CFStringEncoding inEncoding = kCFStringEncodingUTF8);
|
||||
static void GetUnicodeString(CFStringRef inCFString, UInt16* outString, UInt32& ioStringSize);
|
||||
static SInt32 GetAsInteger(CFStringRef inCFString) { SInt32 theAnswer = 0; if(inCFString != NULL){ theAnswer = CFStringGetIntValue(inCFString); } return theAnswer; }
|
||||
static Float64 GetAsFloat64(CFStringRef inCFString) { Float64 theAnswer = 0; if(inCFString != NULL){ theAnswer = CFStringGetDoubleValue(inCFString); } return theAnswer; }
|
||||
|
||||
};
|
||||
|
||||
inline bool operator<(const CACFString& x, const CACFString& y) { return CFStringCompare(x.GetCFString(), y.GetCFString(), 0) == kCFCompareLessThan; }
|
||||
inline bool operator==(const CACFString& x, const CACFString& y) { return CFStringCompare(x.GetCFString(), y.GetCFString(), 0) == kCFCompareEqualTo; }
|
||||
inline bool operator!=(const CACFString& x, const CACFString& y) { return !(x == y); }
|
||||
inline bool operator<=(const CACFString& x, const CACFString& y) { return (x < y) || (x == y); }
|
||||
inline bool operator>=(const CACFString& x, const CACFString& y) { return !(x < y); }
|
||||
inline bool operator>(const CACFString& x, const CACFString& y) { return !((x < y) || (x == y)); }
|
||||
|
||||
//=============================================================================
|
||||
// CACFMutableString
|
||||
//=============================================================================
|
||||
|
||||
class CACFMutableString
|
||||
{
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CACFMutableString() : mCFMutableString(NULL), mWillRelease(true) {}
|
||||
CACFMutableString(CFMutableStringRef inCFMutableString, bool inWillRelease = true) : mCFMutableString(inCFMutableString), mWillRelease(inWillRelease) {}
|
||||
CACFMutableString(CFStringRef inStringToCopy, bool /*inMakeCopy*/, bool inWillRelease = true) : mCFMutableString(CFStringCreateMutableCopy(NULL, 0, inStringToCopy)), mWillRelease(inWillRelease) {}
|
||||
CACFMutableString(const char* inCString, bool inWillRelease = true) : mCFMutableString(NULL), mWillRelease(inWillRelease) { CACFString theString(inCString); mCFMutableString = CFStringCreateMutableCopy(NULL, 0, theString.GetCFString()); }
|
||||
CACFMutableString(const char* inCString, CFStringEncoding inCStringEncoding, bool inWillRelease = true) : mCFMutableString(NULL), mWillRelease(inWillRelease) { CACFString theString(inCString, inCStringEncoding); mCFMutableString = CFStringCreateMutableCopy(NULL, 0, theString.GetCFString()); }
|
||||
~CACFMutableString() { Release(); }
|
||||
CACFMutableString(const CACFMutableString& inString) : mCFMutableString(inString.mCFMutableString), mWillRelease(inString.mWillRelease) { Retain(); }
|
||||
CACFMutableString& operator=(const CACFMutableString& inString) { Release(); mCFMutableString = inString.mCFMutableString; mWillRelease = inString.mWillRelease; Retain(); return *this; }
|
||||
CACFMutableString& operator=(CFMutableStringRef inCFMutableString) { Release(); mCFMutableString = inCFMutableString; mWillRelease = true; return *this; }
|
||||
|
||||
private:
|
||||
void Retain() { if(mWillRelease && (mCFMutableString != NULL)) { CFRetain(mCFMutableString); } }
|
||||
void Release() { if(mWillRelease && (mCFMutableString != NULL)) { CFRelease(mCFMutableString); } }
|
||||
|
||||
CFMutableStringRef mCFMutableString;
|
||||
bool mWillRelease;
|
||||
|
||||
// Operations
|
||||
public:
|
||||
void AllowRelease() { mWillRelease = true; }
|
||||
void DontAllowRelease() { mWillRelease = false; }
|
||||
bool IsValid() { return mCFMutableString != NULL; }
|
||||
bool IsEqualTo(CFStringRef inString) const { bool theAnswer = false; if(mCFMutableString != NULL) { theAnswer = CFStringCompare(inString, mCFMutableString, 0) == kCFCompareEqualTo; } return theAnswer; }
|
||||
bool StartsWith(CFStringRef inString) const { bool theAnswer = false; if(mCFMutableString != NULL) { theAnswer = CFStringHasPrefix(mCFMutableString, inString); } return theAnswer; }
|
||||
bool EndsWith(CFStringRef inString) const { bool theAnswer = false; if(mCFMutableString != NULL) { theAnswer = CFStringHasSuffix(mCFMutableString, inString); } return theAnswer; }
|
||||
void Append(CFStringRef inString) { if(mCFMutableString != NULL) { CFStringAppend(mCFMutableString, inString); } }
|
||||
|
||||
// Value Access
|
||||
public:
|
||||
CFMutableStringRef GetCFMutableString() const { return mCFMutableString; }
|
||||
CFMutableStringRef CopyCFMutableString() const { if(mCFMutableString != NULL) { CFRetain(mCFMutableString); } return mCFMutableString; }
|
||||
UInt32 GetLength() const { UInt32 theAnswer = 0; if(mCFMutableString != NULL) { theAnswer = ToUInt32(CFStringGetLength(mCFMutableString)); } return theAnswer; }
|
||||
UInt32 GetByteLength(CFStringEncoding inEncoding = kCFStringEncodingUTF8) const { UInt32 theAnswer = 0; if(mCFMutableString != NULL) { theAnswer = CACFString::GetStringByteLength(mCFMutableString, inEncoding); } return theAnswer; }
|
||||
void GetCString(char* outString, UInt32& ioStringSize, CFStringEncoding inEncoding = kCFStringEncodingUTF8) const { CACFString::GetCString(mCFMutableString, outString, ioStringSize, inEncoding); }
|
||||
void GetUnicodeString(UInt16* outString, UInt32& ioStringSize) const { CACFString::GetUnicodeString(mCFMutableString, outString, ioStringSize); }
|
||||
SInt32 GetAsInteger() { return CACFString::GetAsInteger(mCFMutableString); }
|
||||
Float64 GetAsFloat64() { return CACFString::GetAsFloat64(mCFMutableString); }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
116
driver/PublicUtility/CADebugMacros.cpp
Normal file
116
driver/PublicUtility/CADebugMacros.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
File: CADebugMacros.cpp
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#include "CADebugMacros.h"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#if TARGET_API_MAC_OSX
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
#include <stdio.h>
|
||||
|
||||
void DebugPrint(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
void LogError(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
// VC edit: vprintf leaves args in an undefined state, which can cause a crash in
|
||||
// vsyslog. Also added __ASSERT_STOP. Original code commented out below.
|
||||
//#if DEBUG
|
||||
// vprintf(fmt, args);
|
||||
//#endif
|
||||
//#if TARGET_API_MAC_OSX
|
||||
// vsyslog(LOG_ERR, fmt, args);
|
||||
//#endif
|
||||
#if (DEBUG || !TARGET_API_MAC_OSX) && !CoreAudio_UseSysLog
|
||||
printf("[ERROR] ");
|
||||
vprintf(fmt, args);
|
||||
printf("\n");
|
||||
#else
|
||||
vsyslog(LOG_ERR, fmt, args);
|
||||
#endif
|
||||
#if DEBUG
|
||||
__ASSERT_STOP;
|
||||
#endif
|
||||
// VC edit end
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void LogWarning(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
// VC edit: vprintf leaves args in an undefined state, which can cause a crash in
|
||||
// vsyslog. Also added __ASSERT_STOP. Original code commented out below.
|
||||
//#if DEBUG
|
||||
// vprintf(fmt, args);
|
||||
//#endif
|
||||
//#if TARGET_API_MAC_OSX
|
||||
// vsyslog(LOG_WARNING, fmt, args);
|
||||
//#endif
|
||||
#if (DEBUG || !TARGET_API_MAC_OSX) && !CoreAudio_UseSysLog
|
||||
printf("[WARNING] ");
|
||||
vprintf(fmt, args);
|
||||
printf("\n");
|
||||
#else
|
||||
vsyslog(LOG_WARNING, fmt, args);
|
||||
#endif
|
||||
#if DEBUG
|
||||
//__ASSERT_STOP; // TODO: Add a toggle for this to the project file (under "Preprocessor Macros"). Default to off.
|
||||
#endif
|
||||
// VC edit end
|
||||
va_end(args);
|
||||
}
|
||||
583
driver/PublicUtility/CADebugMacros.h
Normal file
583
driver/PublicUtility/CADebugMacros.h
Normal file
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
File: CADebugMacros.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CADebugMacros_h__)
|
||||
#define __CADebugMacros_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include "CoreAudioTypes.h"
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CADebugMacros
|
||||
//=============================================================================
|
||||
|
||||
//#define CoreAudio_StopOnFailure 1
|
||||
//#define CoreAudio_TimeStampMessages 1
|
||||
//#define CoreAudio_ThreadStampMessages 1
|
||||
//#define CoreAudio_FlushDebugMessages 1
|
||||
|
||||
#if TARGET_RT_BIG_ENDIAN
|
||||
#define CA4CCToCString(the4CC) { ((char*)&the4CC)[0], ((char*)&the4CC)[1], ((char*)&the4CC)[2], ((char*)&the4CC)[3], 0 }
|
||||
#define CACopy4CCToCString(theCString, the4CC) { theCString[0] = ((char*)&the4CC)[0]; theCString[1] = ((char*)&the4CC)[1]; theCString[2] = ((char*)&the4CC)[2]; theCString[3] = ((char*)&the4CC)[3]; theCString[4] = 0; }
|
||||
#else
|
||||
#define CA4CCToCString(the4CC) { ((char*)&the4CC)[3], ((char*)&the4CC)[2], ((char*)&the4CC)[1], ((char*)&the4CC)[0], 0 }
|
||||
#define CACopy4CCToCString(theCString, the4CC) { theCString[0] = ((char*)&the4CC)[3]; theCString[1] = ((char*)&the4CC)[2]; theCString[2] = ((char*)&the4CC)[1]; theCString[3] = ((char*)&the4CC)[0]; theCString[4] = 0; }
|
||||
#endif
|
||||
|
||||
// This is a macro that does a sizeof and casts the result to a UInt32. This is useful for all the
|
||||
// places where -wshorten64-32 catches assigning a sizeof expression to a UInt32.
|
||||
// For want of a better place to park this, we'll park it here.
|
||||
#define SizeOf32(X) ((UInt32)sizeof(X))
|
||||
|
||||
// This is a macro that does a offsetof and casts the result to a UInt32. This is useful for all the
|
||||
// places where -wshorten64-32 catches assigning an offsetof expression to a UInt32.
|
||||
// For want of a better place to park this, we'll park it here.
|
||||
#define OffsetOf32(X, Y) ((UInt32)offsetof(X, Y))
|
||||
|
||||
// This macro casts the expression to a UInt32. It is called out specially to allow us to track casts
|
||||
// that have been added purely to avert -wshorten64-32 warnings on 64 bit platforms.
|
||||
// For want of a better place to park this, we'll park it here.
|
||||
#define ToUInt32(X) ((UInt32)(X))
|
||||
#define ToSInt32(X) ((SInt32)(X))
|
||||
|
||||
#pragma mark Basic Definitions
|
||||
|
||||
#if DEBUG || CoreAudio_Debug
|
||||
// can be used to break into debugger immediately, also see CADebugger
|
||||
#define BusError() { long* p=NULL; *p=0; }
|
||||
|
||||
// basic debugging print routines
|
||||
#if TARGET_OS_MAC && !TARGET_API_MAC_CARBON
|
||||
extern void DebugStr(const unsigned char* debuggerMsg);
|
||||
#define DebugMessage(msg) DebugStr("\p"msg)
|
||||
#define DebugMessageN1(msg, N1)
|
||||
#define DebugMessageN2(msg, N1, N2)
|
||||
#define DebugMessageN3(msg, N1, N2, N3)
|
||||
#else
|
||||
#include "CADebugPrintf.h"
|
||||
|
||||
#if (CoreAudio_FlushDebugMessages && !CoreAudio_UseSysLog) || defined(CoreAudio_UseSideFile)
|
||||
#define FlushRtn ,fflush(DebugPrintfFile)
|
||||
#else
|
||||
#define FlushRtn
|
||||
#endif
|
||||
|
||||
#if CoreAudio_ThreadStampMessages
|
||||
#include <pthread.h>
|
||||
#include "CAHostTimeBase.h"
|
||||
#if TARGET_RT_64_BIT
|
||||
#define DebugPrintfThreadIDFormat "%16p"
|
||||
#else
|
||||
#define DebugPrintfThreadIDFormat "%8p"
|
||||
#endif
|
||||
#define DebugMsg(inFormat, ...) DebugPrintf("%17qd: " DebugPrintfThreadIDFormat " " inFormat, CAHostTimeBase::GetCurrentTimeInNanos(), pthread_self(), ## __VA_ARGS__) FlushRtn
|
||||
#elif CoreAudio_TimeStampMessages
|
||||
#include "CAHostTimeBase.h"
|
||||
#define DebugMsg(inFormat, ...) DebugPrintf("%17qd: " inFormat, CAHostTimeBase::GetCurrentTimeInNanos(), ## __VA_ARGS__) FlushRtn
|
||||
#else
|
||||
#define DebugMsg(inFormat, ...) DebugPrintf(inFormat, ## __VA_ARGS__) FlushRtn
|
||||
#endif
|
||||
#endif
|
||||
void DebugPrint(const char *fmt, ...); // can be used like printf
|
||||
#ifndef DEBUGPRINT
|
||||
#define DEBUGPRINT(msg) DebugPrint msg // have to double-parenthesize arglist (see Debugging.h)
|
||||
#endif
|
||||
#if VERBOSE
|
||||
#define vprint(msg) DEBUGPRINT(msg)
|
||||
#else
|
||||
#define vprint(msg)
|
||||
#endif
|
||||
|
||||
// Original macro keeps its function of turning on and off use of CADebuggerStop() for both asserts and throws.
|
||||
// For backwards compat, it overrides any setting of the two sub-macros.
|
||||
#if CoreAudio_StopOnFailure
|
||||
#include "CADebugger.h"
|
||||
#undef CoreAudio_StopOnAssert
|
||||
#define CoreAudio_StopOnAssert 1
|
||||
#undef CoreAudio_StopOnThrow
|
||||
#define CoreAudio_StopOnThrow 1
|
||||
#define STOP CADebuggerStop()
|
||||
#else
|
||||
#define STOP
|
||||
#endif
|
||||
|
||||
#if CoreAudio_StopOnAssert
|
||||
#if !CoreAudio_StopOnFailure
|
||||
#include "CADebugger.h"
|
||||
#define STOP
|
||||
#endif
|
||||
#define __ASSERT_STOP CADebuggerStop()
|
||||
#else
|
||||
#define __ASSERT_STOP
|
||||
#endif
|
||||
|
||||
#if CoreAudio_StopOnThrow
|
||||
#if !CoreAudio_StopOnFailure
|
||||
#include "CADebugger.h"
|
||||
#define STOP
|
||||
#endif
|
||||
#define __THROW_STOP CADebuggerStop()
|
||||
#else
|
||||
#define __THROW_STOP
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define DebugMsg(inFormat, ...)
|
||||
#ifndef DEBUGPRINT
|
||||
#define DEBUGPRINT(msg)
|
||||
#endif
|
||||
#define vprint(msg)
|
||||
#define STOP
|
||||
#define __ASSERT_STOP
|
||||
#define __THROW_STOP
|
||||
#endif
|
||||
|
||||
// Old-style numbered DebugMessage calls are implemented in terms of DebugMsg() now
|
||||
#define DebugMessage(msg) DebugMsg(msg)
|
||||
#define DebugMessageN1(msg, N1) DebugMsg(msg, N1)
|
||||
#define DebugMessageN2(msg, N1, N2) DebugMsg(msg, N1, N2)
|
||||
#define DebugMessageN3(msg, N1, N2, N3) DebugMsg(msg, N1, N2, N3)
|
||||
#define DebugMessageN4(msg, N1, N2, N3, N4) DebugMsg(msg, N1, N2, N3, N4)
|
||||
#define DebugMessageN5(msg, N1, N2, N3, N4, N5) DebugMsg(msg, N1, N2, N3, N4, N5)
|
||||
#define DebugMessageN6(msg, N1, N2, N3, N4, N5, N6) DebugMsg(msg, N1, N2, N3, N4, N5, N6)
|
||||
#define DebugMessageN7(msg, N1, N2, N3, N4, N5, N6, N7) DebugMsg(msg, N1, N2, N3, N4, N5, N6, N7)
|
||||
#define DebugMessageN8(msg, N1, N2, N3, N4, N5, N6, N7, N8) DebugMsg(msg, N1, N2, N3, N4, N5, N6, N7, N8)
|
||||
#define DebugMessageN9(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9) DebugMsg(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9)
|
||||
|
||||
// VC edit: Added __printflike.
|
||||
void LogError(const char *fmt, ...) __printflike(1, 2); // writes to syslog (and stderr if debugging)
|
||||
void LogWarning(const char *fmt, ...) __printflike(1, 2); // writes to syslog (and stderr if debugging)
|
||||
|
||||
#define NO_ACTION (void)0
|
||||
|
||||
#if DEBUG || CoreAudio_Debug
|
||||
|
||||
#pragma mark Debug Macros
|
||||
|
||||
#define Assert(inCondition, inMessage) \
|
||||
if(!(inCondition)) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
__ASSERT_STOP; \
|
||||
}
|
||||
|
||||
#define AssertFileLine(inCondition, inMessage) \
|
||||
if(!(inCondition)) \
|
||||
{ \
|
||||
DebugMessageN3("%s, line %d: %s", __FILE__, __LINE__, inMessage); \
|
||||
__ASSERT_STOP; \
|
||||
}
|
||||
|
||||
#define AssertNoError(inError, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
char __4CC[5] = CA4CCToCString(__Err); \
|
||||
DebugMessageN2(inMessage ", Error: %d (%s)", (int)__Err, __4CC); \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define AssertNoKernelError(inError, inMessage) \
|
||||
{ \
|
||||
unsigned int __Err = (unsigned int)(inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
DebugMessageN1(inMessage ", Error: 0x%X", __Err); \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define AssertNotNULL(inPtr, inMessage) \
|
||||
{ \
|
||||
if((inPtr) == NULL) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIf(inCondition, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
STOP; \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailWithAction(inCondition, inAction, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfNULL(inPointer, inAction, inHandler, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfKernelError(inKernelError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
unsigned int __Err = (inKernelError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
DebugMessageN1(inMessage ", Error: 0x%X", __Err); \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIfError(inError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
char __4CC[5] = CA4CCToCString(__Err); \
|
||||
DebugMessageN2(inMessage ", Error: %ld (%s)", (long int)__Err, __4CC); \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIfNoMessage(inCondition, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailWithActionNoMessage(inCondition, inAction, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfNULLNoMessage(inPointer, inAction, inHandler, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfKernelErrorNoMessage(inKernelError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
unsigned int __Err = (inKernelError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIfErrorNoMessage(inError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
#define Throw(inException) __THROW_STOP; throw (inException)
|
||||
|
||||
#define ThrowIf(inCondition, inException, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
#define ThrowIfNULL(inPointer, inException, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
#define ThrowIfKernelError(inKernelError, inException, inMessage) \
|
||||
{ \
|
||||
int __Err = (inKernelError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
DebugMessageN1(inMessage ", Error: 0x%X", __Err); \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ThrowIfError(inError, inException, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
char __4CC[5] = CA4CCToCString(__Err); \
|
||||
DebugMessageN2(inMessage ", Error: %d (%s)", (int)__Err, __4CC); \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
|
||||
#if TARGET_OS_WIN32
|
||||
#define ThrowIfWinError(inError, inException, inMessage) \
|
||||
{ \
|
||||
HRESULT __Err = (inError); \
|
||||
if(FAILED(__Err)) \
|
||||
{ \
|
||||
DebugMessageN2(inMessage ", Code: %d, Facility: 0x%X", HRESULT_CODE(__Err), HRESULT_FACILITY(__Err)); \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SubclassResponsibility(inMethodName, inException) \
|
||||
{ \
|
||||
DebugMessage(inMethodName": Subclasses must implement this method"); \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#else
|
||||
|
||||
#pragma mark Release Macros
|
||||
|
||||
#define Assert(inCondition, inMessage) \
|
||||
if(!(inCondition)) \
|
||||
{ \
|
||||
__ASSERT_STOP; \
|
||||
}
|
||||
|
||||
#define AssertFileLine(inCondition, inMessage) Assert(inCondition, inMessage)
|
||||
|
||||
#define AssertNoError(inError, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define AssertNoKernelError(inError, inMessage) \
|
||||
{ \
|
||||
unsigned int __Err = (unsigned int)(inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define AssertNotNULL(inPtr, inMessage) \
|
||||
{ \
|
||||
if((inPtr) == NULL) \
|
||||
{ \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIf(inCondition, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailWithAction(inCondition, inAction, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfNULL(inPointer, inAction, inHandler, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfKernelError(inKernelError, inAction, inHandler, inMessage) \
|
||||
if((inKernelError) != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfError(inError, inAction, inHandler, inMessage) \
|
||||
if((inError) != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfNoMessage(inCondition, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailWithActionNoMessage(inCondition, inAction, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfNULLNoMessage(inPointer, inAction, inHandler, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfKernelErrorNoMessage(inKernelError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
unsigned int __Err = (inKernelError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIfErrorNoMessage(inError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
#define Throw(inException) __THROW_STOP; throw (inException)
|
||||
|
||||
#define ThrowIf(inCondition, inException, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
#define ThrowIfNULL(inPointer, inException, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
// VC edit: Changed "unsigned int" to "int" to silence -Wsign-conversion.
|
||||
#define ThrowIfKernelError(inKernelError, inException, inMessage) \
|
||||
{ \
|
||||
int __Err = (inKernelError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ThrowIfError(inError, inException, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
|
||||
#if TARGET_OS_WIN32
|
||||
#define ThrowIfWinError(inError, inException, inMessage) \
|
||||
{ \
|
||||
HRESULT __Err = (inError); \
|
||||
if(FAILED(__Err)) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SubclassResponsibility(inMethodName, inException) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#endif // DEBUG || CoreAudio_Debug
|
||||
|
||||
#endif
|
||||
89
driver/PublicUtility/CADebugPrintf.cpp
Normal file
89
driver/PublicUtility/CADebugPrintf.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
File: CADebugPrintf.cpp
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CADebugPrintf.h"
|
||||
|
||||
#if DEBUG || CoreAudio_Debug
|
||||
|
||||
#if TARGET_OS_WIN32
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <Windows.h>
|
||||
extern "C"
|
||||
int CAWin32DebugPrintf(char* inFormat, ...)
|
||||
{
|
||||
char theMessage[1024];
|
||||
va_list theArguments;
|
||||
va_start(theArguments, inFormat);
|
||||
_vsnprintf(theMessage, 1024, inFormat, theArguments);
|
||||
va_end(theArguments);
|
||||
OutputDebugString(theMessage);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CoreAudio_UseSideFile)
|
||||
#include <unistd.h>
|
||||
FILE* sDebugPrintfSideFile = NULL;
|
||||
extern "C"
|
||||
void OpenDebugPrintfSideFile()
|
||||
{
|
||||
if(sDebugPrintfSideFile == NULL)
|
||||
{
|
||||
char theFileName[1024];
|
||||
snprintf(theFileName, sizeof(theFileName), CoreAudio_UseSideFile, getpid());
|
||||
sDebugPrintfSideFile = fopen(theFileName, "a+");
|
||||
DebugPrintfRtn(DebugPrintfFileComma "\n------------------------------\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
115
driver/PublicUtility/CADebugPrintf.h
Normal file
115
driver/PublicUtility/CADebugPrintf.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
File: CADebugPrintf.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CADebugPrintf_h__)
|
||||
#define __CADebugPrintf_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include "CoreAudioTypes.h"
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Macros to redirect debugging output to various logging services
|
||||
//=============================================================================
|
||||
|
||||
//#define CoreAudio_UseSysLog 1
|
||||
//#define CoreAudio_UseSideFile "/CoreAudio-%d.txt"
|
||||
|
||||
#if DEBUG || CoreAudio_Debug
|
||||
|
||||
#if TARGET_OS_WIN32
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
#endif
|
||||
extern int CAWin32DebugPrintf(char* inFormat, ...);
|
||||
#define DebugPrintfRtn CAWin32DebugPrintf
|
||||
#define DebugPrintfFile
|
||||
#define DebugPrintfLineEnding "\n"
|
||||
#define DebugPrintfFileComma
|
||||
#else
|
||||
#if CoreAudio_UseSysLog
|
||||
#include <sys/syslog.h>
|
||||
#define DebugPrintfRtn syslog
|
||||
#define DebugPrintfFile LOG_NOTICE
|
||||
#define DebugPrintfLineEnding ""
|
||||
#define DebugPrintfFileComma DebugPrintfFile,
|
||||
#elif defined(CoreAudio_UseSideFile)
|
||||
#include <stdio.h>
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
#endif
|
||||
void OpenDebugPrintfSideFile();
|
||||
extern FILE* sDebugPrintfSideFile;
|
||||
#define DebugPrintfRtn fprintf
|
||||
#define DebugPrintfFile ((sDebugPrintfSideFile != NULL) ? sDebugPrintfSideFile : stderr)
|
||||
#define DebugPrintfLineEnding "\n"
|
||||
#define DebugPrintfFileComma DebugPrintfFile,
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#define DebugPrintfRtn fprintf
|
||||
#define DebugPrintfFile stderr
|
||||
#define DebugPrintfLineEnding "\n"
|
||||
#define DebugPrintfFileComma DebugPrintfFile,
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define DebugPrintf(inFormat, ...) DebugPrintfRtn(DebugPrintfFileComma inFormat DebugPrintfLineEnding, ## __VA_ARGS__)
|
||||
#else
|
||||
#define DebugPrintfRtn
|
||||
#define DebugPrintfFile
|
||||
#define DebugPrintfLineEnding
|
||||
#define DebugPrintfFileComma
|
||||
#define DebugPrintf(inFormat, ...)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
103
driver/PublicUtility/CADebugger.cpp
Normal file
103
driver/PublicUtility/CADebugger.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
File: CADebugger.cpp
|
||||
Abstract: CADebugger.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.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#include "CADebugger.h"
|
||||
|
||||
//=============================================================================
|
||||
// CADebugger
|
||||
//=============================================================================
|
||||
|
||||
#if TARGET_API_MAC_OSX
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool CAIsDebuggerAttached(void)
|
||||
{
|
||||
int mib[4];
|
||||
struct kinfo_proc info;
|
||||
size_t size;
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PID;
|
||||
mib[3] = getpid();
|
||||
size = sizeof(info);
|
||||
info.kp_proc.p_flag = 0;
|
||||
|
||||
sysctl(mib, 4, &info, &size, NULL, 0);
|
||||
|
||||
return (info.kp_proc.p_flag & P_TRACED) == P_TRACED;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void CADebuggerStop(void)
|
||||
{
|
||||
#if CoreAudio_Debug
|
||||
#if TARGET_API_MAC_OSX
|
||||
if(CAIsDebuggerAttached())
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
asm("int3");
|
||||
#else
|
||||
__builtin_trap();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
abort();
|
||||
}
|
||||
#else
|
||||
__debugbreak();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
69
driver/PublicUtility/CADebugger.h
Normal file
69
driver/PublicUtility/CADebugger.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
File: CADebugger.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.
|
||||
|
||||
*/
|
||||
#if !defined(__CADebugger_h__)
|
||||
#define __CADebugger_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CADebugger
|
||||
//=============================================================================
|
||||
|
||||
#if TARGET_API_MAC_OSX
|
||||
extern bool CAIsDebuggerAttached(void);
|
||||
#endif
|
||||
extern void CADebuggerStop(void);
|
||||
|
||||
#endif
|
||||
438
driver/PublicUtility/CADispatchQueue.cpp
Normal file
438
driver/PublicUtility/CADispatchQueue.cpp
Normal file
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
File: CADispatchQueue.cpp
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
/*==================================================================================================
|
||||
CADispatchQueue.cpp
|
||||
==================================================================================================*/
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CADispatchQueue.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CACFString.h"
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
#include "CAHostTimeBase.h"
|
||||
|
||||
// System Includes
|
||||
#include <mach/mach.h>
|
||||
|
||||
// Standard Library Includes
|
||||
#include <algorithm>
|
||||
|
||||
//==================================================================================================
|
||||
// CADispatchQueue
|
||||
//==================================================================================================
|
||||
|
||||
CADispatchQueue::CADispatchQueue(const char* inName)
|
||||
:
|
||||
mDispatchQueue(NULL),
|
||||
mPortDeathList(),
|
||||
mMachPortReceiverList()
|
||||
{
|
||||
mDispatchQueue = dispatch_queue_create(inName, NULL);
|
||||
ThrowIfNULL(mDispatchQueue, CAException('what'), "CADispatchQueue::CADispatchQueue: failed to create the dispatch queue");
|
||||
}
|
||||
|
||||
CADispatchQueue::CADispatchQueue(CFStringRef inName)
|
||||
:
|
||||
mDispatchQueue(NULL),
|
||||
mPortDeathList(),
|
||||
mMachPortReceiverList()
|
||||
{
|
||||
CACFString theCFName(inName, false);
|
||||
char theName[256];
|
||||
UInt32 theSize = 256;
|
||||
theCFName.GetCString(theName, theSize);
|
||||
mDispatchQueue = dispatch_queue_create(theName, NULL);
|
||||
ThrowIfNULL(mDispatchQueue, CAException('what'), "CADispatchQueue::CADispatchQueue: failed to create the dispatch queue");
|
||||
}
|
||||
|
||||
CADispatchQueue::CADispatchQueue(CFStringRef inPattern, CFStringRef inName)
|
||||
:
|
||||
mDispatchQueue(NULL),
|
||||
mPortDeathList(),
|
||||
mMachPortReceiverList()
|
||||
{
|
||||
CACFString theCFName(CFStringCreateWithFormat(NULL, NULL, inPattern, inName), true);
|
||||
char theName[256];
|
||||
UInt32 theSize = 256;
|
||||
theCFName.GetCString(theName, theSize);
|
||||
mDispatchQueue = dispatch_queue_create(theName, NULL);
|
||||
ThrowIfNULL(mDispatchQueue, CAException('what'), "CADispatchQueue::CADispatchQueue: failed to create the dispatch queue");
|
||||
}
|
||||
|
||||
CADispatchQueue::~CADispatchQueue()
|
||||
{
|
||||
// Clean up the port death watchers if any are still around. Note that we do this explicitly to
|
||||
// be sure the source is cleaned up before the queue is released
|
||||
mPortDeathList.clear();
|
||||
Assert(mMachPortReceiverList.size() == 0, "CADispatchQueue::~CADispatchQueue: Implicitly removing the mach port receviers. It is best to explicitly call RemoveMachPortRecevier().");
|
||||
mMachPortReceiverList.clear();
|
||||
|
||||
// release the dispatch queue
|
||||
dispatch_release(mDispatchQueue);
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch(bool inDoSync, dispatch_block_t inTask) const
|
||||
{
|
||||
if(inDoSync)
|
||||
{
|
||||
// Executing a task synchronously while already on the dispatch queue will result in a deadlock
|
||||
dispatch_sync(mDispatchQueue, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_async(mDispatchQueue, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch(UInt64 inNanoseconds, dispatch_block_t inTask) const
|
||||
{
|
||||
if(inNanoseconds == 0)
|
||||
{
|
||||
dispatch_async(mDispatchQueue, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_after(dispatch_time(0, static_cast<int64_t>(CAHostTimeBase::ConvertFromNanos(inNanoseconds))), mDispatchQueue, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch(bool inDoSync, void* inTaskContext, dispatch_function_t inTask) const
|
||||
{
|
||||
if(inDoSync)
|
||||
{
|
||||
// Executing a task synchronously while already on the dispatch queue will result in a deadlock
|
||||
dispatch_sync_f(mDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_async_f(mDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch(UInt64 inNanoseconds, void* inTaskContext, dispatch_function_t inTask) const
|
||||
{
|
||||
if(inNanoseconds == 0)
|
||||
{
|
||||
dispatch_async_f(mDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_after_f(dispatch_time(0, static_cast<int64_t>(CAHostTimeBase::ConvertFromNanos(inNanoseconds))), mDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch_Global(dispatch_queue_priority_t inQueuePriority, bool inDoSync, dispatch_block_t inTask)
|
||||
{
|
||||
dispatch_queue_t theDispatchQueue = dispatch_get_global_queue(inQueuePriority, 0);
|
||||
if(inDoSync)
|
||||
{
|
||||
// Executing a task synchronously while already on the dispatch queue will result in a deadlock
|
||||
dispatch_sync(theDispatchQueue, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_async(theDispatchQueue, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch_Global(dispatch_queue_priority_t inQueuePriority, UInt64 inNanoseconds, dispatch_block_t inTask)
|
||||
{
|
||||
dispatch_queue_t theDispatchQueue = dispatch_get_global_queue(inQueuePriority, 0);
|
||||
if(inNanoseconds == 0)
|
||||
{
|
||||
dispatch_async(theDispatchQueue, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_after(dispatch_time(0, static_cast<int64_t>(CAHostTimeBase::ConvertFromNanos(inNanoseconds))), theDispatchQueue, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch_Global(dispatch_queue_priority_t inQueuePriority, bool inDoSync, void* inTaskContext, dispatch_function_t inTask)
|
||||
{
|
||||
dispatch_queue_t theDispatchQueue = dispatch_get_global_queue(inQueuePriority, 0);
|
||||
if(inDoSync)
|
||||
{
|
||||
// Executing a task synchronously while already on the dispatch queue will result in a deadlock
|
||||
dispatch_sync_f(theDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_async_f(theDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch_Global(dispatch_queue_priority_t inQueuePriority, UInt64 inNanoseconds, void* inTaskContext, dispatch_function_t inTask)
|
||||
{
|
||||
dispatch_queue_t theDispatchQueue = dispatch_get_global_queue(inQueuePriority, 0);
|
||||
if(inNanoseconds == 0)
|
||||
{
|
||||
dispatch_async_f(theDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_after_f(dispatch_time(0, static_cast<int64_t>(CAHostTimeBase::ConvertFromNanos(inNanoseconds))), theDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch_Main(bool inDoSync, dispatch_block_t inTask)
|
||||
{
|
||||
dispatch_queue_t theDispatchQueue = dispatch_get_main_queue();
|
||||
if(inDoSync)
|
||||
{
|
||||
// Executing a task synchronously while already on the dispatch queue will result in a deadlock
|
||||
dispatch_sync(theDispatchQueue, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_async(theDispatchQueue, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch_Main(UInt64 inNanoseconds, dispatch_block_t inTask)
|
||||
{
|
||||
dispatch_queue_t theDispatchQueue = dispatch_get_main_queue();
|
||||
if(inNanoseconds == 0)
|
||||
{
|
||||
dispatch_async(theDispatchQueue, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_after(dispatch_time(0, static_cast<int64_t>(CAHostTimeBase::ConvertFromNanos(inNanoseconds))), theDispatchQueue, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch_Main(bool inDoSync, void* inTaskContext, dispatch_function_t inTask)
|
||||
{
|
||||
dispatch_queue_t theDispatchQueue = dispatch_get_main_queue();
|
||||
if(inDoSync)
|
||||
{
|
||||
// Executing a task synchronously while already on the dispatch queue will result in a deadlock
|
||||
dispatch_sync_f(theDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_async_f(theDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::Dispatch_Main(UInt64 inNanoseconds, void* inTaskContext, dispatch_function_t inTask)
|
||||
{
|
||||
dispatch_queue_t theDispatchQueue = dispatch_get_main_queue();
|
||||
if(inNanoseconds == 0)
|
||||
{
|
||||
dispatch_async_f(theDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_after_f(dispatch_time(0, static_cast<int64_t>(CAHostTimeBase::ConvertFromNanos(inNanoseconds))), theDispatchQueue, inTaskContext, inTask);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::InstallMachPortDeathNotification(mach_port_t inMachPort, dispatch_block_t inNotificationTask)
|
||||
{
|
||||
ThrowIf(inMachPort == MACH_PORT_NULL, CAException('nope'), "CADispatchQueue::InstallMachPortDeathNotification: a mach port is required");
|
||||
|
||||
// look in the list to see if we've already created an event source for it
|
||||
bool wasFound = false;
|
||||
EventSourceList::iterator theIterator = mPortDeathList.begin();
|
||||
while(!wasFound && (theIterator != mPortDeathList.end()))
|
||||
{
|
||||
wasFound = theIterator->mMachPort == inMachPort;
|
||||
if(!wasFound)
|
||||
{
|
||||
++theIterator;
|
||||
}
|
||||
}
|
||||
|
||||
// create and install the event source for the port
|
||||
if(!wasFound)
|
||||
{
|
||||
// create an event source for the mach port
|
||||
dispatch_source_t theDispatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, inMachPort, DISPATCH_MACH_SEND_DEAD, mDispatchQueue);
|
||||
ThrowIfNULL(theDispatchSource, CAException('what'), "CADispatchQueue::InstallMachPortDeathNotification: failed to create the mach port event source");
|
||||
|
||||
// install the event handler
|
||||
dispatch_source_set_event_handler(theDispatchSource, inNotificationTask);
|
||||
|
||||
// put the info in the list
|
||||
mPortDeathList.push_back(EventSource(theDispatchSource, inMachPort));
|
||||
|
||||
// resume the event source so that it can start handling messages and also so that the source can be released
|
||||
dispatch_resume(theDispatchSource);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::RemoveMachPortDeathNotification(mach_port_t inMachPort)
|
||||
{
|
||||
bool wasFound = false;
|
||||
EventSourceList::iterator theIterator = mPortDeathList.begin();
|
||||
while(!wasFound && (theIterator != mPortDeathList.end()))
|
||||
{
|
||||
wasFound = theIterator->mMachPort == inMachPort;
|
||||
if(!wasFound)
|
||||
{
|
||||
++theIterator;
|
||||
}
|
||||
}
|
||||
if(wasFound)
|
||||
{
|
||||
if(theIterator->mDispatchSource != NULL)
|
||||
{
|
||||
dispatch_source_cancel(theIterator->mDispatchSource);
|
||||
}
|
||||
mPortDeathList.erase(theIterator);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::InstallMachPortReceiver(mach_port_t inMachPort, dispatch_block_t inMessageTask)
|
||||
{
|
||||
ThrowIf(inMachPort == MACH_PORT_NULL, CAException('nope'), "CADispatchQueue::InstallMachPortReceiver: a mach port is required");
|
||||
|
||||
// look in the list to see if we've already created an event source for it
|
||||
bool wasFound = false;
|
||||
EventSourceList::iterator theIterator = mMachPortReceiverList.begin();
|
||||
while(!wasFound && (theIterator != mMachPortReceiverList.end()))
|
||||
{
|
||||
wasFound = theIterator->mMachPort == inMachPort;
|
||||
if(!wasFound)
|
||||
{
|
||||
++theIterator;
|
||||
}
|
||||
}
|
||||
|
||||
// create and install the event source for the port
|
||||
if(!wasFound)
|
||||
{
|
||||
// create an event source for the mach port
|
||||
dispatch_source_t theDispatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, inMachPort, 0, mDispatchQueue);
|
||||
ThrowIfNULL(theDispatchSource, CAException('what'), "CADispatchQueue::InstallMachPortReceiver: failed to create the mach port event source");
|
||||
|
||||
// install an event handler that maps the mach messages to the MIG server function
|
||||
dispatch_source_set_event_handler(theDispatchSource, inMessageTask);
|
||||
|
||||
// put the info in the list
|
||||
mMachPortReceiverList.push_back(EventSource(theDispatchSource, inMachPort));
|
||||
|
||||
// resume the event source so that it can start handling messages and also so that the source can be released
|
||||
dispatch_resume(theDispatchSource);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::RemoveMachPortReceiver(mach_port_t inMachPort, dispatch_block_t inCompletionTask)
|
||||
{
|
||||
bool wasFound = false;
|
||||
EventSourceList::iterator theIterator = mMachPortReceiverList.begin();
|
||||
while(!wasFound && (theIterator != mMachPortReceiverList.end()))
|
||||
{
|
||||
wasFound = theIterator->mMachPort == inMachPort;
|
||||
if(!wasFound)
|
||||
{
|
||||
++theIterator;
|
||||
}
|
||||
}
|
||||
if(wasFound)
|
||||
{
|
||||
if(theIterator->mDispatchSource != NULL)
|
||||
{
|
||||
// Set the cancel handler to the completion block. Note that the mach port cannot be freed
|
||||
// before the completion block runs due to a race condition. See the note in the comments
|
||||
// dispatch_source_set_cancel_handler in <dispatch/source.h>.
|
||||
if(inCompletionTask != 0)
|
||||
{
|
||||
dispatch_source_set_cancel_handler(theIterator->mDispatchSource, inCompletionTask);
|
||||
}
|
||||
|
||||
dispatch_source_cancel(theIterator->mDispatchSource);
|
||||
}
|
||||
mMachPortReceiverList.erase(theIterator);
|
||||
}
|
||||
}
|
||||
|
||||
void CADispatchQueue::RemoveMachPortReceiver(mach_port_t inMachPort, bool inDestroySendRight, bool inDestroyReceiveRight)
|
||||
{
|
||||
RemoveMachPortReceiver(inMachPort, ^{
|
||||
if(inDestroySendRight)
|
||||
{
|
||||
kern_return_t theError = mach_port_deallocate(mach_task_self(), inMachPort);
|
||||
AssertNoKernelError(theError, "CADispatchQueue::RemoveMachPortReceiver: deallocating the send right failed");
|
||||
}
|
||||
if(inDestroyReceiveRight)
|
||||
{
|
||||
kern_return_t theError = mach_port_mod_refs(mach_task_self(), inMachPort, MACH_PORT_RIGHT_RECEIVE, -1);
|
||||
AssertNoKernelError(theError, "CADispatchQueue::RemoveMachPortReceiver: deallocating the receive right failed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
CADispatchQueue& CADispatchQueue::GetGlobalSerialQueue()
|
||||
{
|
||||
dispatch_once_f(&sGlobalSerialQueueInitialized, NULL, InitializeGlobalSerialQueue);
|
||||
ThrowIfNULL(sGlobalSerialQueue, CAException('nope'), "CADispatchQueue::GetGlobalSerialQueue: there is no global serial queue");
|
||||
return *sGlobalSerialQueue;
|
||||
}
|
||||
|
||||
void CADispatchQueue::InitializeGlobalSerialQueue(void*)
|
||||
{
|
||||
try
|
||||
{
|
||||
sGlobalSerialQueue = new CADispatchQueue("com.apple.audio.CADispatchQueue.SerialQueue");
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
sGlobalSerialQueue = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CADispatchQueue* CADispatchQueue::sGlobalSerialQueue = NULL;
|
||||
dispatch_once_t CADispatchQueue::sGlobalSerialQueueInitialized = 0;
|
||||
235
driver/PublicUtility/CADispatchQueue.h
Normal file
235
driver/PublicUtility/CADispatchQueue.h
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
File: CADispatchQueue.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
/*==================================================================================================
|
||||
CADispatchQueue.h
|
||||
==================================================================================================*/
|
||||
#if !defined(__CADispatchQueue_h__)
|
||||
#define __CADispatchQueue_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// System Includes
|
||||
#include <CoreFoundation/CFString.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
// Standard Library Includes
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
/*==================================================================================================
|
||||
CADispatchQueue
|
||||
|
||||
This class provides a wrapper for a libdispatch dispatch queue and several kinds of event
|
||||
sources such as MIG servers, port death notifications and other mach port related support. Being
|
||||
a libdispatch client, the unit of work is represented as either a function pointer and context
|
||||
pointer pair or as a Block.
|
||||
|
||||
One thing to keep in mind when using a Block-based construct is that you get a copy of the
|
||||
pointer but you do not get a copy of the memory that the pointer is pointing at. The net effect
|
||||
is that if you have a task that frees the memory that is referenced by a subsequent task,
|
||||
this second task would crash when dereferencing the pointer. This means that every task
|
||||
needs to include some code to validate any pointers it is using before dereferencing them.
|
||||
|
||||
A common example of this problem comes up with C++ objects. Suppose you have an instance method
|
||||
that creates a Block that calls other instance methods or accesses the object's fields. Suppose
|
||||
further that the Block is submitted to a dispatch queue for execution. If the object gets
|
||||
deallocated before the Block can run, the Block will crash. Thus, the Block needs to validate
|
||||
that the "this" pointer is still valid when it executes.
|
||||
|
||||
Another place where this comes up often is when attempting to implment a function by dispatching
|
||||
the work asynchronously. Any arguments to the function that point to the stack will be invalid.
|
||||
This is particularly troublesome inside of a MIG function.
|
||||
|
||||
Still another common issue with using Blocks and dispatch functions with C++ is that it is vital
|
||||
that no exceptions ever leave a Block or a dispatch function. If an exception was thrown out of
|
||||
a block, the result would be undefined and probably would result in the program calling
|
||||
the terminate() function in the standard C++ library (whose default implementation is to call
|
||||
abort(3)). Given that, all Blocks and dispatch functions that might end up throwing an exception
|
||||
need to catch those exceptions.
|
||||
==================================================================================================*/
|
||||
|
||||
class CADispatchQueue
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
public:
|
||||
CADispatchQueue(const char* inName);
|
||||
CADispatchQueue(CFStringRef inName);
|
||||
CADispatchQueue(CFStringRef inPattern, CFStringRef inName);
|
||||
virtual ~CADispatchQueue();
|
||||
|
||||
private:
|
||||
CADispatchQueue(const CADispatchQueue&);
|
||||
CADispatchQueue& operator=(const CADispatchQueue&);
|
||||
|
||||
#pragma mark Execution Operations
|
||||
public:
|
||||
void Dispatch(bool inDoSync, dispatch_block_t inTask) const;
|
||||
void Dispatch(UInt64 inNanoseconds, dispatch_block_t inTask) const;
|
||||
|
||||
void Dispatch(bool inDoSync, void* inTaskContext, dispatch_function_t inTask) const;
|
||||
void Dispatch(UInt64 inNanoseconds, void* inTaskContext, dispatch_function_t inTask) const;
|
||||
|
||||
static void Dispatch_Global(dispatch_queue_priority_t inQueuePriority, bool inDoSync, dispatch_block_t inTask);
|
||||
static void Dispatch_Global(dispatch_queue_priority_t inQueuePriority, UInt64 inNanoseconds, dispatch_block_t inTask);
|
||||
|
||||
static void Dispatch_Global(dispatch_queue_priority_t inQueuePriority, bool inDoSync, void* inTaskContext, dispatch_function_t inTask);
|
||||
static void Dispatch_Global(dispatch_queue_priority_t inQueuePriority, UInt64 inNanoseconds, void* inTaskContext, dispatch_function_t inTask);
|
||||
|
||||
static void Dispatch_Main(bool inDoSync, dispatch_block_t inTask);
|
||||
static void Dispatch_Main(UInt64 inNanoseconds, dispatch_block_t inTask);
|
||||
|
||||
static void Dispatch_Main(bool inDoSync, void* inTaskContext, dispatch_function_t inTask);
|
||||
static void Dispatch_Main(UInt64 inNanoseconds, void* inTaskContext, dispatch_function_t inTask);
|
||||
|
||||
#pragma mark Event Sources
|
||||
public:
|
||||
void InstallMachPortDeathNotification(mach_port_t inMachPort, dispatch_block_t inNotificationTask);
|
||||
void RemoveMachPortDeathNotification(mach_port_t inMachPort);
|
||||
|
||||
void InstallMachPortReceiver(mach_port_t inMachPort, dispatch_block_t inMessageTask);
|
||||
void RemoveMachPortReceiver(mach_port_t inMachPort, dispatch_block_t inCompletionTask);
|
||||
void RemoveMachPortReceiver(mach_port_t inMachPort, bool inDestroySendRight, bool inDestroyReceiveRight);
|
||||
|
||||
#pragma mark Implementation
|
||||
public:
|
||||
dispatch_queue_t GetDispatchQueue() const;
|
||||
|
||||
static CADispatchQueue& GetGlobalSerialQueue();
|
||||
|
||||
protected:
|
||||
static void InitializeGlobalSerialQueue(void*);
|
||||
|
||||
struct EventSource
|
||||
{
|
||||
dispatch_source_t mDispatchSource;
|
||||
mach_port_t mMachPort;
|
||||
|
||||
EventSource();
|
||||
EventSource(dispatch_source_t inDispatchSource, mach_port_t inMachPort);
|
||||
EventSource(const EventSource& inEventSource);
|
||||
EventSource& operator=(const EventSource& inEventSource);
|
||||
~EventSource();
|
||||
void Retain();
|
||||
void Release();
|
||||
};
|
||||
typedef std::vector<EventSource> EventSourceList;
|
||||
|
||||
dispatch_queue_t mDispatchQueue;
|
||||
EventSourceList mPortDeathList;
|
||||
EventSourceList mMachPortReceiverList;
|
||||
|
||||
static CADispatchQueue* sGlobalSerialQueue;
|
||||
static dispatch_once_t sGlobalSerialQueueInitialized;
|
||||
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
#pragma mark CADispatchQueue Inline Method Implementations
|
||||
//==================================================================================================
|
||||
|
||||
inline dispatch_queue_t CADispatchQueue::GetDispatchQueue() const
|
||||
{
|
||||
return mDispatchQueue;
|
||||
}
|
||||
|
||||
inline CADispatchQueue::EventSource::EventSource()
|
||||
:
|
||||
mDispatchSource(NULL),
|
||||
mMachPort(MACH_PORT_NULL)
|
||||
{
|
||||
}
|
||||
|
||||
inline CADispatchQueue::EventSource::EventSource(dispatch_source_t inDispatchSource, mach_port_t inMachPort)
|
||||
:
|
||||
mDispatchSource(inDispatchSource),
|
||||
mMachPort(inMachPort)
|
||||
{
|
||||
}
|
||||
|
||||
inline CADispatchQueue::EventSource::EventSource(const EventSource& inEventSource)
|
||||
:
|
||||
mDispatchSource(inEventSource.mDispatchSource),
|
||||
mMachPort(inEventSource.mMachPort)
|
||||
{
|
||||
Retain();
|
||||
}
|
||||
|
||||
inline CADispatchQueue::EventSource& CADispatchQueue::EventSource::operator=(const EventSource& inEventSource)
|
||||
{
|
||||
Release();
|
||||
mDispatchSource = inEventSource.mDispatchSource;
|
||||
mMachPort = inEventSource.mMachPort;
|
||||
Retain();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline CADispatchQueue::EventSource::~EventSource()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
inline void CADispatchQueue::EventSource::Retain()
|
||||
{
|
||||
if(mDispatchSource != NULL)
|
||||
{
|
||||
dispatch_retain(mDispatchSource);
|
||||
}
|
||||
}
|
||||
|
||||
inline void CADispatchQueue::EventSource::Release()
|
||||
{
|
||||
if(mDispatchSource != NULL)
|
||||
{
|
||||
dispatch_release(mDispatchSource);
|
||||
mDispatchSource = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __CADispatchQueue_h__
|
||||
83
driver/PublicUtility/CAException.h
Normal file
83
driver/PublicUtility/CAException.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
File: CAException.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAException_h__)
|
||||
#define __CAException_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include "CoreAudioTypes.h"
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CAException
|
||||
//=============================================================================
|
||||
|
||||
class CAException
|
||||
{
|
||||
|
||||
public:
|
||||
CAException(OSStatus inError) : mError(inError) {}
|
||||
CAException(const CAException& inException) : mError(inException.mError) {}
|
||||
CAException& operator=(const CAException& inException) { mError = inException.mError; return *this; }
|
||||
~CAException() {}
|
||||
|
||||
OSStatus GetError() const { return mError; }
|
||||
|
||||
protected:
|
||||
OSStatus mError;
|
||||
};
|
||||
|
||||
#define CATry try{
|
||||
#define CACatch } catch(...) {}
|
||||
#define CASwallowException(inExpression) try { inExpression; } catch(...) {}
|
||||
|
||||
#endif
|
||||
99
driver/PublicUtility/CAHostTimeBase.cpp
Normal file
99
driver/PublicUtility/CAHostTimeBase.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
File: CAHostTimeBase.cpp
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#include "CAHostTimeBase.h"
|
||||
|
||||
Float64 CAHostTimeBase::sFrequency = 0;
|
||||
Float64 CAHostTimeBase::sInverseFrequency = 0;
|
||||
UInt32 CAHostTimeBase::sMinDelta = 0;
|
||||
UInt32 CAHostTimeBase::sToNanosNumerator = 0;
|
||||
UInt32 CAHostTimeBase::sToNanosDenominator = 0;
|
||||
pthread_once_t CAHostTimeBase::sIsInited = PTHREAD_ONCE_INIT;
|
||||
#if Track_Host_TimeBase
|
||||
UInt64 CAHostTimeBase::sLastTime = 0;
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CAHostTimeBase
|
||||
//
|
||||
// This class provides platform independent access to the host's time base.
|
||||
//=============================================================================
|
||||
|
||||
void CAHostTimeBase::Initialize()
|
||||
{
|
||||
// get the info about Absolute time
|
||||
#if TARGET_OS_MAC
|
||||
struct mach_timebase_info theTimeBaseInfo;
|
||||
mach_timebase_info(&theTimeBaseInfo);
|
||||
sMinDelta = 1;
|
||||
sToNanosNumerator = theTimeBaseInfo.numer;
|
||||
sToNanosDenominator = theTimeBaseInfo.denom;
|
||||
|
||||
// the frequency of that clock is: (sToNanosDenominator / sToNanosNumerator) * 10^9
|
||||
sFrequency = static_cast<Float64>(sToNanosDenominator) / static_cast<Float64>(sToNanosNumerator);
|
||||
sFrequency *= 1000000000.0;
|
||||
#elif TARGET_OS_WIN32
|
||||
LARGE_INTEGER theFrequency;
|
||||
QueryPerformanceFrequency(&theFrequency);
|
||||
sMinDelta = 1;
|
||||
sToNanosNumerator = 1000000000ULL;
|
||||
sToNanosDenominator = *((UInt64*)&theFrequency);
|
||||
sFrequency = static_cast<Float64>(*((UInt64*)&theFrequency));
|
||||
#endif
|
||||
sInverseFrequency = 1.0 / sFrequency;
|
||||
|
||||
#if Log_Host_Time_Base_Parameters
|
||||
DebugPrintf("Host Time Base Parameters");
|
||||
DebugPrintf(" Minimum Delta: %lu", (unsigned long)sMinDelta);
|
||||
DebugPrintf(" Frequency: %f", sFrequency);
|
||||
DebugPrintf(" To Nanos Numerator: %lu", (unsigned long)sToNanosNumerator);
|
||||
DebugPrintf(" To Nanos Denominator: %lu", (unsigned long)sToNanosDenominator);
|
||||
#endif
|
||||
}
|
||||
234
driver/PublicUtility/CAHostTimeBase.h
Normal file
234
driver/PublicUtility/CAHostTimeBase.h
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
File: CAHostTimeBase.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAHostTimeBase_h__)
|
||||
#define __CAHostTimeBase_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
#include <mach/mach_time.h>
|
||||
#include <pthread.h>
|
||||
#elif TARGET_OS_WIN32
|
||||
#include <windows.h>
|
||||
#include "WinPThreadDefs.h"
|
||||
#else
|
||||
#error Unsupported operating system
|
||||
#endif
|
||||
|
||||
#include "CADebugPrintf.h"
|
||||
|
||||
//=============================================================================
|
||||
// CAHostTimeBase
|
||||
//
|
||||
// This class provides platform independent access to the host's time base.
|
||||
//=============================================================================
|
||||
|
||||
#if CoreAudio_Debug
|
||||
// #define Log_Host_Time_Base_Parameters 1
|
||||
// #define Track_Host_TimeBase 1
|
||||
#endif
|
||||
|
||||
class CAHostTimeBase
|
||||
{
|
||||
|
||||
public:
|
||||
static UInt64 ConvertToNanos(UInt64 inHostTime);
|
||||
static UInt64 ConvertFromNanos(UInt64 inNanos);
|
||||
|
||||
static UInt64 GetTheCurrentTime();
|
||||
#if TARGET_OS_MAC
|
||||
static UInt64 GetCurrentTime() { return GetTheCurrentTime(); }
|
||||
#endif
|
||||
static UInt64 GetCurrentTimeInNanos();
|
||||
|
||||
static Float64 GetFrequency() { pthread_once(&sIsInited, Initialize); return sFrequency; }
|
||||
static Float64 GetInverseFrequency() { pthread_once(&sIsInited, Initialize); return sInverseFrequency; }
|
||||
static UInt32 GetMinimumDelta() { pthread_once(&sIsInited, Initialize); return sMinDelta; }
|
||||
|
||||
static UInt64 AbsoluteHostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime);
|
||||
static SInt64 HostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime);
|
||||
|
||||
static UInt64 MultiplyByRatio(UInt64 inMuliplicand, UInt32 inNumerator, UInt32 inDenominator);
|
||||
|
||||
private:
|
||||
static void Initialize();
|
||||
|
||||
static pthread_once_t sIsInited;
|
||||
|
||||
static Float64 sFrequency;
|
||||
static Float64 sInverseFrequency;
|
||||
static UInt32 sMinDelta;
|
||||
static UInt32 sToNanosNumerator;
|
||||
static UInt32 sToNanosDenominator;
|
||||
#if Track_Host_TimeBase
|
||||
static UInt64 sLastTime;
|
||||
#endif
|
||||
};
|
||||
|
||||
inline UInt64 CAHostTimeBase::GetTheCurrentTime()
|
||||
{
|
||||
UInt64 theTime = 0;
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
theTime = mach_absolute_time();
|
||||
#elif TARGET_OS_WIN32
|
||||
LARGE_INTEGER theValue;
|
||||
QueryPerformanceCounter(&theValue);
|
||||
theTime = *((UInt64*)&theValue);
|
||||
#endif
|
||||
|
||||
#if Track_Host_TimeBase
|
||||
if(sLastTime != 0)
|
||||
{
|
||||
if(theTime <= sLastTime)
|
||||
{
|
||||
DebugPrintf("CAHostTimeBase::GetTheCurrentTime: the current time is earlier than the last time, now: %qd, then: %qd", theTime, sLastTime);
|
||||
}
|
||||
sLastTime = theTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
sLastTime = theTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
return theTime;
|
||||
}
|
||||
|
||||
inline UInt64 CAHostTimeBase::ConvertToNanos(UInt64 inHostTime)
|
||||
{
|
||||
pthread_once(&sIsInited, Initialize);
|
||||
|
||||
UInt64 theAnswer = MultiplyByRatio(inHostTime, sToNanosNumerator, sToNanosDenominator);
|
||||
#if CoreAudio_Debug
|
||||
if(((sToNanosNumerator > sToNanosDenominator) && (theAnswer < inHostTime)) || ((sToNanosDenominator > sToNanosNumerator) && (theAnswer > inHostTime)))
|
||||
{
|
||||
DebugPrintf("CAHostTimeBase::ConvertToNanos: The conversion wrapped");
|
||||
}
|
||||
#endif
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline UInt64 CAHostTimeBase::ConvertFromNanos(UInt64 inNanos)
|
||||
{
|
||||
pthread_once(&sIsInited, Initialize);
|
||||
|
||||
UInt64 theAnswer = MultiplyByRatio(inNanos, sToNanosDenominator, sToNanosNumerator);
|
||||
#if CoreAudio_Debug
|
||||
if(((sToNanosDenominator > sToNanosNumerator) && (theAnswer < inNanos)) || ((sToNanosNumerator > sToNanosDenominator) && (theAnswer > inNanos)))
|
||||
{
|
||||
DebugPrintf("CAHostTimeBase::ConvertFromNanos: The conversion wrapped");
|
||||
}
|
||||
#endif
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline UInt64 CAHostTimeBase::GetCurrentTimeInNanos()
|
||||
{
|
||||
return ConvertToNanos(GetTheCurrentTime());
|
||||
}
|
||||
|
||||
inline UInt64 CAHostTimeBase::AbsoluteHostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime)
|
||||
{
|
||||
UInt64 theAnswer;
|
||||
|
||||
if(inStartTime <= inEndTime)
|
||||
{
|
||||
theAnswer = inEndTime - inStartTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = inStartTime - inEndTime;
|
||||
}
|
||||
|
||||
return ConvertToNanos(theAnswer);
|
||||
}
|
||||
|
||||
inline SInt64 CAHostTimeBase::HostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime)
|
||||
{
|
||||
SInt64 theAnswer;
|
||||
SInt64 theSign = 1;
|
||||
|
||||
if(inStartTime <= inEndTime)
|
||||
{
|
||||
theAnswer = static_cast<SInt64>(inEndTime - inStartTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = static_cast<SInt64>(inStartTime - inEndTime);
|
||||
theSign = -1;
|
||||
}
|
||||
|
||||
return theSign * static_cast<SInt64>(ConvertToNanos(static_cast<UInt64>(theAnswer)));
|
||||
}
|
||||
|
||||
inline UInt64 CAHostTimeBase::MultiplyByRatio(UInt64 inMuliplicand, UInt32 inNumerator, UInt32 inDenominator)
|
||||
{
|
||||
#if TARGET_OS_MAC && TARGET_RT_64_BIT
|
||||
__uint128_t theAnswer = inMuliplicand;
|
||||
#else
|
||||
long double theAnswer = inMuliplicand;
|
||||
#endif
|
||||
if(inNumerator != inDenominator)
|
||||
{
|
||||
theAnswer *= inNumerator;
|
||||
theAnswer /= inDenominator;
|
||||
}
|
||||
return static_cast<UInt64>(theAnswer);
|
||||
}
|
||||
|
||||
#endif
|
||||
345
driver/PublicUtility/CAMutex.cpp
Normal file
345
driver/PublicUtility/CAMutex.cpp
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
File: CAMutex.cpp
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CAMutex.h"
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
#include "CAHostTimeBase.h"
|
||||
|
||||
//==================================================================================================
|
||||
// Logging
|
||||
//==================================================================================================
|
||||
|
||||
#if CoreAudio_Debug
|
||||
// #define Log_Ownership 1
|
||||
// #define Log_Errors 1
|
||||
// #define Log_LongLatencies 1
|
||||
// #define LongLatencyThreshholdNS 1000000ULL // nanoseconds
|
||||
#endif
|
||||
|
||||
//==================================================================================================
|
||||
// CAMutex
|
||||
//==================================================================================================
|
||||
|
||||
CAMutex::CAMutex(const char* inName)
|
||||
:
|
||||
mName(inName),
|
||||
mOwner(0)
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
OSStatus theError = pthread_mutex_init(&mMutex, NULL);
|
||||
ThrowIf(theError != 0, CAException(theError), "CAMutex::CAMutex: Could not init the mutex");
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::CAMutex: creating %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
|
||||
#endif
|
||||
#elif TARGET_OS_WIN32
|
||||
mMutex = CreateMutex(NULL, false, NULL);
|
||||
ThrowIfNULL(mMutex, CAException(GetLastError()), "CAMutex::CAMutex: could not create the mutex.");
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::CAMutex: creating %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
CAMutex::~CAMutex()
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::~CAMutex: destroying %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
|
||||
#endif
|
||||
pthread_mutex_destroy(&mMutex);
|
||||
#elif TARGET_OS_WIN32
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::~CAMutex: destroying %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
|
||||
#endif
|
||||
if(mMutex != NULL)
|
||||
{
|
||||
CloseHandle(mMutex);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CAMutex::Lock()
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
pthread_t theCurrentThread = pthread_self();
|
||||
if(!pthread_equal(theCurrentThread, mOwner))
|
||||
{
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Lock: thread %p is locking %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
|
||||
#endif
|
||||
|
||||
#if Log_LongLatencies
|
||||
UInt64 lockTryTime = CAHostTimeBase::GetCurrentTimeInNanos();
|
||||
#endif
|
||||
|
||||
OSStatus theError = pthread_mutex_lock(&mMutex);
|
||||
ThrowIf(theError != 0, CAException(theError), "CAMutex::Lock: Could not lock the mutex");
|
||||
mOwner = theCurrentThread;
|
||||
theAnswer = true;
|
||||
|
||||
#if Log_LongLatencies
|
||||
UInt64 lockAcquireTime = CAHostTimeBase::GetCurrentTimeInNanos();
|
||||
if (lockAcquireTime - lockTryTime >= LongLatencyThresholdNS)
|
||||
DebugPrintfRtn(DebugPrintfFileComma "Thread %p took %.6fs to acquire the lock %s\n", theCurrentThread, (lockAcquireTime - lockTryTime) * 1.0e-9 /* nanos to seconds */, mName);
|
||||
#endif
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Lock: thread %p has locked %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(mOwner != GetCurrentThreadId())
|
||||
{
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Lock: thread %lu is locking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
|
||||
OSStatus theError = WaitForSingleObject(mMutex, INFINITE);
|
||||
ThrowIfError(theError, CAException(theError), "CAMutex::Lock: could not lock the mutex");
|
||||
mOwner = GetCurrentThreadId();
|
||||
theAnswer = true;
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Lock: thread %lu has locked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CAMutex::Unlock()
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
if(pthread_equal(pthread_self(), mOwner))
|
||||
{
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Unlock: thread %p is unlocking %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner);
|
||||
#endif
|
||||
|
||||
mOwner = 0;
|
||||
OSStatus theError = pthread_mutex_unlock(&mMutex);
|
||||
ThrowIf(theError != 0, CAException(theError), "CAMutex::Unlock: Could not unlock the mutex");
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Unlock: thread %p has unlocked %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugMessage("CAMutex::Unlock: A thread is attempting to unlock a Mutex it doesn't own");
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(mOwner == GetCurrentThreadId())
|
||||
{
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Unlock: thread %lu is unlocking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
|
||||
mOwner = 0;
|
||||
bool wasReleased = ReleaseMutex(mMutex);
|
||||
ThrowIf(!wasReleased, CAException(GetLastError()), "CAMutex::Unlock: Could not unlock the mutex");
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Unlock: thread %lu has unlocked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugMessage("CAMutex::Unlock: A thread is attempting to unlock a Mutex it doesn't own");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CAMutex::Try(bool& outWasLocked)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
outWasLocked = false;
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
pthread_t theCurrentThread = pthread_self();
|
||||
if(!pthread_equal(theCurrentThread, mOwner))
|
||||
{
|
||||
// this means the current thread doesn't already own the lock
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Try: thread %p is try-locking %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
|
||||
#endif
|
||||
|
||||
// go ahead and call trylock to see if we can lock it.
|
||||
int theError = pthread_mutex_trylock(&mMutex);
|
||||
if(theError == 0)
|
||||
{
|
||||
// return value of 0 means we successfully locked the lock
|
||||
mOwner = theCurrentThread;
|
||||
theAnswer = true;
|
||||
outWasLocked = true;
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Try: thread %p has locked %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else if(theError == EBUSY)
|
||||
{
|
||||
// return value of EBUSY means that the lock was already locked by another thread
|
||||
theAnswer = false;
|
||||
outWasLocked = false;
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Try: thread %p failed to lock %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// any other return value means something really bad happenned
|
||||
ThrowIfError(theError, CAException(theError), "CAMutex::Try: call to pthread_mutex_trylock failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this means the current thread already owns the lock
|
||||
theAnswer = true;
|
||||
outWasLocked = false;
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(mOwner != GetCurrentThreadId())
|
||||
{
|
||||
// this means the current thread doesn't own the lock
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Try: thread %lu is try-locking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
|
||||
// try to acquire the mutex
|
||||
OSStatus theError = WaitForSingleObject(mMutex, 0);
|
||||
if(theError == WAIT_OBJECT_0)
|
||||
{
|
||||
// this means we successfully locked the lock
|
||||
mOwner = GetCurrentThreadId();
|
||||
theAnswer = true;
|
||||
outWasLocked = true;
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Try: thread %lu has locked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else if(theError == WAIT_TIMEOUT)
|
||||
{
|
||||
// this means that the lock was already locked by another thread
|
||||
theAnswer = false;
|
||||
outWasLocked = false;
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Try: thread %lu failed to lock %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// any other return value means something really bad happenned
|
||||
ThrowIfError(theError, CAException(GetLastError()), "CAMutex::Try: call to lock the mutex failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this means the current thread already owns the lock
|
||||
theAnswer = true;
|
||||
outWasLocked = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CAMutex::IsFree() const
|
||||
{
|
||||
return mOwner == 0;
|
||||
}
|
||||
|
||||
bool CAMutex::IsOwnedByCurrentThread() const
|
||||
{
|
||||
bool theAnswer = true;
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
theAnswer = pthread_equal(pthread_self(), mOwner);
|
||||
#elif TARGET_OS_WIN32
|
||||
theAnswer = (mOwner == GetCurrentThreadId());
|
||||
#endif
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
|
||||
CAMutex::Unlocker::Unlocker(CAMutex& inMutex)
|
||||
: mMutex(inMutex),
|
||||
mNeedsLock(false)
|
||||
{
|
||||
Assert(mMutex.IsOwnedByCurrentThread(), "Major problem: Unlocker attempted to unlock a mutex not owned by the current thread!");
|
||||
|
||||
mMutex.Unlock();
|
||||
mNeedsLock = true;
|
||||
}
|
||||
|
||||
CAMutex::Unlocker::~Unlocker()
|
||||
{
|
||||
if(mNeedsLock)
|
||||
{
|
||||
mMutex.Lock();
|
||||
}
|
||||
}
|
||||
164
driver/PublicUtility/CAMutex.h
Normal file
164
driver/PublicUtility/CAMutex.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
File: CAMutex.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#ifndef __CAMutex_h__
|
||||
#define __CAMutex_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// System Includes
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
#include <pthread.h>
|
||||
#elif TARGET_OS_WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#error Unsupported operating system
|
||||
#endif
|
||||
|
||||
//==================================================================================================
|
||||
// A recursive mutex.
|
||||
//==================================================================================================
|
||||
|
||||
class CAMutex
|
||||
{
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAMutex(const char* inName);
|
||||
virtual ~CAMutex();
|
||||
|
||||
// Actions
|
||||
public:
|
||||
virtual bool Lock();
|
||||
virtual void Unlock();
|
||||
virtual bool Try(bool& outWasLocked); // returns true if lock is free, false if not
|
||||
|
||||
virtual bool IsFree() const;
|
||||
virtual bool IsOwnedByCurrentThread() const;
|
||||
|
||||
// Implementation
|
||||
protected:
|
||||
const char* mName;
|
||||
#if TARGET_OS_MAC
|
||||
pthread_t mOwner;
|
||||
pthread_mutex_t mMutex;
|
||||
#elif TARGET_OS_WIN32
|
||||
UInt32 mOwner;
|
||||
HANDLE mMutex;
|
||||
#endif
|
||||
|
||||
// Helper class to manage taking and releasing recursively
|
||||
public:
|
||||
class Locker
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
Locker(CAMutex& inMutex) : mMutex(&inMutex), mNeedsRelease(false) { mNeedsRelease = mMutex->Lock(); }
|
||||
Locker(const CAMutex& inMutex) : mMutex(const_cast<CAMutex*>(&inMutex)), mNeedsRelease(false) { mNeedsRelease = mMutex->Lock(); }
|
||||
Locker(CAMutex* inMutex) : mMutex(inMutex), mNeedsRelease(false) { mNeedsRelease = (mMutex != NULL && mMutex->Lock()); }
|
||||
// in this case the mutex can be null
|
||||
~Locker() { if(mNeedsRelease) { mMutex->Unlock(); } }
|
||||
|
||||
|
||||
private:
|
||||
Locker(const Locker&);
|
||||
Locker& operator=(const Locker&);
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
CAMutex* mMutex;
|
||||
bool mNeedsRelease;
|
||||
|
||||
};
|
||||
|
||||
// Unlocker
|
||||
class Unlocker
|
||||
{
|
||||
public:
|
||||
Unlocker(CAMutex& inMutex);
|
||||
~Unlocker();
|
||||
|
||||
private:
|
||||
CAMutex& mMutex;
|
||||
bool mNeedsLock;
|
||||
|
||||
// Hidden definitions of copy ctor, assignment operator
|
||||
Unlocker(const Unlocker& copy); // Not implemented
|
||||
Unlocker& operator=(const Unlocker& copy); // Not implemented
|
||||
};
|
||||
|
||||
// you can use this with Try - if you take the lock in try, pass in the outWasLocked var
|
||||
class Tryer {
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
Tryer (CAMutex &mutex) : mMutex(mutex), mNeedsRelease(false), mHasLock(false) { mHasLock = mMutex.Try (mNeedsRelease); }
|
||||
~Tryer () { if (mNeedsRelease) mMutex.Unlock(); }
|
||||
|
||||
bool HasLock () const { return mHasLock; }
|
||||
|
||||
private:
|
||||
Tryer(const Tryer&);
|
||||
Tryer& operator=(const Tryer&);
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
CAMutex & mMutex;
|
||||
bool mNeedsRelease;
|
||||
bool mHasLock;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif // __CAMutex_h__
|
||||
450
driver/PublicUtility/CAPThread.cpp
Normal file
450
driver/PublicUtility/CAPThread.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
File: CAPThread.cpp
|
||||
Abstract: CAPThread.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.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CAPThread.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
|
||||
// System Includes
|
||||
#if TARGET_OS_MAC
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
// Standard Library Includes
|
||||
#include <stdio.h>
|
||||
|
||||
//==================================================================================================
|
||||
// CAPThread
|
||||
//==================================================================================================
|
||||
|
||||
// returns the thread's priority as it was last set by the API
|
||||
#define CAPTHREAD_SET_PRIORITY 0
|
||||
// returns the thread's priority as it was last scheduled by the Kernel
|
||||
#define CAPTHREAD_SCHEDULED_PRIORITY 1
|
||||
|
||||
//#define Log_SetPriority 1
|
||||
|
||||
CAPThread::CAPThread(ThreadRoutine inThreadRoutine, void* inParameter, UInt32 inPriority, bool inFixedPriority, bool inAutoDelete, const char* inThreadName)
|
||||
:
|
||||
#if TARGET_OS_MAC
|
||||
mPThread(0),
|
||||
mSpawningThreadPriority(getScheduledPriority(pthread_self(), CAPTHREAD_SET_PRIORITY)),
|
||||
#elif TARGET_OS_WIN32
|
||||
mThreadHandle(NULL),
|
||||
mThreadID(0),
|
||||
#endif
|
||||
mThreadRoutine(inThreadRoutine),
|
||||
mThreadParameter(inParameter),
|
||||
mPriority(inPriority),
|
||||
mPeriod(0),
|
||||
mComputation(0),
|
||||
mConstraint(0),
|
||||
mIsPreemptible(true),
|
||||
mTimeConstraintSet(false),
|
||||
mFixedPriority(inFixedPriority),
|
||||
mAutoDelete(inAutoDelete)
|
||||
{
|
||||
if(inThreadName != NULL)
|
||||
{
|
||||
strlcpy(mThreadName, inThreadName, kMaxThreadNameLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(mThreadName, 0, kMaxThreadNameLength);
|
||||
}
|
||||
}
|
||||
|
||||
CAPThread::CAPThread(ThreadRoutine inThreadRoutine, void* inParameter, UInt32 inPeriod, UInt32 inComputation, UInt32 inConstraint, bool inIsPreemptible, bool inAutoDelete, const char* inThreadName)
|
||||
:
|
||||
#if TARGET_OS_MAC
|
||||
mPThread(0),
|
||||
mSpawningThreadPriority(getScheduledPriority(pthread_self(), CAPTHREAD_SET_PRIORITY)),
|
||||
#elif TARGET_OS_WIN32
|
||||
mThreadHandle(NULL),
|
||||
mThreadID(0),
|
||||
#endif
|
||||
mThreadRoutine(inThreadRoutine),
|
||||
mThreadParameter(inParameter),
|
||||
mPriority(kDefaultThreadPriority),
|
||||
mPeriod(inPeriod),
|
||||
mComputation(inComputation),
|
||||
mConstraint(inConstraint),
|
||||
mIsPreemptible(inIsPreemptible),
|
||||
mTimeConstraintSet(true),
|
||||
mFixedPriority(false),
|
||||
mAutoDelete(inAutoDelete)
|
||||
{
|
||||
if(inThreadName != NULL)
|
||||
{
|
||||
strlcpy(mThreadName, inThreadName, kMaxThreadNameLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(mThreadName, 0, kMaxThreadNameLength);
|
||||
}
|
||||
}
|
||||
|
||||
CAPThread::~CAPThread()
|
||||
{
|
||||
}
|
||||
|
||||
UInt32 CAPThread::GetScheduledPriority()
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
return CAPThread::getScheduledPriority( mPThread, CAPTHREAD_SCHEDULED_PRIORITY );
|
||||
#elif TARGET_OS_WIN32
|
||||
UInt32 theAnswer = 0;
|
||||
if(mThreadHandle != NULL)
|
||||
{
|
||||
theAnswer = GetThreadPriority(mThreadHandle);
|
||||
}
|
||||
return theAnswer;
|
||||
#endif
|
||||
}
|
||||
|
||||
UInt32 CAPThread::GetScheduledPriority(NativeThread thread)
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
return getScheduledPriority( thread, CAPTHREAD_SCHEDULED_PRIORITY );
|
||||
#elif TARGET_OS_WIN32
|
||||
return 0; // ???
|
||||
#endif
|
||||
}
|
||||
|
||||
void CAPThread::SetPriority(UInt32 inPriority, bool inFixedPriority)
|
||||
{
|
||||
mPriority = inPriority;
|
||||
mTimeConstraintSet = false;
|
||||
mFixedPriority = inFixedPriority;
|
||||
#if TARGET_OS_MAC
|
||||
if(mPThread != 0)
|
||||
{
|
||||
SetPriority(mPThread, mPriority, mFixedPriority);
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(mThreadID != NULL)
|
||||
{
|
||||
SetPriority(mThreadID, mPriority, mFixedPriority);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CAPThread::SetPriority(NativeThread inThread, UInt32 inPriority, bool inFixedPriority)
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
if(inThread != 0)
|
||||
{
|
||||
kern_return_t theError = 0;
|
||||
|
||||
// set whether or not this is a fixed priority thread
|
||||
if (inFixedPriority)
|
||||
{
|
||||
thread_extended_policy_data_t theFixedPolicy = { false };
|
||||
theError = thread_policy_set(pthread_mach_thread_np(inThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
|
||||
AssertNoKernelError(theError, "CAPThread::SetPriority: failed to set the fixed-priority policy");
|
||||
}
|
||||
|
||||
// set the thread's absolute priority which is relative to the priority on which thread_policy_set() is called
|
||||
UInt32 theCurrentThreadPriority = getScheduledPriority(pthread_self(), CAPTHREAD_SET_PRIORITY);
|
||||
thread_precedence_policy_data_t thePrecedencePolicy = { static_cast<integer_t>(inPriority - theCurrentThreadPriority) };
|
||||
theError = thread_policy_set(pthread_mach_thread_np(inThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
|
||||
AssertNoKernelError(theError, "CAPThread::SetPriority: failed to set the precedence policy");
|
||||
|
||||
#if Log_SetPriority
|
||||
DebugMessageN4("CAPThread::SetPriority: requsted: %lu spawning: %lu current: %lu assigned: %d", mPriority, mSpawningThreadPriority, theCurrentThreadPriority, thePrecedencePolicy.importance);
|
||||
#endif
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(inThread != NULL)
|
||||
{
|
||||
HANDLE hThread = OpenThread(NULL, FALSE, inThread);
|
||||
if(hThread != NULL) {
|
||||
SetThreadPriority(hThread, inPriority);
|
||||
CloseHandle(hThread);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CAPThread::SetTimeConstraints(UInt32 inPeriod, UInt32 inComputation, UInt32 inConstraint, bool inIsPreemptible)
|
||||
{
|
||||
mPeriod = inPeriod;
|
||||
mComputation = inComputation;
|
||||
mConstraint = inConstraint;
|
||||
mIsPreemptible = inIsPreemptible;
|
||||
mTimeConstraintSet = true;
|
||||
#if TARGET_OS_MAC
|
||||
if(mPThread != 0)
|
||||
{
|
||||
thread_time_constraint_policy_data_t thePolicy;
|
||||
thePolicy.period = mPeriod;
|
||||
thePolicy.computation = mComputation;
|
||||
thePolicy.constraint = mConstraint;
|
||||
thePolicy.preemptible = mIsPreemptible;
|
||||
AssertNoError(thread_policy_set(pthread_mach_thread_np(mPThread), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t)&thePolicy, THREAD_TIME_CONSTRAINT_POLICY_COUNT), "CAPThread::SetTimeConstraints: thread_policy_set failed");
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(mThreadHandle != NULL)
|
||||
{
|
||||
SetThreadPriority(mThreadHandle, THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CAPThread::Start()
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
Assert(mPThread == 0, "CAPThread::Start: can't start because the thread is already running");
|
||||
if(mPThread == 0)
|
||||
{
|
||||
OSStatus theResult;
|
||||
pthread_attr_t theThreadAttributes;
|
||||
|
||||
theResult = pthread_attr_init(&theThreadAttributes);
|
||||
ThrowIf(theResult != 0, CAException(theResult), "CAPThread::Start: Thread attributes could not be created.");
|
||||
|
||||
theResult = pthread_attr_setdetachstate(&theThreadAttributes, PTHREAD_CREATE_DETACHED);
|
||||
ThrowIf(theResult != 0, CAException(theResult), "CAPThread::Start: A thread could not be created in the detached state.");
|
||||
|
||||
theResult = pthread_create(&mPThread, &theThreadAttributes, (ThreadRoutine)CAPThread::Entry, this);
|
||||
ThrowIf(theResult != 0 || !mPThread, CAException(theResult), "CAPThread::Start: Could not create a thread.");
|
||||
|
||||
pthread_attr_destroy(&theThreadAttributes);
|
||||
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
Assert(mThreadID == 0, "CAPThread::Start: can't start because the thread is already running");
|
||||
if(mThreadID == 0)
|
||||
{
|
||||
// clean up the existing thread handle
|
||||
if(mThreadHandle != NULL)
|
||||
{
|
||||
CloseHandle(mThreadHandle);
|
||||
mThreadHandle = NULL;
|
||||
}
|
||||
|
||||
// create a new thread
|
||||
mThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Entry, this, 0, &mThreadID);
|
||||
ThrowIf(mThreadHandle == NULL, CAException(GetLastError()), "CAPThread::Start: Could not create a thread.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
|
||||
void* CAPThread::Entry(CAPThread* inCAPThread)
|
||||
{
|
||||
void* theAnswer = NULL;
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
inCAPThread->mPThread = pthread_self();
|
||||
#elif TARGET_OS_WIN32
|
||||
// do we need to do something here?
|
||||
#endif
|
||||
|
||||
#if !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
|
||||
if(inCAPThread->mThreadName[0] != 0)
|
||||
{
|
||||
pthread_setname_np(inCAPThread->mThreadName);
|
||||
}
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
if(inCAPThread->mTimeConstraintSet)
|
||||
{
|
||||
inCAPThread->SetTimeConstraints(inCAPThread->mPeriod, inCAPThread->mComputation, inCAPThread->mConstraint, inCAPThread->mIsPreemptible);
|
||||
}
|
||||
else
|
||||
{
|
||||
inCAPThread->SetPriority(inCAPThread->mPriority, inCAPThread->mFixedPriority);
|
||||
}
|
||||
|
||||
if(inCAPThread->mThreadRoutine != NULL)
|
||||
{
|
||||
theAnswer = inCAPThread->mThreadRoutine(inCAPThread->mThreadParameter);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// what should be done here?
|
||||
}
|
||||
inCAPThread->mPThread = 0;
|
||||
if (inCAPThread->mAutoDelete)
|
||||
delete inCAPThread;
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
UInt32 CAPThread::getScheduledPriority(pthread_t inThread, int inPriorityKind)
|
||||
{
|
||||
thread_basic_info_data_t threadInfo;
|
||||
policy_info_data_t thePolicyInfo;
|
||||
unsigned int count;
|
||||
|
||||
if (inThread == NULL)
|
||||
return 0;
|
||||
|
||||
// get basic info
|
||||
count = THREAD_BASIC_INFO_COUNT;
|
||||
thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (thread_info_t)&threadInfo, &count);
|
||||
|
||||
switch (threadInfo.policy) {
|
||||
case POLICY_TIMESHARE:
|
||||
count = POLICY_TIMESHARE_INFO_COUNT;
|
||||
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (thread_info_t)&(thePolicyInfo.ts), &count);
|
||||
if (inPriorityKind == CAPTHREAD_SCHEDULED_PRIORITY) {
|
||||
return static_cast<UInt32>(thePolicyInfo.ts.cur_priority);
|
||||
}
|
||||
return static_cast<UInt32>(thePolicyInfo.ts.base_priority);
|
||||
break;
|
||||
|
||||
case POLICY_FIFO:
|
||||
count = POLICY_FIFO_INFO_COUNT;
|
||||
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (thread_info_t)&(thePolicyInfo.fifo), &count);
|
||||
if ( (thePolicyInfo.fifo.depressed) && (inPriorityKind == CAPTHREAD_SCHEDULED_PRIORITY) ) {
|
||||
return static_cast<UInt32>(thePolicyInfo.fifo.depress_priority);
|
||||
}
|
||||
return static_cast<UInt32>(thePolicyInfo.fifo.base_priority);
|
||||
break;
|
||||
|
||||
case POLICY_RR:
|
||||
count = POLICY_RR_INFO_COUNT;
|
||||
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (thread_info_t)&(thePolicyInfo.rr), &count);
|
||||
if ( (thePolicyInfo.rr.depressed) && (inPriorityKind == CAPTHREAD_SCHEDULED_PRIORITY) ) {
|
||||
return static_cast<UInt32>(thePolicyInfo.rr.depress_priority);
|
||||
}
|
||||
return static_cast<UInt32>(thePolicyInfo.rr.base_priority);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#elif TARGET_OS_WIN32
|
||||
|
||||
UInt32 WINAPI CAPThread::Entry(CAPThread* inCAPThread)
|
||||
{
|
||||
UInt32 theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if(inCAPThread->mTimeConstraintSet)
|
||||
{
|
||||
inCAPThread->SetTimeConstraints(inCAPThread->mPeriod, inCAPThread->mComputation, inCAPThread->mConstraint, inCAPThread->mIsPreemptible);
|
||||
}
|
||||
else
|
||||
{
|
||||
inCAPThread->SetPriority(inCAPThread->mPriority, inCAPThread->mFixedPriority);
|
||||
}
|
||||
|
||||
if(inCAPThread->mThreadRoutine != NULL)
|
||||
{
|
||||
theAnswer = reinterpret_cast<UInt32>(inCAPThread->mThreadRoutine(inCAPThread->mThreadParameter));
|
||||
}
|
||||
inCAPThread->mThreadID = 0;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// what should be done here?
|
||||
}
|
||||
CloseHandle(inCAPThread->mThreadHandle);
|
||||
inCAPThread->mThreadHandle = NULL;
|
||||
if (inCAPThread->mAutoDelete)
|
||||
delete inCAPThread;
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
Boolean CompareAndSwap(UInt32 inOldValue, UInt32 inNewValue, UInt32* inOldValuePtr)
|
||||
{
|
||||
return InterlockedCompareExchange((volatile LONG*)inOldValuePtr, inNewValue, inOldValue) == inOldValue;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void CAPThread::SetName(const char* inThreadName)
|
||||
{
|
||||
if(inThreadName != NULL)
|
||||
{
|
||||
strlcpy(mThreadName, inThreadName, kMaxThreadNameLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(mThreadName, 0, kMaxThreadNameLength);
|
||||
}
|
||||
}
|
||||
|
||||
#if CoreAudio_Debug
|
||||
void CAPThread::DebugPriority(const char *label)
|
||||
{
|
||||
#if !TARGET_OS_WIN32
|
||||
if (mTimeConstraintSet)
|
||||
printf("CAPThread::%s %p: pri=<time constraint>, spawning pri=%d, scheduled pri=%d\n", label, this,
|
||||
(int)mSpawningThreadPriority, (mPThread != NULL) ? (int)GetScheduledPriority() : -1);
|
||||
else
|
||||
printf("CAPThread::%s %p: pri=%d%s, spawning pri=%d, scheduled pri=%d\n", label, this, (int)mPriority, mFixedPriority ? " fixed" : "",
|
||||
(int)mSpawningThreadPriority, (mPThread != NULL) ? (int)GetScheduledPriority() : -1);
|
||||
#else
|
||||
if (mTimeConstraintSet)
|
||||
{
|
||||
printf("CAPThread::%s %p: pri=<time constraint>, spawning pri=%d, scheduled pri=%d\n", label, this,
|
||||
(int)mPriority, (mThreadHandle != NULL) ? (int)GetScheduledPriority() : -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("CAPThread::%s %p: pri=%d%s, spawning pri=%d, scheduled pri=%d\n", label, this, (int)mPriority, mFixedPriority ? " fixed" : "",
|
||||
(int)mPriority, (mThreadHandle != NULL) ? (int)GetScheduledPriority() : -1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
191
driver/PublicUtility/CAPThread.h
Normal file
191
driver/PublicUtility/CAPThread.h
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
File: CAPThread.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.
|
||||
|
||||
*/
|
||||
#if !defined(__CAPThread_h__)
|
||||
#define __CAPThread_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// System Includes
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
#else
|
||||
#include <CFBase.h>
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#elif TARGET_OS_WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#error Unsupported operating system
|
||||
#endif
|
||||
|
||||
//==================================================================================================
|
||||
// CAPThread
|
||||
//
|
||||
// This class wraps a pthread and a Win32 thread.
|
||||
// caution: long-running fixed priority threads can make the system unresponsive
|
||||
//==================================================================================================
|
||||
|
||||
class CAPThread
|
||||
{
|
||||
|
||||
// Types
|
||||
public:
|
||||
typedef void* (*ThreadRoutine)(void* inParameter);
|
||||
|
||||
// Constants
|
||||
public:
|
||||
enum
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
kMinThreadPriority = 1,
|
||||
kMaxThreadPriority = 63,
|
||||
kDefaultThreadPriority = 31,
|
||||
kMaxThreadNameLength = 64
|
||||
#elif TARGET_OS_WIN32
|
||||
kMinThreadPriority = 1,
|
||||
kMaxThreadPriority = 31,
|
||||
kDefaultThreadPriority = THREAD_PRIORITY_NORMAL,
|
||||
kMaxThreadNameLength = 256
|
||||
#endif
|
||||
};
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAPThread(ThreadRoutine inThreadRoutine, void* inParameter, UInt32 inPriority = kDefaultThreadPriority, bool inFixedPriority=false, bool inAutoDelete=false, const char* inThreadName = NULL);
|
||||
CAPThread(ThreadRoutine inThreadRoutine, void* inParameter, UInt32 inPeriod, UInt32 inComputation, UInt32 inConstraint, bool inIsPreemptible, bool inAutoDelete=false, const char* inThreadName = NULL);
|
||||
virtual ~CAPThread();
|
||||
|
||||
// Properties
|
||||
public:
|
||||
#if TARGET_OS_MAC
|
||||
typedef pthread_t NativeThread;
|
||||
|
||||
NativeThread GetNativeThread() { return mPThread; }
|
||||
static NativeThread GetCurrentThread() { return pthread_self(); }
|
||||
static bool IsNativeThreadsEqual(NativeThread a, NativeThread b) { return (a==b); }
|
||||
|
||||
bool operator==(NativeThread b) { return pthread_equal(mPThread,b); }
|
||||
|
||||
pthread_t GetPThread() const { return mPThread; }
|
||||
bool IsCurrentThread() const { return (0 != mPThread) && (pthread_self() == mPThread); }
|
||||
bool IsRunning() const { return 0 != mPThread; }
|
||||
static UInt32 getScheduledPriority(pthread_t inThread, int inPriorityKind);
|
||||
#elif TARGET_OS_WIN32
|
||||
typedef unsigned long NativeThread;
|
||||
|
||||
NativeThread GetNativeThread() { return mThreadID; }
|
||||
static NativeThread GetCurrentThread() { return GetCurrentThreadId(); }
|
||||
static bool IsNativeThreadsEqual(NativeThread a, NativeThread b) { return (a==b); }
|
||||
|
||||
bool operator ==(NativeThread b) { return (mThreadID==b); }
|
||||
|
||||
HANDLE GetThreadHandle() const { return mThreadHandle; }
|
||||
UInt32 GetThreadID() const { return mThreadID; }
|
||||
bool IsCurrentThread() const { return (0 != mThreadID) && (GetCurrentThreadId() == mThreadID); }
|
||||
bool IsRunning() const { return 0 != mThreadID; }
|
||||
#endif
|
||||
|
||||
bool IsTimeShareThread() const { return !mTimeConstraintSet; }
|
||||
bool IsTimeConstraintThread() const { return mTimeConstraintSet; }
|
||||
|
||||
UInt32 GetPriority() const { return mPriority; }
|
||||
UInt32 GetScheduledPriority();
|
||||
static UInt32 GetScheduledPriority(NativeThread thread);
|
||||
void SetPriority(UInt32 inPriority, bool inFixedPriority=false);
|
||||
static void SetPriority(NativeThread inThread, UInt32 inPriority, bool inFixedPriority = false);
|
||||
|
||||
void GetTimeConstraints(UInt32& outPeriod, UInt32& outComputation, UInt32& outConstraint, bool& outIsPreemptible) const { outPeriod = mPeriod; outComputation = mComputation; outConstraint = mConstraint; outIsPreemptible = mIsPreemptible; }
|
||||
void SetTimeConstraints(UInt32 inPeriod, UInt32 inComputation, UInt32 inConstraint, bool inIsPreemptible);
|
||||
void ClearTimeConstraints() { SetPriority(mPriority); }
|
||||
|
||||
bool WillAutoDelete() const { return mAutoDelete; }
|
||||
void SetAutoDelete(bool b) { mAutoDelete = b; }
|
||||
|
||||
void SetName(const char* inThreadName);
|
||||
|
||||
#if CoreAudio_Debug
|
||||
void DebugPriority(const char *label);
|
||||
#endif
|
||||
|
||||
// Actions
|
||||
public:
|
||||
virtual void Start();
|
||||
|
||||
// Implementation
|
||||
protected:
|
||||
#if TARGET_OS_MAC
|
||||
static void* Entry(CAPThread* inCAPThread);
|
||||
#elif TARGET_OS_WIN32
|
||||
static UInt32 WINAPI Entry(CAPThread* inCAPThread);
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
pthread_t mPThread;
|
||||
UInt32 mSpawningThreadPriority;
|
||||
#elif TARGET_OS_WIN32
|
||||
HANDLE mThreadHandle;
|
||||
unsigned long mThreadID;
|
||||
#endif
|
||||
ThreadRoutine mThreadRoutine;
|
||||
void* mThreadParameter;
|
||||
char mThreadName[kMaxThreadNameLength];
|
||||
UInt32 mPriority;
|
||||
UInt32 mPeriod;
|
||||
UInt32 mComputation;
|
||||
UInt32 mConstraint;
|
||||
bool mIsPreemptible;
|
||||
bool mTimeConstraintSet;
|
||||
bool mFixedPriority;
|
||||
bool mAutoDelete; // delete self when thread terminates
|
||||
};
|
||||
|
||||
#endif
|
||||
321
driver/PublicUtility/CAPropertyAddress.h
Normal file
321
driver/PublicUtility/CAPropertyAddress.h
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
File: CAPropertyAddress.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.
|
||||
|
||||
*/
|
||||
#if !defined(__CAPropertyAddress_h__)
|
||||
#define __CAPropertyAddress_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioHardware.h>
|
||||
|
||||
// Standard Library Includes
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
//==================================================================================================
|
||||
// CAPropertyAddress
|
||||
//
|
||||
// CAPropertyAddress extends the AudioObjectPropertyAddress structure to C++ including constructors
|
||||
// and other utility operations. Note that there is no defined operator< or operator== because the
|
||||
// presence of wildcards for the fields make comparisons ambiguous without specifying whether or
|
||||
// not to take the wildcards into account. Consequently, if you want to use this struct in an STL
|
||||
// data structure, you'll need to specify the approriate function object explicitly in the template
|
||||
// declaration.
|
||||
//==================================================================================================
|
||||
|
||||
struct CAPropertyAddress
|
||||
:
|
||||
public AudioObjectPropertyAddress
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAPropertyAddress() : AudioObjectPropertyAddress() { mSelector = 0; mScope = kAudioObjectPropertyScopeGlobal; mElement = kAudioObjectPropertyElementMaster; }
|
||||
CAPropertyAddress(AudioObjectPropertySelector inSelector) : AudioObjectPropertyAddress() { mSelector = inSelector; mScope = kAudioObjectPropertyScopeGlobal; mElement = kAudioObjectPropertyElementMaster; }
|
||||
CAPropertyAddress(AudioObjectPropertySelector inSelector, AudioObjectPropertyScope inScope) : AudioObjectPropertyAddress() { mSelector = inSelector; mScope = inScope; mElement = kAudioObjectPropertyElementMaster; }
|
||||
CAPropertyAddress(AudioObjectPropertySelector inSelector, AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) : AudioObjectPropertyAddress() { mSelector = inSelector; mScope = inScope; mElement = inElement; }
|
||||
CAPropertyAddress(const AudioObjectPropertyAddress& inAddress) : AudioObjectPropertyAddress(inAddress){}
|
||||
CAPropertyAddress(const CAPropertyAddress& inAddress) : AudioObjectPropertyAddress(inAddress){}
|
||||
CAPropertyAddress& operator=(const AudioObjectPropertyAddress& inAddress) { AudioObjectPropertyAddress::operator=(inAddress); return *this; }
|
||||
CAPropertyAddress& operator=(const CAPropertyAddress& inAddress) { AudioObjectPropertyAddress::operator=(inAddress); return *this; }
|
||||
|
||||
// Operations
|
||||
public:
|
||||
static bool IsSameAddress(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) { return (inAddress1.mScope == inAddress2.mScope) && (inAddress1.mSelector == inAddress2.mSelector) && (inAddress1.mElement == inAddress2.mElement); }
|
||||
static bool IsLessThanAddress(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) { bool theAnswer = false; if(inAddress1.mScope != inAddress2.mScope) { theAnswer = inAddress1.mScope < inAddress2.mScope; } else if(inAddress1.mSelector != inAddress2.mSelector) { theAnswer = inAddress1.mSelector < inAddress2.mSelector; } else { theAnswer = inAddress1.mElement < inAddress2.mElement; } return theAnswer; }
|
||||
static bool IsCongruentSelector(AudioObjectPropertySelector inSelector1, AudioObjectPropertySelector inSelector2) { return (inSelector1 == inSelector2) || (inSelector1 == kAudioObjectPropertySelectorWildcard) || (inSelector2 == kAudioObjectPropertySelectorWildcard); }
|
||||
static bool IsCongruentScope(AudioObjectPropertyScope inScope1, AudioObjectPropertyScope inScope2) { return (inScope1 == inScope2) || (inScope1 == kAudioObjectPropertyScopeWildcard) || (inScope2 == kAudioObjectPropertyScopeWildcard); }
|
||||
static bool IsCongruentElement(AudioObjectPropertyElement inElement1, AudioObjectPropertyElement inElement2) { return (inElement1 == inElement2) || (inElement1 == kAudioObjectPropertyElementWildcard) || (inElement2 == kAudioObjectPropertyElementWildcard); }
|
||||
static bool IsCongruentAddress(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) { return IsCongruentScope(inAddress1.mScope, inAddress2.mScope) && IsCongruentSelector(inAddress1.mSelector, inAddress2.mSelector) && IsCongruentElement(inAddress1.mElement, inAddress2.mElement); }
|
||||
static bool IsCongruentLessThanAddress(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) { bool theAnswer = false; if(!IsCongruentScope(inAddress1.mScope, inAddress2.mScope)) { theAnswer = inAddress1.mScope < inAddress2.mScope; } else if(!IsCongruentSelector(inAddress1.mSelector, inAddress2.mSelector)) { theAnswer = inAddress1.mSelector < inAddress2.mSelector; } else if(!IsCongruentElement(inAddress1.mElement, inAddress2.mElement)) { theAnswer = inAddress1.mElement < inAddress2.mElement; } return theAnswer; }
|
||||
|
||||
// STL Helpers
|
||||
public:
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
struct EqualTo : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
|
||||
{
|
||||
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsSameAddress(inAddress1, inAddress2); }
|
||||
};
|
||||
|
||||
struct LessThan : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
|
||||
{
|
||||
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsLessThanAddress(inAddress1, inAddress2); }
|
||||
};
|
||||
|
||||
struct CongruentEqualTo : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
|
||||
{
|
||||
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsCongruentAddress(inAddress1, inAddress2); }
|
||||
};
|
||||
|
||||
struct CongruentLessThan : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
|
||||
{
|
||||
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsCongruentLessThanAddress(inAddress1, inAddress2); }
|
||||
};
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
// CAPropertyAddressList
|
||||
//
|
||||
// An auto-resizing array of CAPropertyAddress structures.
|
||||
//==================================================================================================
|
||||
|
||||
class CAPropertyAddressList
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAPropertyAddressList() : mAddressList(), mToken(NULL) {}
|
||||
explicit CAPropertyAddressList(void* inToken) : mAddressList(), mToken(inToken) {}
|
||||
explicit CAPropertyAddressList(uintptr_t inToken) : mAddressList(), mToken(reinterpret_cast<void*>(inToken)) {}
|
||||
CAPropertyAddressList(const CAPropertyAddressList& inAddressList) : mAddressList(inAddressList.mAddressList), mToken(inAddressList.mToken) {}
|
||||
CAPropertyAddressList& operator=(const CAPropertyAddressList& inAddressList) { mAddressList = inAddressList.mAddressList; mToken = inAddressList.mToken; return *this; }
|
||||
~CAPropertyAddressList() {}
|
||||
|
||||
// Operations
|
||||
public:
|
||||
void* GetToken() const { return mToken; }
|
||||
void SetToken(void* inToken) { mToken = inToken; }
|
||||
|
||||
uintptr_t GetIntToken() const { return reinterpret_cast<uintptr_t>(mToken); }
|
||||
void SetIntToken(uintptr_t inToken) { mToken = reinterpret_cast<void*>(inToken); }
|
||||
|
||||
AudioObjectID GetAudioObjectIDToken() const { return static_cast<AudioObjectID>(reinterpret_cast<uintptr_t>(mToken)); }
|
||||
|
||||
bool IsEmpty() const { return mAddressList.empty(); }
|
||||
UInt32 GetNumberItems() const { return ToUInt32(mAddressList.size()); }
|
||||
void GetItemByIndex(UInt32 inIndex, AudioObjectPropertyAddress& outAddress) const { if(inIndex < mAddressList.size()) { outAddress = mAddressList.at(inIndex); } }
|
||||
const AudioObjectPropertyAddress* GetItems() const { return &(*mAddressList.begin()); }
|
||||
AudioObjectPropertyAddress* GetItems() { return &(*mAddressList.begin()); }
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
bool HasItem(const AudioObjectPropertyAddress& inAddress) const { AddressList::const_iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), std::bind1st(CAPropertyAddress::CongruentEqualTo(), inAddress)); return theIterator != mAddressList.end(); }
|
||||
bool HasExactItem(const AudioObjectPropertyAddress& inAddress) const { AddressList::const_iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), std::bind1st(CAPropertyAddress::EqualTo(), inAddress)); return theIterator != mAddressList.end(); }
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
void AppendItem(const AudioObjectPropertyAddress& inAddress) { mAddressList.push_back(inAddress); }
|
||||
void AppendUniqueItem(const AudioObjectPropertyAddress& inAddress) { if(!HasItem(inAddress)) { mAddressList.push_back(inAddress); } }
|
||||
void AppendUniqueExactItem(const AudioObjectPropertyAddress& inAddress) { if(!HasExactItem(inAddress)) { mAddressList.push_back(inAddress); } }
|
||||
void InsertItemAtIndex(UInt32 inIndex, const AudioObjectPropertyAddress& inAddress) { if(inIndex < mAddressList.size()) { AddressList::iterator theIterator = mAddressList.begin(); std::advance(theIterator, static_cast<int>(inIndex)); mAddressList.insert(theIterator, inAddress); } else { mAddressList.push_back(inAddress); } }
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
void EraseExactItem(const AudioObjectPropertyAddress& inAddress) { AddressList::iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), std::bind1st(CAPropertyAddress::EqualTo(), inAddress)); if(theIterator != mAddressList.end()) { mAddressList.erase(theIterator); } }
|
||||
#pragma clang diagnostic pop
|
||||
void EraseItemAtIndex(UInt32 inIndex) { if(inIndex < mAddressList.size()) { AddressList::iterator theIterator = mAddressList.begin(); std::advance(theIterator, static_cast<int>(inIndex)); mAddressList.erase(theIterator); } }
|
||||
void EraseAllItems() { mAddressList.clear(); }
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
typedef std::vector<CAPropertyAddress> AddressList;
|
||||
|
||||
AddressList mAddressList;
|
||||
void* mToken;
|
||||
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
// CAPropertyAddressListVector
|
||||
//
|
||||
// An auto-resizing array of CAPropertyAddressList objects.
|
||||
//==================================================================================================
|
||||
|
||||
class CAPropertyAddressListVector
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAPropertyAddressListVector() : mAddressListVector() {}
|
||||
CAPropertyAddressListVector(const CAPropertyAddressListVector& inAddressListVector) : mAddressListVector(inAddressListVector.mAddressListVector) {}
|
||||
CAPropertyAddressListVector& operator=(const CAPropertyAddressListVector& inAddressListVector) { mAddressListVector = inAddressListVector.mAddressListVector; return *this; }
|
||||
~CAPropertyAddressListVector() {}
|
||||
|
||||
// Operations
|
||||
public:
|
||||
bool IsEmpty() const { return mAddressListVector.empty(); }
|
||||
bool HasAnyNonEmptyItems() const;
|
||||
bool HasAnyItemsWithAddress(const AudioObjectPropertyAddress& inAddress) const;
|
||||
bool HasAnyItemsWithExactAddress(const AudioObjectPropertyAddress& inAddress) const;
|
||||
|
||||
UInt32 GetNumberItems() const { return ToUInt32(mAddressListVector.size()); }
|
||||
const CAPropertyAddressList& GetItemByIndex(UInt32 inIndex) const { return mAddressListVector.at(inIndex); }
|
||||
CAPropertyAddressList& GetItemByIndex(UInt32 inIndex) { return mAddressListVector.at(inIndex); }
|
||||
const CAPropertyAddressList* GetItemByToken(void* inToken) const;
|
||||
CAPropertyAddressList* GetItemByToken(void* inToken);
|
||||
const CAPropertyAddressList* GetItemByIntToken(uintptr_t inToken) const;
|
||||
CAPropertyAddressList* GetItemByIntToken(uintptr_t inToken);
|
||||
|
||||
void AppendItem(const CAPropertyAddressList& inAddressList) { mAddressListVector.push_back(inAddressList); }
|
||||
void EraseAllItems() { mAddressListVector.clear(); }
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
typedef std::vector<CAPropertyAddressList> AddressListVector;
|
||||
|
||||
AddressListVector mAddressListVector;
|
||||
|
||||
};
|
||||
|
||||
inline bool CAPropertyAddressListVector::HasAnyNonEmptyItems() const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
for(AddressListVector::const_iterator theIterator = mAddressListVector.begin(); !theAnswer && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
theAnswer = !theIterator->IsEmpty();
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline bool CAPropertyAddressListVector::HasAnyItemsWithAddress(const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
for(AddressListVector::const_iterator theIterator = mAddressListVector.begin(); !theAnswer && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
theAnswer = theIterator->HasItem(inAddress);
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline bool CAPropertyAddressListVector::HasAnyItemsWithExactAddress(const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
for(AddressListVector::const_iterator theIterator = mAddressListVector.begin(); !theAnswer && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
theAnswer = theIterator->HasExactItem(inAddress);
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline const CAPropertyAddressList* CAPropertyAddressListVector::GetItemByToken(void* inToken) const
|
||||
{
|
||||
const CAPropertyAddressList* theAnswer = NULL;
|
||||
bool wasFound = false;
|
||||
for(AddressListVector::const_iterator theIterator = mAddressListVector.begin(); !wasFound && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
if(theIterator->GetToken() == inToken)
|
||||
{
|
||||
wasFound = true;
|
||||
theAnswer = &(*theIterator);
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline CAPropertyAddressList* CAPropertyAddressListVector::GetItemByToken(void* inToken)
|
||||
{
|
||||
CAPropertyAddressList* theAnswer = NULL;
|
||||
bool wasFound = false;
|
||||
for(AddressListVector::iterator theIterator = mAddressListVector.begin(); !wasFound && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
if(theIterator->GetToken() == inToken)
|
||||
{
|
||||
wasFound = true;
|
||||
theAnswer = &(*theIterator);
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline const CAPropertyAddressList* CAPropertyAddressListVector::GetItemByIntToken(uintptr_t inToken) const
|
||||
{
|
||||
const CAPropertyAddressList* theAnswer = NULL;
|
||||
bool wasFound = false;
|
||||
for(AddressListVector::const_iterator theIterator = mAddressListVector.begin(); !wasFound && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
if(theIterator->GetIntToken() == inToken)
|
||||
{
|
||||
wasFound = true;
|
||||
theAnswer = &(*theIterator);
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline CAPropertyAddressList* CAPropertyAddressListVector::GetItemByIntToken(uintptr_t inToken)
|
||||
{
|
||||
CAPropertyAddressList* theAnswer = NULL;
|
||||
bool wasFound = false;
|
||||
for(AddressListVector::iterator theIterator = mAddressListVector.begin(); !wasFound && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
if(theIterator->GetIntToken() == inToken)
|
||||
{
|
||||
wasFound = true;
|
||||
theAnswer = &(*theIterator);
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#endif
|
||||
319
driver/PublicUtility/CARingBuffer.cpp
Normal file
319
driver/PublicUtility/CARingBuffer.cpp
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
File: CARingBuffer.cpp
|
||||
Abstract: CARingBuffer.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 "CARingBuffer.h"
|
||||
#include "CABitOperations.h"
|
||||
#include "CAAutoDisposer.h"
|
||||
#include "CAAtomic.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <libkern/OSAtomic.h>
|
||||
|
||||
CARingBuffer::CARingBuffer() :
|
||||
mBuffers(NULL), mNumberChannels(0), mCapacityFrames(0), mCapacityBytes(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CARingBuffer::~CARingBuffer()
|
||||
{
|
||||
Deallocate();
|
||||
}
|
||||
|
||||
|
||||
void CARingBuffer::Allocate(int nChannels, UInt32 bytesPerFrame, UInt32 capacityFrames)
|
||||
{
|
||||
Deallocate();
|
||||
|
||||
capacityFrames = NextPowerOfTwo(capacityFrames);
|
||||
|
||||
mNumberChannels = nChannels;
|
||||
mBytesPerFrame = bytesPerFrame;
|
||||
mCapacityFrames = capacityFrames;
|
||||
mCapacityFramesMask = capacityFrames - 1;
|
||||
mCapacityBytes = bytesPerFrame * capacityFrames;
|
||||
|
||||
// put everything in one memory allocation, first the pointers, then the deinterleaved channels
|
||||
UInt32 allocSize = (mCapacityBytes + sizeof(Byte *)) * nChannels;
|
||||
Byte *p = (Byte *)CA_malloc(allocSize);
|
||||
memset(p, 0, allocSize);
|
||||
mBuffers = (Byte **)p;
|
||||
p += nChannels * sizeof(Byte *);
|
||||
for (int i = 0; i < nChannels; ++i) {
|
||||
mBuffers[i] = p;
|
||||
p += mCapacityBytes;
|
||||
}
|
||||
|
||||
for (UInt32 i = 0; i<kGeneralRingTimeBoundsQueueSize; ++i)
|
||||
{
|
||||
mTimeBoundsQueue[i].mStartTime = 0;
|
||||
mTimeBoundsQueue[i].mEndTime = 0;
|
||||
mTimeBoundsQueue[i].mUpdateCounter = 0;
|
||||
}
|
||||
mTimeBoundsQueuePtr = 0;
|
||||
}
|
||||
|
||||
void CARingBuffer::Deallocate()
|
||||
{
|
||||
if (mBuffers) {
|
||||
free(mBuffers);
|
||||
mBuffers = NULL;
|
||||
}
|
||||
mNumberChannels = 0;
|
||||
mCapacityBytes = 0;
|
||||
mCapacityFrames = 0;
|
||||
}
|
||||
|
||||
inline void ZeroRange(Byte **buffers, int nchannels, int offset, int nbytes)
|
||||
{
|
||||
while (--nchannels >= 0) {
|
||||
memset(*buffers + offset, 0, nbytes);
|
||||
++buffers;
|
||||
}
|
||||
}
|
||||
|
||||
inline void StoreABL(Byte **buffers, int destOffset, const AudioBufferList *abl, int srcOffset, int nbytes)
|
||||
{
|
||||
int nchannels = abl->mNumberBuffers;
|
||||
const AudioBuffer *src = abl->mBuffers;
|
||||
while (--nchannels >= 0) {
|
||||
if (srcOffset > (int)src->mDataByteSize) continue;
|
||||
memcpy(*buffers + destOffset, (Byte *)src->mData + srcOffset, std::min(nbytes, (int)src->mDataByteSize - srcOffset));
|
||||
++buffers;
|
||||
++src;
|
||||
}
|
||||
}
|
||||
|
||||
inline void FetchABL(AudioBufferList *abl, int destOffset, Byte **buffers, int srcOffset, int nbytes)
|
||||
{
|
||||
int nchannels = abl->mNumberBuffers;
|
||||
AudioBuffer *dest = abl->mBuffers;
|
||||
while (--nchannels >= 0) {
|
||||
if (destOffset > (int)dest->mDataByteSize) continue;
|
||||
memcpy((Byte *)dest->mData + destOffset, *buffers + srcOffset, std::min(nbytes, (int)dest->mDataByteSize - destOffset));
|
||||
++buffers;
|
||||
++dest;
|
||||
}
|
||||
}
|
||||
|
||||
inline void ZeroABL(AudioBufferList *abl, int destOffset, int nbytes)
|
||||
{
|
||||
int nBuffers = abl->mNumberBuffers;
|
||||
AudioBuffer *dest = abl->mBuffers;
|
||||
while (--nBuffers >= 0) {
|
||||
if (destOffset > (int)dest->mDataByteSize) continue;
|
||||
memset((Byte *)dest->mData + destOffset, 0, std::min(nbytes, (int)dest->mDataByteSize - destOffset));
|
||||
++dest;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CARingBufferError CARingBuffer::Store(const AudioBufferList *abl, UInt32 framesToWrite, SampleTime startWrite)
|
||||
{
|
||||
if (framesToWrite == 0)
|
||||
return kCARingBufferError_OK;
|
||||
|
||||
if (framesToWrite > mCapacityFrames)
|
||||
return kCARingBufferError_TooMuch; // too big!
|
||||
|
||||
SampleTime endWrite = startWrite + framesToWrite;
|
||||
|
||||
if (startWrite < EndTime()) {
|
||||
// going backwards, throw everything out
|
||||
SetTimeBounds(startWrite, startWrite);
|
||||
} else if (endWrite - StartTime() <= mCapacityFrames) {
|
||||
// the buffer has not yet wrapped and will not need to
|
||||
} else {
|
||||
// advance the start time past the region we are about to overwrite
|
||||
SampleTime newStart = endWrite - mCapacityFrames; // one buffer of time behind where we're writing
|
||||
SampleTime newEnd = std::max(newStart, EndTime());
|
||||
SetTimeBounds(newStart, newEnd);
|
||||
}
|
||||
|
||||
// write the new frames
|
||||
Byte **buffers = mBuffers;
|
||||
int nchannels = mNumberChannels;
|
||||
int offset0, offset1, nbytes;
|
||||
SampleTime curEnd = EndTime();
|
||||
|
||||
if (startWrite > curEnd) {
|
||||
// we are skipping some samples, so zero the range we are skipping
|
||||
offset0 = FrameOffset(curEnd);
|
||||
offset1 = FrameOffset(startWrite);
|
||||
if (offset0 < offset1)
|
||||
ZeroRange(buffers, nchannels, offset0, offset1 - offset0);
|
||||
else {
|
||||
ZeroRange(buffers, nchannels, offset0, mCapacityBytes - offset0);
|
||||
ZeroRange(buffers, nchannels, 0, offset1);
|
||||
}
|
||||
offset0 = offset1;
|
||||
} else {
|
||||
offset0 = FrameOffset(startWrite);
|
||||
}
|
||||
|
||||
offset1 = FrameOffset(endWrite);
|
||||
if (offset0 < offset1)
|
||||
StoreABL(buffers, offset0, abl, 0, offset1 - offset0);
|
||||
else {
|
||||
nbytes = mCapacityBytes - offset0;
|
||||
StoreABL(buffers, offset0, abl, 0, nbytes);
|
||||
StoreABL(buffers, 0, abl, nbytes, offset1);
|
||||
}
|
||||
|
||||
// now update the end time
|
||||
SetTimeBounds(StartTime(), endWrite);
|
||||
|
||||
return kCARingBufferError_OK; // success
|
||||
}
|
||||
|
||||
void CARingBuffer::SetTimeBounds(SampleTime startTime, SampleTime endTime)
|
||||
{
|
||||
UInt32 nextPtr = mTimeBoundsQueuePtr + 1;
|
||||
UInt32 index = nextPtr & kGeneralRingTimeBoundsQueueMask;
|
||||
|
||||
mTimeBoundsQueue[index].mStartTime = startTime;
|
||||
mTimeBoundsQueue[index].mEndTime = endTime;
|
||||
mTimeBoundsQueue[index].mUpdateCounter = nextPtr;
|
||||
CAAtomicCompareAndSwap32Barrier(mTimeBoundsQueuePtr, mTimeBoundsQueuePtr + 1, (SInt32*)&mTimeBoundsQueuePtr);
|
||||
}
|
||||
|
||||
CARingBufferError CARingBuffer::GetTimeBounds(SampleTime &startTime, SampleTime &endTime)
|
||||
{
|
||||
for (int i=0; i<8; ++i) // fail after a few tries.
|
||||
{
|
||||
UInt32 curPtr = mTimeBoundsQueuePtr;
|
||||
UInt32 index = curPtr & kGeneralRingTimeBoundsQueueMask;
|
||||
CARingBuffer::TimeBounds* bounds = mTimeBoundsQueue + index;
|
||||
|
||||
startTime = bounds->mStartTime;
|
||||
endTime = bounds->mEndTime;
|
||||
UInt32 newPtr = bounds->mUpdateCounter;
|
||||
|
||||
if (newPtr == curPtr)
|
||||
return kCARingBufferError_OK;
|
||||
}
|
||||
return kCARingBufferError_CPUOverload;
|
||||
}
|
||||
|
||||
CARingBufferError CARingBuffer::ClipTimeBounds(SampleTime& startRead, SampleTime& endRead)
|
||||
{
|
||||
SampleTime startTime, endTime;
|
||||
|
||||
CARingBufferError err = GetTimeBounds(startTime, endTime);
|
||||
if (err) return err;
|
||||
|
||||
if (startRead > endTime || endRead < startTime) {
|
||||
endRead = startRead;
|
||||
return kCARingBufferError_OK;
|
||||
}
|
||||
|
||||
startRead = std::max(startRead, startTime);
|
||||
endRead = std::min(endRead, endTime);
|
||||
endRead = std::max(endRead, startRead);
|
||||
|
||||
return kCARingBufferError_OK; // success
|
||||
}
|
||||
|
||||
CARingBufferError CARingBuffer::Fetch(AudioBufferList *abl, UInt32 nFrames, SampleTime startRead)
|
||||
{
|
||||
if (nFrames == 0)
|
||||
return kCARingBufferError_OK;
|
||||
|
||||
startRead = std::max(0LL, startRead);
|
||||
|
||||
SampleTime endRead = startRead + nFrames;
|
||||
|
||||
SampleTime startRead0 = startRead;
|
||||
SampleTime endRead0 = endRead;
|
||||
|
||||
CARingBufferError err = ClipTimeBounds(startRead, endRead);
|
||||
if (err) return err;
|
||||
|
||||
if (startRead == endRead) {
|
||||
ZeroABL(abl, 0, nFrames * mBytesPerFrame);
|
||||
return kCARingBufferError_OK;
|
||||
}
|
||||
|
||||
SInt32 byteSize = (SInt32)((endRead - startRead) * mBytesPerFrame);
|
||||
|
||||
SInt32 destStartByteOffset = std::max((SInt32)0, (SInt32)((startRead - startRead0) * mBytesPerFrame));
|
||||
|
||||
if (destStartByteOffset > 0) {
|
||||
ZeroABL(abl, 0, std::min((SInt32)(nFrames * mBytesPerFrame), destStartByteOffset));
|
||||
}
|
||||
|
||||
SInt32 destEndSize = std::max((SInt32)0, (SInt32)(endRead0 - endRead));
|
||||
if (destEndSize > 0) {
|
||||
ZeroABL(abl, destStartByteOffset + byteSize, destEndSize * mBytesPerFrame);
|
||||
}
|
||||
|
||||
Byte **buffers = mBuffers;
|
||||
int offset0 = FrameOffset(startRead);
|
||||
int offset1 = FrameOffset(endRead);
|
||||
int nbytes;
|
||||
|
||||
if (offset0 < offset1) {
|
||||
nbytes = offset1 - offset0;
|
||||
FetchABL(abl, destStartByteOffset, buffers, offset0, nbytes);
|
||||
} else {
|
||||
nbytes = mCapacityBytes - offset0;
|
||||
FetchABL(abl, destStartByteOffset, buffers, offset0, nbytes);
|
||||
FetchABL(abl, destStartByteOffset + nbytes, buffers, 0, offset1);
|
||||
nbytes += offset1;
|
||||
}
|
||||
|
||||
int nchannels = abl->mNumberBuffers;
|
||||
AudioBuffer *dest = abl->mBuffers;
|
||||
while (--nchannels >= 0)
|
||||
{
|
||||
dest->mDataByteSize = nbytes;
|
||||
dest++;
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
126
driver/PublicUtility/CARingBuffer.h
Normal file
126
driver/PublicUtility/CARingBuffer.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
File: CARingBuffer.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.
|
||||
|
||||
*/
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef CARingBuffer_Header
|
||||
#define CARingBuffer_Header
|
||||
|
||||
enum {
|
||||
kCARingBufferError_OK = 0,
|
||||
kCARingBufferError_TooMuch = 3, // fetch start time is earlier than buffer start time and fetch end time is later than buffer end time
|
||||
kCARingBufferError_CPUOverload = 4 // the reader is unable to get enough CPU cycles to capture a consistent snapshot of the time bounds
|
||||
};
|
||||
|
||||
typedef SInt32 CARingBufferError;
|
||||
|
||||
const UInt32 kGeneralRingTimeBoundsQueueSize = 32;
|
||||
const UInt32 kGeneralRingTimeBoundsQueueMask = kGeneralRingTimeBoundsQueueSize - 1;
|
||||
|
||||
class CARingBuffer {
|
||||
public:
|
||||
typedef SInt64 SampleTime;
|
||||
|
||||
CARingBuffer();
|
||||
~CARingBuffer();
|
||||
|
||||
void Allocate(int nChannels, UInt32 bytesPerFrame, UInt32 capacityFrames);
|
||||
// capacityFrames will be rounded up to a power of 2
|
||||
void Deallocate();
|
||||
|
||||
CARingBufferError Store(const AudioBufferList *abl, UInt32 nFrames, SampleTime frameNumber);
|
||||
// Copy nFrames of data into the ring buffer at the specified sample time.
|
||||
// The sample time should normally increase sequentially, though gaps
|
||||
// are filled with zeroes. A sufficiently large gap effectively empties
|
||||
// the buffer before storing the new data.
|
||||
|
||||
// If frameNumber is less than the previous frame number, the behavior is undefined.
|
||||
|
||||
// Return false for failure (buffer not large enough).
|
||||
|
||||
CARingBufferError Fetch(AudioBufferList *abl, UInt32 nFrames, SampleTime frameNumber);
|
||||
// will alter mDataByteSize of the buffers
|
||||
|
||||
CARingBufferError GetTimeBounds(SampleTime &startTime, SampleTime &endTime);
|
||||
|
||||
protected:
|
||||
|
||||
UInt32 FrameOffset(SampleTime frameNumber) { return (frameNumber & mCapacityFramesMask) * mBytesPerFrame; }
|
||||
|
||||
CARingBufferError ClipTimeBounds(SampleTime& startRead, SampleTime& endRead);
|
||||
|
||||
// these should only be called from Store.
|
||||
SampleTime StartTime() const { return mTimeBoundsQueue[mTimeBoundsQueuePtr & kGeneralRingTimeBoundsQueueMask].mStartTime; }
|
||||
SampleTime EndTime() const { return mTimeBoundsQueue[mTimeBoundsQueuePtr & kGeneralRingTimeBoundsQueueMask].mEndTime; }
|
||||
void SetTimeBounds(SampleTime startTime, SampleTime endTime);
|
||||
|
||||
protected:
|
||||
Byte ** mBuffers; // allocated in one chunk of memory
|
||||
int mNumberChannels;
|
||||
UInt32 mBytesPerFrame; // within one deinterleaved channel
|
||||
UInt32 mCapacityFrames; // per channel, must be a power of 2
|
||||
UInt32 mCapacityFramesMask;
|
||||
UInt32 mCapacityBytes; // per channel
|
||||
|
||||
// range of valid sample time in the buffer
|
||||
typedef struct {
|
||||
volatile SampleTime mStartTime;
|
||||
volatile SampleTime mEndTime;
|
||||
volatile UInt32 mUpdateCounter;
|
||||
} TimeBounds;
|
||||
|
||||
CARingBuffer::TimeBounds mTimeBoundsQueue[kGeneralRingTimeBoundsQueueSize];
|
||||
UInt32 mTimeBoundsQueuePtr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
482
driver/PublicUtility/CAVolumeCurve.cpp
Normal file
482
driver/PublicUtility/CAVolumeCurve.cpp
Normal file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
File: CAVolumeCurve.cpp
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#include "CAVolumeCurve.h"
|
||||
#include "CADebugMacros.h"
|
||||
#include <math.h>
|
||||
|
||||
//=============================================================================
|
||||
// CAVolumeCurve
|
||||
//=============================================================================
|
||||
|
||||
CAVolumeCurve::CAVolumeCurve()
|
||||
:
|
||||
mTag(0),
|
||||
mCurveMap(),
|
||||
mIsApplyingTransferFunction(true),
|
||||
mTransferFunction(kPow2Over1Curve),
|
||||
mRawToScalarExponentNumerator(2.0f),
|
||||
mRawToScalarExponentDenominator(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
CAVolumeCurve::~CAVolumeCurve()
|
||||
{
|
||||
}
|
||||
|
||||
SInt32 CAVolumeCurve::GetMinimumRaw() const
|
||||
{
|
||||
SInt32 theAnswer = 0;
|
||||
|
||||
if(!mCurveMap.empty())
|
||||
{
|
||||
CurveMap::const_iterator theIterator = mCurveMap.begin();
|
||||
theAnswer = theIterator->first.mMinimum;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
SInt32 CAVolumeCurve::GetMaximumRaw() const
|
||||
{
|
||||
SInt32 theAnswer = 0;
|
||||
|
||||
if(!mCurveMap.empty())
|
||||
{
|
||||
CurveMap::const_iterator theIterator = mCurveMap.begin();
|
||||
std::advance(theIterator, static_cast<int>(mCurveMap.size() - 1));
|
||||
theAnswer = theIterator->first.mMaximum;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
Float32 CAVolumeCurve::GetMinimumDB() const
|
||||
{
|
||||
Float32 theAnswer = 0;
|
||||
|
||||
if(!mCurveMap.empty())
|
||||
{
|
||||
CurveMap::const_iterator theIterator = mCurveMap.begin();
|
||||
theAnswer = theIterator->second.mMinimum;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
Float32 CAVolumeCurve::GetMaximumDB() const
|
||||
{
|
||||
Float32 theAnswer = 0;
|
||||
|
||||
if(!mCurveMap.empty())
|
||||
{
|
||||
CurveMap::const_iterator theIterator = mCurveMap.begin();
|
||||
std::advance(theIterator, static_cast<int>(mCurveMap.size() - 1));
|
||||
theAnswer = theIterator->second.mMaximum;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CAVolumeCurve::SetTransferFunction(UInt32 inTransferFunction)
|
||||
{
|
||||
mTransferFunction = inTransferFunction;
|
||||
|
||||
// figure out the co-efficients
|
||||
switch(inTransferFunction)
|
||||
{
|
||||
case kLinearCurve:
|
||||
mIsApplyingTransferFunction = false;
|
||||
mRawToScalarExponentNumerator = 1.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
case kPow1Over3Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 1.0f;
|
||||
mRawToScalarExponentDenominator = 3.0f;
|
||||
break;
|
||||
|
||||
case kPow1Over2Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 1.0f;
|
||||
mRawToScalarExponentDenominator = 2.0f;
|
||||
break;
|
||||
|
||||
case kPow3Over4Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 3.0f;
|
||||
mRawToScalarExponentDenominator = 4.0f;
|
||||
break;
|
||||
|
||||
case kPow3Over2Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 3.0f;
|
||||
mRawToScalarExponentDenominator = 2.0f;
|
||||
break;
|
||||
|
||||
case kPow2Over1Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 2.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
case kPow3Over1Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 3.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
case kPow4Over1Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 4.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
case kPow5Over1Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 5.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
case kPow6Over1Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 6.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
case kPow7Over1Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 7.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
case kPow8Over1Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 8.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
case kPow9Over1Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 9.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
case kPow10Over1Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 10.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
case kPow11Over1Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 11.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
case kPow12Over1Curve:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 12.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
|
||||
default:
|
||||
mIsApplyingTransferFunction = true;
|
||||
mRawToScalarExponentNumerator = 2.0f;
|
||||
mRawToScalarExponentDenominator = 1.0f;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void CAVolumeCurve::AddRange(SInt32 inMinRaw, SInt32 inMaxRaw, Float32 inMinDB, Float32 inMaxDB)
|
||||
{
|
||||
CARawPoint theRaw(inMinRaw, inMaxRaw);
|
||||
CADBPoint theDB(inMinDB, inMaxDB);
|
||||
|
||||
bool isOverlapped = false;
|
||||
bool isDone = false;
|
||||
CurveMap::iterator theIterator = mCurveMap.begin();
|
||||
while((theIterator != mCurveMap.end()) && !isOverlapped && !isDone)
|
||||
{
|
||||
isOverlapped = CARawPoint::Overlap(theRaw, theIterator->first);
|
||||
isDone = theRaw >= theIterator->first;
|
||||
|
||||
if(!isOverlapped && !isDone)
|
||||
{
|
||||
std::advance(theIterator, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(!isOverlapped)
|
||||
{
|
||||
mCurveMap.insert(CurveMap::value_type(theRaw, theDB));
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugMessage("CAVolumeCurve::AddRange: new point overlaps");
|
||||
}
|
||||
}
|
||||
|
||||
void CAVolumeCurve::ResetRange()
|
||||
{
|
||||
mCurveMap.clear();
|
||||
}
|
||||
|
||||
bool CAVolumeCurve::CheckForContinuity() const
|
||||
{
|
||||
bool theAnswer = true;
|
||||
|
||||
CurveMap::const_iterator theIterator = mCurveMap.begin();
|
||||
if(theIterator != mCurveMap.end())
|
||||
{
|
||||
SInt32 theRaw = theIterator->first.mMinimum;
|
||||
Float32 theDB = theIterator->second.mMinimum;
|
||||
do
|
||||
{
|
||||
SInt32 theRawMin = theIterator->first.mMinimum;
|
||||
SInt32 theRawMax = theIterator->first.mMaximum;
|
||||
SInt32 theRawRange = theRawMax - theRawMin;
|
||||
|
||||
Float32 theDBMin = theIterator->second.mMinimum;
|
||||
Float32 theDBMax = theIterator->second.mMaximum;
|
||||
Float32 theDBRange = theDBMax - theDBMin;
|
||||
|
||||
theAnswer = theRaw == theRawMin;
|
||||
theAnswer = theAnswer && (theDB == theDBMin);
|
||||
|
||||
theRaw += theRawRange;
|
||||
theDB += theDBRange;
|
||||
|
||||
std::advance(theIterator, 1);
|
||||
}
|
||||
while((theIterator != mCurveMap.end()) && theAnswer);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
SInt32 CAVolumeCurve::ConvertDBToRaw(Float32 inDB) const
|
||||
{
|
||||
// clamp the value to the dB range
|
||||
Float32 theOverallDBMin = GetMinimumDB();
|
||||
Float32 theOverallDBMax = GetMaximumDB();
|
||||
|
||||
if(inDB < theOverallDBMin) inDB = theOverallDBMin;
|
||||
if(inDB > theOverallDBMax) inDB = theOverallDBMax;
|
||||
|
||||
// get the first entry in the curve map;
|
||||
CurveMap::const_iterator theIterator = mCurveMap.begin();
|
||||
|
||||
// initialize the answer to the minimum raw of the first item in the curve map
|
||||
SInt32 theAnswer = theIterator->first.mMinimum;
|
||||
|
||||
// iterate through the curve map until we run out of dB
|
||||
bool isDone = false;
|
||||
while(!isDone && (theIterator != mCurveMap.end()))
|
||||
{
|
||||
SInt32 theRawMin = theIterator->first.mMinimum;
|
||||
SInt32 theRawMax = theIterator->first.mMaximum;
|
||||
SInt32 theRawRange = theRawMax - theRawMin;
|
||||
|
||||
Float32 theDBMin = theIterator->second.mMinimum;
|
||||
Float32 theDBMax = theIterator->second.mMaximum;
|
||||
Float32 theDBRange = theDBMax - theDBMin;
|
||||
|
||||
Float32 theDBPerRaw = theDBRange / static_cast<Float32>(theRawRange);
|
||||
|
||||
// figure out how many steps we are into this entry in the curve map
|
||||
if(inDB > theDBMax)
|
||||
{
|
||||
// we're past the end of this one, so add in the whole range for this entry
|
||||
theAnswer += theRawRange;
|
||||
}
|
||||
else
|
||||
{
|
||||
// it's somewhere within the current entry
|
||||
// figure out how many steps it is
|
||||
Float32 theNumberRawSteps = inDB - theDBMin;
|
||||
theNumberRawSteps /= theDBPerRaw;
|
||||
|
||||
// only move in whole steps
|
||||
theNumberRawSteps = roundf(theNumberRawSteps);
|
||||
|
||||
// add this many steps to the answer
|
||||
theAnswer += static_cast<SInt32>(theNumberRawSteps);
|
||||
|
||||
// mark that we are done
|
||||
isDone = true;
|
||||
}
|
||||
|
||||
// go to the next entry in the curve map
|
||||
std::advance(theIterator, 1);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
Float32 CAVolumeCurve::ConvertRawToDB(SInt32 inRaw) const
|
||||
{
|
||||
Float32 theAnswer = 0;
|
||||
|
||||
// clamp the raw value
|
||||
SInt32 theOverallRawMin = GetMinimumRaw();
|
||||
SInt32 theOverallRawMax = GetMaximumRaw();
|
||||
|
||||
if(inRaw < theOverallRawMin) inRaw = theOverallRawMin;
|
||||
if(inRaw > theOverallRawMax) inRaw = theOverallRawMax;
|
||||
|
||||
// figure out how many raw steps need to be taken from the first one
|
||||
SInt32 theNumberRawSteps = inRaw - theOverallRawMin;
|
||||
|
||||
// get the first item in the curve map
|
||||
CurveMap::const_iterator theIterator = mCurveMap.begin();
|
||||
|
||||
// initialize the answer to the minimum dB of the first item in the curve map
|
||||
theAnswer = theIterator->second.mMinimum;
|
||||
|
||||
// iterate through the curve map until we run out of steps
|
||||
while((theNumberRawSteps > 0) && (theIterator != mCurveMap.end()))
|
||||
{
|
||||
// compute some values
|
||||
SInt32 theRawMin = theIterator->first.mMinimum;
|
||||
SInt32 theRawMax = theIterator->first.mMaximum;
|
||||
SInt32 theRawRange = theRawMax - theRawMin;
|
||||
|
||||
Float32 theDBMin = theIterator->second.mMinimum;
|
||||
Float32 theDBMax = theIterator->second.mMaximum;
|
||||
Float32 theDBRange = theDBMax - theDBMin;
|
||||
|
||||
Float32 theDBPerRaw = theDBRange / static_cast<Float32>(theRawRange);
|
||||
|
||||
// there might be more steps than the current map entry accounts for
|
||||
SInt32 theRawStepsToAdd = std::min(theRawRange, theNumberRawSteps);
|
||||
|
||||
// add this many steps worth of db to the answer;
|
||||
theAnswer += theRawStepsToAdd * theDBPerRaw;
|
||||
|
||||
// figure out how many steps are left
|
||||
theNumberRawSteps -= theRawStepsToAdd;
|
||||
|
||||
// go to the next map entry
|
||||
std::advance(theIterator, 1);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
Float32 CAVolumeCurve::ConvertRawToScalar(SInt32 inRaw) const
|
||||
{
|
||||
// get some important values
|
||||
Float32 theDBMin = GetMinimumDB();
|
||||
Float32 theDBMax = GetMaximumDB();
|
||||
Float32 theDBRange = theDBMax - theDBMin;
|
||||
SInt32 theRawMin = GetMinimumRaw();
|
||||
SInt32 theRawMax = GetMaximumRaw();
|
||||
SInt32 theRawRange = theRawMax - theRawMin;
|
||||
|
||||
// range the raw value
|
||||
if(inRaw < theRawMin) inRaw = theRawMin;
|
||||
if(inRaw > theRawMax) inRaw = theRawMax;
|
||||
|
||||
// calculate the distance in the range inRaw is
|
||||
Float32 theAnswer = static_cast<Float32>(inRaw - theRawMin) / static_cast<Float32>(theRawRange);
|
||||
|
||||
// only apply a curve to the scalar values if the dB range is greater than 30
|
||||
if(mIsApplyingTransferFunction && (theDBRange > 30.0f))
|
||||
{
|
||||
theAnswer = powf(theAnswer, mRawToScalarExponentNumerator / mRawToScalarExponentDenominator);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
Float32 CAVolumeCurve::ConvertDBToScalar(Float32 inDB) const
|
||||
{
|
||||
SInt32 theRawValue = ConvertDBToRaw(inDB);
|
||||
Float32 theAnswer = ConvertRawToScalar(theRawValue);
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
SInt32 CAVolumeCurve::ConvertScalarToRaw(Float32 inScalar) const
|
||||
{
|
||||
// range the scalar value
|
||||
inScalar = std::min(1.0f, std::max(0.0f, inScalar));
|
||||
|
||||
// get some important values
|
||||
Float32 theDBMin = GetMinimumDB();
|
||||
Float32 theDBMax = GetMaximumDB();
|
||||
Float32 theDBRange = theDBMax - theDBMin;
|
||||
SInt32 theRawMin = GetMinimumRaw();
|
||||
SInt32 theRawMax = GetMaximumRaw();
|
||||
SInt32 theRawRange = theRawMax - theRawMin;
|
||||
|
||||
// have to undo the curve if the dB range is greater than 30
|
||||
if(mIsApplyingTransferFunction && (theDBRange > 30.0f))
|
||||
{
|
||||
inScalar = powf(inScalar, mRawToScalarExponentDenominator / mRawToScalarExponentNumerator);
|
||||
}
|
||||
|
||||
// now we can figure out how many raw steps this is
|
||||
Float32 theNumberRawSteps = inScalar * static_cast<Float32>(theRawRange);
|
||||
theNumberRawSteps = roundf(theNumberRawSteps);
|
||||
|
||||
// the answer is the minimum raw value plus the number of raw steps
|
||||
SInt32 theAnswer = theRawMin + static_cast<SInt32>(theNumberRawSteps);
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
Float32 CAVolumeCurve::ConvertScalarToDB(Float32 inScalar) const
|
||||
{
|
||||
SInt32 theRawValue = ConvertScalarToRaw(inScalar);
|
||||
Float32 theAnswer = ConvertRawToDB(theRawValue);
|
||||
return theAnswer;
|
||||
}
|
||||
178
driver/PublicUtility/CAVolumeCurve.h
Normal file
178
driver/PublicUtility/CAVolumeCurve.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
File: CAVolumeCurve.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.0.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) 2013 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAVolumeCurve_h__)
|
||||
#define __CAVolumeCurve_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#endif
|
||||
#include <map>
|
||||
|
||||
//=============================================================================
|
||||
// Types
|
||||
//=============================================================================
|
||||
|
||||
struct CARawPoint
|
||||
{
|
||||
SInt32 mMinimum;
|
||||
SInt32 mMaximum;
|
||||
|
||||
CARawPoint() : mMinimum(0), mMaximum(0) {}
|
||||
CARawPoint(const CARawPoint& inPoint) : mMinimum(inPoint.mMinimum), mMaximum(inPoint.mMaximum) {}
|
||||
CARawPoint(SInt32 inMinimum, SInt32 inMaximum) : mMinimum(inMinimum), mMaximum(inMaximum) {}
|
||||
CARawPoint& operator=(const CARawPoint& inPoint) { mMinimum = inPoint.mMinimum; mMaximum = inPoint.mMaximum; return *this; }
|
||||
|
||||
static bool Overlap(const CARawPoint& x, const CARawPoint& y) { return (x.mMinimum < y.mMaximum) && (x.mMaximum > y.mMinimum); }
|
||||
};
|
||||
|
||||
inline bool operator<(const CARawPoint& x, const CARawPoint& y) { return x.mMinimum < y.mMinimum; }
|
||||
inline bool operator==(const CARawPoint& x, const CARawPoint& y) { return (x.mMinimum == y.mMinimum) && (x.mMaximum == y.mMaximum); }
|
||||
inline bool operator!=(const CARawPoint& x, const CARawPoint& y) { return !(x == y); }
|
||||
inline bool operator<=(const CARawPoint& x, const CARawPoint& y) { return (x < y) || (x == y); }
|
||||
inline bool operator>=(const CARawPoint& x, const CARawPoint& y) { return !(x < y); }
|
||||
inline bool operator>(const CARawPoint& x, const CARawPoint& y) { return !((x < y) || (x == y)); }
|
||||
|
||||
struct CADBPoint
|
||||
{
|
||||
Float32 mMinimum;
|
||||
Float32 mMaximum;
|
||||
|
||||
CADBPoint() : mMinimum(0), mMaximum(0) {}
|
||||
CADBPoint(const CADBPoint& inPoint) : mMinimum(inPoint.mMinimum), mMaximum(inPoint.mMaximum) {}
|
||||
CADBPoint(Float32 inMinimum, Float32 inMaximum) : mMinimum(inMinimum), mMaximum(inMaximum) {}
|
||||
CADBPoint& operator=(const CADBPoint& inPoint) { mMinimum = inPoint.mMinimum; mMaximum = inPoint.mMaximum; return *this; }
|
||||
|
||||
static bool Overlap(const CADBPoint& x, const CADBPoint& y) { return (x.mMinimum < y.mMaximum) && (x.mMaximum >= y.mMinimum); }
|
||||
};
|
||||
|
||||
inline bool operator<(const CADBPoint& x, const CADBPoint& y) { return x.mMinimum < y.mMinimum; }
|
||||
inline bool operator==(const CADBPoint& x, const CADBPoint& y) { return (x.mMinimum == y.mMinimum) && (x.mMaximum == y.mMaximum); }
|
||||
inline bool operator!=(const CADBPoint& x, const CADBPoint& y) { return !(x == y); }
|
||||
inline bool operator<=(const CADBPoint& x, const CADBPoint& y) { return (x < y) || (x == y); }
|
||||
inline bool operator>=(const CADBPoint& x, const CADBPoint& y) { return !(x < y); }
|
||||
inline bool operator>(const CADBPoint& x, const CADBPoint& y) { return !((x < y) || (x == y)); }
|
||||
|
||||
//=============================================================================
|
||||
// CAVolumeCurve
|
||||
//=============================================================================
|
||||
|
||||
class CAVolumeCurve
|
||||
{
|
||||
|
||||
// Constants
|
||||
public:
|
||||
enum
|
||||
{
|
||||
kLinearCurve = 0,
|
||||
kPow1Over3Curve = 1,
|
||||
kPow1Over2Curve = 2,
|
||||
kPow3Over4Curve = 3,
|
||||
kPow3Over2Curve = 4,
|
||||
kPow2Over1Curve = 5,
|
||||
kPow3Over1Curve = 6,
|
||||
kPow4Over1Curve = 7,
|
||||
kPow5Over1Curve = 8,
|
||||
kPow6Over1Curve = 9,
|
||||
kPow7Over1Curve = 10,
|
||||
kPow8Over1Curve = 11,
|
||||
kPow9Over1Curve = 12,
|
||||
kPow10Over1Curve = 13,
|
||||
kPow11Over1Curve = 14,
|
||||
kPow12Over1Curve = 15
|
||||
};
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAVolumeCurve();
|
||||
virtual ~CAVolumeCurve();
|
||||
|
||||
// Attributes
|
||||
public:
|
||||
UInt32 GetTag() const { return mTag; }
|
||||
void SetTag(UInt32 inTag) { mTag = inTag; }
|
||||
SInt32 GetMinimumRaw() const;
|
||||
SInt32 GetMaximumRaw() const;
|
||||
Float32 GetMinimumDB() const;
|
||||
Float32 GetMaximumDB() const;
|
||||
|
||||
void SetIsApplyingTransferFunction(bool inIsApplyingTransferFunction) { mIsApplyingTransferFunction = inIsApplyingTransferFunction; }
|
||||
UInt32 GetTransferFunction() const { return mTransferFunction; }
|
||||
void SetTransferFunction(UInt32 inTransferFunction);
|
||||
|
||||
// Operations
|
||||
public:
|
||||
void AddRange(SInt32 mMinRaw, SInt32 mMaxRaw, Float32 inMinDB, Float32 inMaxDB);
|
||||
void ResetRange();
|
||||
bool CheckForContinuity() const;
|
||||
|
||||
SInt32 ConvertDBToRaw(Float32 inDB) const;
|
||||
Float32 ConvertRawToDB(SInt32 inRaw) const;
|
||||
Float32 ConvertRawToScalar(SInt32 inRaw) const;
|
||||
Float32 ConvertDBToScalar(Float32 inDB) const;
|
||||
SInt32 ConvertScalarToRaw(Float32 inScalar) const;
|
||||
Float32 ConvertScalarToDB(Float32 inScalar) const;
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
typedef std::map<CARawPoint, CADBPoint> CurveMap;
|
||||
|
||||
UInt32 mTag;
|
||||
CurveMap mCurveMap;
|
||||
bool mIsApplyingTransferFunction;
|
||||
UInt32 mTransferFunction;
|
||||
Float32 mRawToScalarExponentNumerator;
|
||||
Float32 mRawToScalarExponentDenominator;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
410
driver/VC_AbstractDevice.cpp
Normal file
410
driver/VC_AbstractDevice.cpp
Normal file
@@ -0,0 +1,410 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_AbstractDevice.cpp
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2017 Kyle Neideck
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "VC_AbstractDevice.h"
|
||||
|
||||
// Local Includes
|
||||
#include "VC_Utils.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAException.h"
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
VC_AbstractDevice::VC_AbstractDevice(AudioObjectID inObjectID, AudioObjectID inOwnerObjectID)
|
||||
:
|
||||
VC_Object(inObjectID, kAudioDeviceClassID, kAudioObjectClassID, inOwnerObjectID)
|
||||
{
|
||||
}
|
||||
|
||||
VC_AbstractDevice::~VC_AbstractDevice()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
bool VC_AbstractDevice::HasProperty(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyName:
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
case kAudioDevicePropertyDeviceUID:
|
||||
case kAudioDevicePropertyModelUID:
|
||||
case kAudioDevicePropertyTransportType:
|
||||
case kAudioDevicePropertyRelatedDevices:
|
||||
case kAudioDevicePropertyClockDomain:
|
||||
case kAudioDevicePropertyDeviceIsAlive:
|
||||
case kAudioDevicePropertyDeviceIsRunning:
|
||||
case kAudioDevicePropertyDeviceCanBeDefaultDevice:
|
||||
case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice:
|
||||
case kAudioDevicePropertyLatency:
|
||||
case kAudioDevicePropertyStreams:
|
||||
case kAudioObjectPropertyControlList:
|
||||
case kAudioDevicePropertySafetyOffset:
|
||||
case kAudioDevicePropertyNominalSampleRate:
|
||||
case kAudioDevicePropertyAvailableNominalSampleRates:
|
||||
case kAudioDevicePropertyIsHidden:
|
||||
case kAudioDevicePropertyZeroTimeStampPeriod:
|
||||
theAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::HasProperty(inObjectID, inClientPID, inAddress);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool VC_AbstractDevice::IsPropertySettable(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyName:
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
case kAudioDevicePropertyDeviceUID:
|
||||
case kAudioDevicePropertyModelUID:
|
||||
case kAudioDevicePropertyTransportType:
|
||||
case kAudioDevicePropertyRelatedDevices:
|
||||
case kAudioDevicePropertyClockDomain:
|
||||
case kAudioDevicePropertyDeviceIsAlive:
|
||||
case kAudioDevicePropertyDeviceIsRunning:
|
||||
case kAudioDevicePropertyDeviceCanBeDefaultDevice:
|
||||
case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice:
|
||||
case kAudioDevicePropertyLatency:
|
||||
case kAudioDevicePropertyStreams:
|
||||
case kAudioObjectPropertyControlList:
|
||||
case kAudioDevicePropertySafetyOffset:
|
||||
case kAudioDevicePropertyNominalSampleRate:
|
||||
case kAudioDevicePropertyAvailableNominalSampleRates:
|
||||
case kAudioDevicePropertyIsHidden:
|
||||
case kAudioDevicePropertyZeroTimeStampPeriod:
|
||||
theAnswer = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::IsPropertySettable(inObjectID, inClientPID, inAddress);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
UInt32 VC_AbstractDevice::GetPropertyDataSize(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* __nullable inQualifierData) const
|
||||
{
|
||||
UInt32 theAnswer = 0;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyName:
|
||||
theAnswer = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
theAnswer = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyDeviceUID:
|
||||
theAnswer = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyModelUID:
|
||||
theAnswer = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyTransportType:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyRelatedDevices:
|
||||
theAnswer = sizeof(AudioObjectID);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyClockDomain:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyDeviceCanBeDefaultDevice:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyDeviceIsAlive:
|
||||
theAnswer = sizeof(AudioClassID);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyDeviceIsRunning:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyLatency:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyStreams:
|
||||
theAnswer = 0;
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyControlList:
|
||||
theAnswer = 0;
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertySafetyOffset:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyNominalSampleRate:
|
||||
theAnswer = sizeof(Float64);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyAvailableNominalSampleRates:
|
||||
theAnswer = 0;
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyIsHidden:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyZeroTimeStampPeriod:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::GetPropertyDataSize(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void VC_AbstractDevice::GetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* __nullable inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
UInt32& outDataSize,
|
||||
void* outData) const
|
||||
{
|
||||
UInt32 theNumberItemsToFetch;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyName:
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
case kAudioDevicePropertyDeviceUID:
|
||||
case kAudioDevicePropertyModelUID:
|
||||
case kAudioDevicePropertyDeviceIsRunning:
|
||||
case kAudioDevicePropertyZeroTimeStampPeriod:
|
||||
case kAudioDevicePropertyNominalSampleRate:
|
||||
case kAudioDevicePropertyAvailableNominalSampleRates:
|
||||
// Should be unreachable. Reaching this point would mean a concrete device has delegated
|
||||
// a required property that can't be handled by this class or its parent, VC_Object.
|
||||
//
|
||||
// See VC_Device for info about these properties.
|
||||
//
|
||||
// TODO: Write a test that checks all required properties for each subclass.
|
||||
VCAssert(false,
|
||||
"VC_AbstractDevice::GetPropertyData: Property %u not handled in subclass",
|
||||
inAddress.mSelector);
|
||||
// Throw in release builds.
|
||||
Throw(CAException(kAudioHardwareIllegalOperationError));
|
||||
|
||||
case kAudioDevicePropertyTransportType:
|
||||
// This value represents how the device is attached to the system. This can be
|
||||
// any 32 bit integer, but common values for this property are defined in
|
||||
// <CoreAudio/AudioHardwareBase.h>.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_AbstractDevice::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioDevicePropertyTransportType for the device");
|
||||
// Default to virtual device.
|
||||
*reinterpret_cast<UInt32*>(outData) = kAudioDeviceTransportTypeVirtual;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyRelatedDevices:
|
||||
// The related devices property identifies device objects that are very closely
|
||||
// related. Generally, this is for relating devices that are packaged together
|
||||
// in the hardware such as when the input side and the output side of a piece
|
||||
// of hardware can be clocked separately and therefore need to be represented
|
||||
// as separate AudioDevice objects. In such case, both devices would report
|
||||
// that they are related to each other. Note that at minimum, a device is
|
||||
// related to itself, so this list will always be at least one item long.
|
||||
|
||||
// Calculate the number of items that have been requested. Note that this
|
||||
// number is allowed to be smaller than the actual size of the list. In such
|
||||
// case, only that number of items will be returned
|
||||
theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID);
|
||||
|
||||
// Default to only have the one device.
|
||||
if(theNumberItemsToFetch > 1)
|
||||
{
|
||||
theNumberItemsToFetch = 1;
|
||||
}
|
||||
|
||||
// Write the devices' object IDs into the return value.
|
||||
if(theNumberItemsToFetch > 0)
|
||||
{
|
||||
reinterpret_cast<AudioObjectID*>(outData)[0] = GetObjectID();
|
||||
}
|
||||
|
||||
// Report how much we wrote.
|
||||
outDataSize = theNumberItemsToFetch * sizeof(AudioObjectID);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyClockDomain:
|
||||
// This property allows the device to declare what other devices it is
|
||||
// synchronized with in hardware. The way it works is that if two devices have
|
||||
// the same value for this property and the value is not zero, then the two
|
||||
// devices are synchronized in hardware. Note that a device that either can't
|
||||
// be synchronized with others or doesn't know should return 0 for this
|
||||
// property.
|
||||
//
|
||||
// Default to 0.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_AbstractDevice::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioDevicePropertyClockDomain for the device");
|
||||
*reinterpret_cast<UInt32*>(outData) = 0;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyDeviceIsAlive:
|
||||
// Default to alive.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_AbstractDevice::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioDevicePropertyDeviceIsAlive for the device");
|
||||
*reinterpret_cast<UInt32*>(outData) = 1;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyDeviceCanBeDefaultDevice:
|
||||
// This property returns whether or not the device wants to be able to be the
|
||||
// default device for content. This is the device that iTunes and QuickTime
|
||||
// will use to play their content on and FaceTime will use as it's microphone.
|
||||
// Nearly all devices should allow for this.
|
||||
//
|
||||
// Default to true.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_AbstractDevice::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioDevicePropertyDeviceCanBeDefaultDevice for the device");
|
||||
*reinterpret_cast<UInt32*>(outData) = 1;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice:
|
||||
// This property returns whether or not the device wants to be the system
|
||||
// default device. This is the device that is used to play interface sounds and
|
||||
// other incidental or UI-related sounds on. Most devices should allow this
|
||||
// although devices with lots of latency may not want to.
|
||||
//
|
||||
// Default to true.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_AbstractDevice::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioDevicePropertyDeviceCanBeDefaultSystemDevice for the device");
|
||||
*reinterpret_cast<UInt32*>(outData) = 1;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyLatency:
|
||||
// This property returns the presentation latency of the device. Default to 0.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_AbstractDevice::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioDevicePropertyLatency for the device");
|
||||
*reinterpret_cast<UInt32*>(outData) = 0;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyStreams:
|
||||
// Default to not having any streams.
|
||||
outDataSize = 0;
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyControlList:
|
||||
// Default to not having any controls.
|
||||
outDataSize = 0;
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertySafetyOffset:
|
||||
// This property returns the how close to now the HAL can read and write. Default to 0.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_AbstractDevice::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioDevicePropertySafetyOffset for the device");
|
||||
*reinterpret_cast<UInt32*>(outData) = 0;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyIsHidden:
|
||||
// This returns whether or not the device is visible to clients. Default to not hidden.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_AbstractDevice::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioDevicePropertyIsHidden for the device");
|
||||
*reinterpret_cast<UInt32*>(outData) = 0;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
default:
|
||||
VC_Object::GetPropertyData(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData,
|
||||
inDataSize,
|
||||
outDataSize,
|
||||
outData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
111
driver/VC_AbstractDevice.h
Normal file
111
driver/VC_AbstractDevice.h
Normal file
@@ -0,0 +1,111 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_AbstractDevice.h
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2017 Kyle Neideck
|
||||
//
|
||||
|
||||
#ifndef VC_Driver__VC_AbstractDevice
|
||||
#define VC_Driver__VC_AbstractDevice
|
||||
|
||||
// SuperClass Includes
|
||||
#include "VC_Object.h"
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
class VC_AbstractDevice
|
||||
:
|
||||
public VC_Object
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
protected:
|
||||
VC_AbstractDevice(AudioObjectID inObjectID,
|
||||
AudioObjectID inOwnerObjectID);
|
||||
virtual ~VC_AbstractDevice();
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
public:
|
||||
virtual bool HasProperty(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual bool IsPropertySettable(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* __nullable inQualifierData) const;
|
||||
virtual void GetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* __nullable inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
UInt32& outDataSize,
|
||||
void* outData) const;
|
||||
|
||||
#pragma mark IO Operations
|
||||
|
||||
public:
|
||||
virtual void StartIO(UInt32 inClientID) = 0;
|
||||
virtual void StopIO(UInt32 inClientID) = 0;
|
||||
|
||||
virtual void GetZeroTimeStamp(Float64& outSampleTime,
|
||||
UInt64& outHostTime,
|
||||
UInt64& outSeed) = 0;
|
||||
|
||||
virtual void WillDoIOOperation(UInt32 inOperationID,
|
||||
bool& outWillDo,
|
||||
bool& outWillDoInPlace) const = 0;
|
||||
virtual void BeginIOOperation(UInt32 inOperationID,
|
||||
UInt32 inIOBufferFrameSize,
|
||||
const AudioServerPlugInIOCycleInfo& inIOCycleInfo,
|
||||
UInt32 inClientID) = 0;
|
||||
virtual void DoIOOperation(AudioObjectID inStreamObjectID,
|
||||
UInt32 inClientID, UInt32 inOperationID,
|
||||
UInt32 inIOBufferFrameSize,
|
||||
const AudioServerPlugInIOCycleInfo& inIOCycleInfo,
|
||||
void* ioMainBuffer,
|
||||
void* __nullable ioSecondaryBuffer) = 0;
|
||||
virtual void EndIOOperation(UInt32 inOperationID,
|
||||
UInt32 inIOBufferFrameSize,
|
||||
const AudioServerPlugInIOCycleInfo& inIOCycleInfo,
|
||||
UInt32 inClientID) = 0;
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
public:
|
||||
virtual CFStringRef CopyDeviceUID() const = 0;
|
||||
virtual void AddClient(const AudioServerPlugInClientInfo* inClientInfo) = 0;
|
||||
virtual void RemoveClient(const AudioServerPlugInClientInfo* inClientInfo) = 0;
|
||||
virtual void PerformConfigChange(UInt64 inChangeAction,
|
||||
void* __nullable inChangeInfo) = 0;
|
||||
virtual void AbortConfigChange(UInt64 inChangeAction,
|
||||
void* __nullable inChangeInfo) = 0;
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* VC_Driver__VC_AbstractDevice */
|
||||
|
||||
182
driver/VC_Control.cpp
Normal file
182
driver/VC_Control.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_Control.cpp
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2017 Kyle Neideck
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "VC_Control.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioHardwareBase.h>
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
VC_Control::VC_Control(AudioObjectID inObjectID,
|
||||
AudioClassID inClassID,
|
||||
AudioClassID inBaseClassID,
|
||||
AudioObjectID inOwnerObjectID,
|
||||
AudioObjectPropertyScope inScope,
|
||||
AudioObjectPropertyElement inElement)
|
||||
:
|
||||
VC_Object(inObjectID, inClassID, inBaseClassID, inOwnerObjectID),
|
||||
mScope(inScope),
|
||||
mElement(inElement)
|
||||
{
|
||||
}
|
||||
|
||||
bool VC_Control::HasProperty(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
bool theAnswer = false;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioControlPropertyScope:
|
||||
case kAudioControlPropertyElement:
|
||||
theAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::HasProperty(inObjectID, inClientPID, inAddress);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool VC_Control::IsPropertySettable(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
bool theAnswer = false;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioControlPropertyScope:
|
||||
case kAudioControlPropertyElement:
|
||||
theAnswer = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::IsPropertySettable(inObjectID, inClientPID, inAddress);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
UInt32 VC_Control::GetPropertyDataSize(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
UInt32 theAnswer = 0;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioControlPropertyScope:
|
||||
theAnswer = sizeof(AudioObjectPropertyScope);
|
||||
break;
|
||||
|
||||
case kAudioControlPropertyElement:
|
||||
theAnswer = sizeof(AudioObjectPropertyElement);
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::GetPropertyDataSize(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void VC_Control::GetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
UInt32& outDataSize,
|
||||
void* outData) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioControlPropertyScope:
|
||||
// This property returns the scope that the control is attached to.
|
||||
ThrowIf(inDataSize < sizeof(AudioObjectPropertyScope),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Control::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioControlPropertyScope for the control");
|
||||
*reinterpret_cast<AudioObjectPropertyScope*>(outData) = mScope;
|
||||
outDataSize = sizeof(AudioObjectPropertyScope);
|
||||
break;
|
||||
|
||||
case kAudioControlPropertyElement:
|
||||
// This property returns the element that the control is attached to.
|
||||
ThrowIf(inDataSize < sizeof(AudioObjectPropertyElement),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Control::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioControlPropertyElement for the control");
|
||||
*reinterpret_cast<AudioObjectPropertyElement*>(outData) = mElement;
|
||||
outDataSize = sizeof(AudioObjectPropertyElement);
|
||||
break;
|
||||
|
||||
default:
|
||||
VC_Object::GetPropertyData(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData,
|
||||
inDataSize,
|
||||
outDataSize,
|
||||
outData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void VC_Control::CheckObjectID(AudioObjectID inObjectID) const
|
||||
{
|
||||
ThrowIf(inObjectID == kAudioObjectUnknown || inObjectID != GetObjectID(),
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_Control::CheckObjectID: wrong audio object ID for the control");
|
||||
}
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
87
driver/VC_Control.h
Normal file
87
driver/VC_Control.h
Normal file
@@ -0,0 +1,87 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_Control.h
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2017 Kyle Neideck
|
||||
//
|
||||
// An AudioObject that represents a user-controllable aspect of a device or stream, such as volume
|
||||
// or balance.
|
||||
//
|
||||
|
||||
#ifndef VCDriver__VC_Control
|
||||
#define VCDriver__VC_Control
|
||||
|
||||
// Superclass Includes
|
||||
#include "VC_Object.h"
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
class VC_Control
|
||||
:
|
||||
public VC_Object
|
||||
{
|
||||
|
||||
protected:
|
||||
VC_Control(AudioObjectID inObjectID,
|
||||
AudioClassID inClassID,
|
||||
AudioClassID inBaseClassID,
|
||||
AudioObjectID inOwnerObjectID,
|
||||
AudioObjectPropertyScope inScope =
|
||||
kAudioObjectPropertyScopeOutput,
|
||||
AudioObjectPropertyElement inElement =
|
||||
kAudioObjectPropertyElementMaster);
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
public:
|
||||
virtual bool HasProperty(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual bool IsPropertySettable(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData) const;
|
||||
virtual void GetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
UInt32& outDataSize,
|
||||
void* outData) const;
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
protected:
|
||||
void CheckObjectID(AudioObjectID inObjectID) const;
|
||||
|
||||
protected:
|
||||
const AudioObjectPropertyScope mScope;
|
||||
const AudioObjectPropertyElement mElement;
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* VCDriver__VC_Control */
|
||||
|
||||
1363
driver/VC_Device.cpp
Normal file
1363
driver/VC_Device.cpp
Normal file
File diff suppressed because it is too large
Load Diff
240
driver/VC_Device.h
Normal file
240
driver/VC_Device.h
Normal file
@@ -0,0 +1,240 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_Device.h
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016, 2017, 2019 Kyle Neideck
|
||||
// Copyright © 2019 Gordon Childs
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_Device.h from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
#ifndef VCDriver__VC_Device
|
||||
#define VCDriver__VC_Device
|
||||
|
||||
// SuperClass Includes
|
||||
#include "VC_AbstractDevice.h"
|
||||
|
||||
// Local Includes
|
||||
#include "VC_Types.h"
|
||||
#include "VC_Stream.h"
|
||||
#include "VC_VolumeControl.h"
|
||||
#include "VC_MuteControl.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAMutex.h"
|
||||
#include "CAVolumeCurve.h"
|
||||
#include "CARingBuffer.h"
|
||||
|
||||
// System Includes
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <pthread.h>
|
||||
#include <atomic>
|
||||
|
||||
|
||||
class VC_Device
|
||||
:
|
||||
public VC_AbstractDevice
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
public:
|
||||
static VC_Device& GetInstance();
|
||||
|
||||
private:
|
||||
static void StaticInitializer();
|
||||
|
||||
protected:
|
||||
VC_Device(AudioObjectID inObjectID,
|
||||
const CFStringRef __nonnull inDeviceName,
|
||||
const CFStringRef __nonnull inDeviceUID,
|
||||
const CFStringRef __nonnull inDeviceModelUID,
|
||||
AudioObjectID inInputStreamID,
|
||||
AudioObjectID inOutputStreamID,
|
||||
AudioObjectID inOutputVolumeControlID,
|
||||
AudioObjectID inOutputMuteControlID);
|
||||
virtual ~VC_Device();
|
||||
|
||||
virtual void Activate();
|
||||
virtual void Deactivate();
|
||||
|
||||
private:
|
||||
void InitLoopback();
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
public:
|
||||
virtual bool HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual bool IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData) const;
|
||||
virtual void GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* __nonnull outData) const;
|
||||
virtual void SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, const void* __nonnull inData);
|
||||
|
||||
#pragma mark Device Property Operations
|
||||
|
||||
private:
|
||||
bool Device_HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
bool Device_IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
UInt32 Device_GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData) const;
|
||||
void Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* __nonnull outData) const;
|
||||
void Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, const void* __nonnull inData);
|
||||
|
||||
#pragma mark IO Operations
|
||||
|
||||
public:
|
||||
void StartIO(UInt32 inClientID);
|
||||
void StopIO(UInt32 inClientID);
|
||||
|
||||
void GetZeroTimeStamp(Float64& outSampleTime, UInt64& outHostTime, UInt64& outSeed);
|
||||
|
||||
void WillDoIOOperation(UInt32 inOperationID, bool& outWillDo, bool& outWillDoInPlace) const;
|
||||
void BeginIOOperation(UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo& inIOCycleInfo, UInt32 inClientID);
|
||||
void DoIOOperation(AudioObjectID inStreamObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo& inIOCycleInfo, void* __nonnull ioMainBuffer, void* __nullable ioSecondaryBuffer);
|
||||
void EndIOOperation(UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo& inIOCycleInfo, UInt32 inClientID);
|
||||
|
||||
private:
|
||||
void ReadInputData(UInt32 inIOBufferFrameSize, Float64 inSampleTime, void* __nonnull outBuffer);
|
||||
void WriteOutputData(UInt32 inIOBufferFrameSize, Float64 inSampleTime, const void* __nonnull inBuffer);
|
||||
|
||||
#pragma mark Accessors
|
||||
|
||||
public:
|
||||
Float64 GetSampleRate() const;
|
||||
void RequestSampleRate(Float64 inRequestedSampleRate);
|
||||
|
||||
private:
|
||||
/*!
|
||||
@return The Audio Object that has the ID inObjectID and belongs to this device.
|
||||
@throws CAException if there is no such Audio Object.
|
||||
*/
|
||||
const VC_Object& GetOwnedObjectByID(AudioObjectID inObjectID) const;
|
||||
VC_Object& GetOwnedObjectByID(AudioObjectID inObjectID);
|
||||
|
||||
/*! @return The number of Audio Objects belonging to this device, e.g. streams and controls. */
|
||||
UInt32 GetNumberOfSubObjects() const;
|
||||
/*! @return The number of Audio Objects with output scope belonging to this device. */
|
||||
UInt32 GetNumberOfOutputSubObjects() const;
|
||||
/*!
|
||||
@return The number of control Audio Objects with output scope belonging to this device, e.g.
|
||||
output volume and mute controls.
|
||||
*/
|
||||
UInt32 GetNumberOfOutputControls() const;
|
||||
/*!
|
||||
Set the device's sample rate.
|
||||
|
||||
Private because (after initialisation) this can only be called after asking the host to stop IO
|
||||
for the device. See VC_Device::PerformConfigChange and
|
||||
RequestDeviceConfigurationChange in AudioServerPlugIn.h.
|
||||
|
||||
@param inNewSampleRate The sample rate.
|
||||
@param force If true, set the sample rate on the device even if it's currently set to
|
||||
inNewSampleRate.
|
||||
@throws CAException if inNewSampleRate < 1 or if applying the sample rate to one of the streams
|
||||
fails.
|
||||
*/
|
||||
void SetSampleRate(Float64 inNewSampleRate, bool force = false);
|
||||
|
||||
/*! @return True if inObjectID is the ID of one of this device's streams. */
|
||||
inline bool IsStreamID(AudioObjectID inObjectID) const noexcept;
|
||||
|
||||
#pragma mark Hardware Accessors
|
||||
|
||||
private:
|
||||
void _HW_Open();
|
||||
void _HW_Close();
|
||||
kern_return_t _HW_StartIO();
|
||||
void _HW_StopIO();
|
||||
Float64 _HW_GetSampleRate() const;
|
||||
kern_return_t _HW_SetSampleRate(Float64 inNewSampleRate);
|
||||
UInt32 _HW_GetRingBufferFrameSize() const;
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
public:
|
||||
CFStringRef __nonnull CopyDeviceUID() const { return mDeviceUID; }
|
||||
void AddClient(const AudioServerPlugInClientInfo* __nonnull inClientInfo);
|
||||
void RemoveClient(const AudioServerPlugInClientInfo* __nonnull inClientInfo);
|
||||
/*!
|
||||
Apply a change requested with VC_PlugIn::Host_RequestDeviceConfigurationChange. See
|
||||
PerformDeviceConfigurationChange in AudioServerPlugIn.h.
|
||||
*/
|
||||
void PerformConfigChange(UInt64 inChangeAction, void* __nullable inChangeInfo);
|
||||
/*! Cancel a change requested with VC_PlugIn::Host_RequestDeviceConfigurationChange. */
|
||||
void AbortConfigChange(UInt64 inChangeAction, void* __nullable inChangeInfo);
|
||||
|
||||
private:
|
||||
static pthread_once_t sStaticInitializer;
|
||||
static VC_Device* __nonnull sInstance;
|
||||
|
||||
#define kDeviceName "Volume Control"
|
||||
#define kDeviceManufacturerName "VolumeControl"
|
||||
|
||||
const CFStringRef __nonnull mDeviceName;
|
||||
const CFStringRef __nonnull mDeviceUID;
|
||||
const CFStringRef __nonnull mDeviceModelUID;
|
||||
|
||||
bool mIsHidden;
|
||||
|
||||
enum
|
||||
{
|
||||
// The number of global/output sub-objects varies because the controls can be disabled.
|
||||
kNumberOfInputSubObjects = 1,
|
||||
|
||||
kNumberOfStreams = 2,
|
||||
kNumberOfInputStreams = 1,
|
||||
kNumberOfOutputStreams = 1
|
||||
};
|
||||
|
||||
CAMutex mStateMutex;
|
||||
CAMutex mIOMutex;
|
||||
|
||||
const Float64 kSampleRateDefault = 44100.0;
|
||||
// Before we can change sample rate, the host has to stop the device. The new sample rate is
|
||||
// stored here while it does.
|
||||
Float64 mPendingSampleRate = kSampleRateDefault;
|
||||
|
||||
#define kLoopbackRingBufferFrameSize 16384
|
||||
Float64 mLoopbackSampleRate;
|
||||
CARingBuffer mLoopbackRingBuffer;
|
||||
|
||||
// TODO: a comment explaining why we need a clock for loopback-only mode
|
||||
struct {
|
||||
Float64 hostTicksPerFrame = 0.0;
|
||||
UInt64 numberTimeStamps = 0;
|
||||
UInt64 anchorHostTime = 0;
|
||||
} mLoopbackTime;
|
||||
|
||||
VC_Stream mInputStream;
|
||||
VC_Stream mOutputStream;
|
||||
|
||||
enum class ChangeAction : UInt64
|
||||
{
|
||||
SetSampleRate
|
||||
};
|
||||
|
||||
VC_VolumeControl mVolumeControl;
|
||||
VC_MuteControl mMuteControl;
|
||||
|
||||
// Simple IO client counter to replace mClients-based tracking.
|
||||
std::atomic<UInt32> mIOClientCount{0};
|
||||
|
||||
};
|
||||
|
||||
#endif /* VCDriver__VC_Device */
|
||||
225
driver/VC_MuteControl.cpp
Normal file
225
driver/VC_MuteControl.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_MuteControl.cpp
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016, 2017 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "VC_MuteControl.h"
|
||||
|
||||
// Local Includes
|
||||
#include "VC_PlugIn.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
#include "CADispatchQueue.h"
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
VC_MuteControl::VC_MuteControl(AudioObjectID inObjectID,
|
||||
AudioObjectID inOwnerObjectID,
|
||||
AudioObjectPropertyScope inScope,
|
||||
AudioObjectPropertyElement inElement)
|
||||
:
|
||||
VC_Control(inObjectID,
|
||||
kAudioMuteControlClassID,
|
||||
kAudioBooleanControlClassID,
|
||||
inOwnerObjectID,
|
||||
inScope,
|
||||
inElement),
|
||||
mMutex("Mute Control"),
|
||||
mMuted(false)
|
||||
{
|
||||
}
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
bool VC_MuteControl::HasProperty(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
bool theAnswer = false;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioBooleanControlPropertyValue:
|
||||
theAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Control::HasProperty(inObjectID, inClientPID, inAddress);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool VC_MuteControl::IsPropertySettable(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
bool theAnswer = false;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioBooleanControlPropertyValue:
|
||||
theAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Control::IsPropertySettable(inObjectID, inClientPID, inAddress);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
UInt32 VC_MuteControl::GetPropertyDataSize(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
UInt32 theAnswer = 0;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioBooleanControlPropertyValue:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Control::GetPropertyDataSize(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void VC_MuteControl::GetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
UInt32& outDataSize,
|
||||
void* outData) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioBooleanControlPropertyValue:
|
||||
// This returns the mute value of the control.
|
||||
{
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_MuteControl::GetPropertyData: not enough space for the return value "
|
||||
"of kAudioBooleanControlPropertyValue for the mute control");
|
||||
|
||||
CAMutex::Locker theLocker(mMutex);
|
||||
|
||||
// Non-zero for true, which means audio is being muted.
|
||||
*reinterpret_cast<UInt32*>(outData) = mMuted ? 1 : 0;
|
||||
outDataSize = sizeof(UInt32);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
VC_Control::GetPropertyData(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData,
|
||||
inDataSize,
|
||||
outDataSize,
|
||||
outData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void VC_MuteControl::SetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
const void* inData)
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioBooleanControlPropertyValue:
|
||||
{
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_MuteControl::SetPropertyData: wrong size for the data for "
|
||||
"kAudioBooleanControlPropertyValue");
|
||||
|
||||
CAMutex::Locker theLocker(mMutex);
|
||||
|
||||
// Non-zero for true, meaning audio will be muted.
|
||||
bool theNewMuted = (*reinterpret_cast<const UInt32*>(inData) != 0);
|
||||
|
||||
if(mMuted != theNewMuted)
|
||||
{
|
||||
mMuted = theNewMuted;
|
||||
|
||||
// Send notifications.
|
||||
CADispatchQueue::GetGlobalSerialQueue().Dispatch(false, ^{
|
||||
AudioObjectPropertyAddress theChangedProperty[1];
|
||||
theChangedProperty[0] = {
|
||||
kAudioBooleanControlPropertyValue, mScope, mElement
|
||||
};
|
||||
|
||||
VC_PlugIn::Host_PropertiesChanged(inObjectID, 1, theChangedProperty);
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
VC_Control::SetPropertyData(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData,
|
||||
inDataSize,
|
||||
inData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
98
driver/VC_MuteControl.h
Normal file
98
driver/VC_MuteControl.h
Normal file
@@ -0,0 +1,98 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_MuteControl.h
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2017 Kyle Neideck
|
||||
//
|
||||
|
||||
#ifndef VCDriver__VC_MuteControl
|
||||
#define VCDriver__VC_MuteControl
|
||||
|
||||
// Superclass Includes
|
||||
#include "VC_Control.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAMutex.h"
|
||||
|
||||
// System Includes
|
||||
#include <MacTypes.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
class VC_MuteControl
|
||||
:
|
||||
public VC_Control
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
public:
|
||||
VC_MuteControl(AudioObjectID inObjectID,
|
||||
AudioObjectID inOwnerObjectID,
|
||||
AudioObjectPropertyScope inScope =
|
||||
kAudioObjectPropertyScopeOutput,
|
||||
AudioObjectPropertyElement inElement =
|
||||
kAudioObjectPropertyElementMaster);
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
bool HasProperty(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const;
|
||||
|
||||
bool IsPropertySettable(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const;
|
||||
|
||||
UInt32 GetPropertyDataSize(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData) const;
|
||||
|
||||
void GetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
UInt32& outDataSize,
|
||||
void* outData) const;
|
||||
|
||||
void SetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
const void* inData);
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
private:
|
||||
CAMutex mMutex;
|
||||
bool mMuted;
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* VCDriver__VC_MuteControl */
|
||||
|
||||
184
driver/VC_Object.cpp
Normal file
184
driver/VC_Object.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_Object.cpp
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_Object.cpp from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
// Similarly to VC_Object.h, this file hasn't been changed much from SA_Object.cpp, except to
|
||||
// remove the SA_ObjectMap class.
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "VC_Object.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
|
||||
|
||||
//==================================================================================================
|
||||
#pragma mark -
|
||||
#pragma mark VC_Object
|
||||
//==================================================================================================
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
VC_Object::VC_Object(AudioObjectID inObjectID, AudioClassID inClassID, AudioClassID inBaseClassID, AudioObjectID inOwnerObjectID)
|
||||
:
|
||||
mObjectID(inObjectID),
|
||||
mClassID(inClassID),
|
||||
mBaseClassID(inBaseClassID),
|
||||
mOwnerObjectID(inOwnerObjectID),
|
||||
mIsActive(false)
|
||||
{
|
||||
}
|
||||
|
||||
void VC_Object::Activate()
|
||||
{
|
||||
mIsActive = true;
|
||||
}
|
||||
|
||||
void VC_Object::Deactivate()
|
||||
{
|
||||
mIsActive = false;
|
||||
}
|
||||
|
||||
VC_Object::~VC_Object()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
bool VC_Object::HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
#pragma unused(inObjectID, inClientPID)
|
||||
|
||||
bool theAnswer = false;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyBaseClass:
|
||||
case kAudioObjectPropertyClass:
|
||||
case kAudioObjectPropertyOwner:
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
theAnswer = true;
|
||||
break;
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool VC_Object::IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
#pragma unused(inObjectID, inClientPID)
|
||||
|
||||
bool theAnswer = false;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyBaseClass:
|
||||
case kAudioObjectPropertyClass:
|
||||
case kAudioObjectPropertyOwner:
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
theAnswer = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
Throw(CAException(kAudioHardwareUnknownPropertyError));
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
UInt32 VC_Object::GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const
|
||||
{
|
||||
#pragma unused(inObjectID, inClientPID, inQualifierDataSize, inQualifierData)
|
||||
|
||||
UInt32 theAnswer = 0;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyBaseClass:
|
||||
case kAudioObjectPropertyClass:
|
||||
theAnswer = sizeof(AudioClassID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwner:
|
||||
theAnswer = sizeof(AudioObjectID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
theAnswer = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
Throw(CAException(kAudioHardwareUnknownPropertyError));
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void VC_Object::GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const
|
||||
{
|
||||
#pragma unused(inObjectID, inClientPID, inQualifierDataSize, inQualifierData)
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyBaseClass:
|
||||
// This is the AudioClassID of the base class of this object. This is an invariant.
|
||||
ThrowIf(inDataSize < sizeof(AudioClassID), CAException(kAudioHardwareBadPropertySizeError), "VC_Object::GetPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass");
|
||||
*reinterpret_cast<AudioClassID*>(outData) = mBaseClassID;
|
||||
outDataSize = sizeof(AudioClassID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyClass:
|
||||
// This is the AudioClassID of the class of this object. This is an invariant.
|
||||
ThrowIf(inDataSize < sizeof(AudioClassID), CAException(kAudioHardwareBadPropertySizeError), "VC_Object::GetPropertyData: not enough space for the return value of kAudioObjectPropertyClass");
|
||||
*reinterpret_cast<AudioClassID*>(outData) = mClassID;
|
||||
outDataSize = sizeof(AudioClassID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwner:
|
||||
// The AudioObjectID of the object that owns this object. This is an invariant.
|
||||
ThrowIf(inDataSize < sizeof(AudioObjectID), CAException(kAudioHardwareBadPropertySizeError), "VC_Object::GetPropertyData: not enough space for the return value of kAudioObjectPropertyOwner");
|
||||
*reinterpret_cast<AudioClassID*>(outData) = mOwnerObjectID;
|
||||
outDataSize = sizeof(AudioObjectID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
// This is an array of AudioObjectIDs for the objects owned by this object. By default,
|
||||
// objects don't own any other objects. This is an invariant by default, but an object
|
||||
// that can contain other objects will likely need to do some synchronization to access
|
||||
// this property.
|
||||
outDataSize = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
Throw(CAException(kAudioHardwareUnknownPropertyError));
|
||||
};
|
||||
}
|
||||
|
||||
void VC_Object::SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
|
||||
{
|
||||
#pragma unused(inObjectID, inClientPID, inQualifierDataSize, inQualifierData, inDataSize, inData)
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
default:
|
||||
Throw(CAException(kAudioHardwareUnknownPropertyError));
|
||||
};
|
||||
}
|
||||
|
||||
162
driver/VC_Object.h
Normal file
162
driver/VC_Object.h
Normal file
@@ -0,0 +1,162 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_Object.h
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_Object.h from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
// The base class for our classes that represent audio objects. (See AudioServerPlugIn.h for a
|
||||
// quick explanation of audio objects.)
|
||||
//
|
||||
// This is a stripped down version of SA_Object.h. Unlike the sample code plugin that SA_Object.h
|
||||
// is from, we have a fixed set of audio objects. So we've removed the code that handled
|
||||
// growing/shrinking that set.
|
||||
//
|
||||
|
||||
#ifndef __VCDriver__VC_Object__
|
||||
#define __VCDriver__VC_Object__
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioServerPlugIn.h>
|
||||
|
||||
|
||||
// TODO: Update the large comment below to reflect what was removed from SA_Object.h? It hasn't
|
||||
// been changed at all so far, except to replace "SA_Object" with "VC_Object".
|
||||
|
||||
//==================================================================================================
|
||||
// VC_Object
|
||||
//
|
||||
// This is the base class for objects managed by VC_ObjectMap. It's only job is to ensure that
|
||||
// objects of this type have the proper external semantics for a reference counted object. This
|
||||
// means that the destructor is protected so that these objects cannot be deleted directly. Also,
|
||||
// these objects many not make a copy of another object or be assigned from another object. Note
|
||||
// that the reference count of the object is tracked and owned by the VC_ObjectMap.
|
||||
//
|
||||
// These objects provide RTTI information tied to the constants describing the HAL's API class
|
||||
// hierarchy as described in the headers. The class ID and base class IDs passed in to the
|
||||
// constructor must operate with the semantics described in AudioObjectBase.h where the base class
|
||||
// has to always be one of the standard classes. The class ID can be a custom value or a standard
|
||||
// value however. If it is a standard value, the base class should be the proper standard base
|
||||
// class. So for example, a standard volume control object will say that it's class is
|
||||
// kAudioVolumeControlClassID and that its base class is kAudioLevelControlClassID. In the case of
|
||||
// a custom boolean control, it would say that it's class is a custom value like 'MYBL' and that
|
||||
// its base class is kAudioBooleanControlClassID.
|
||||
//
|
||||
// Subclasses of this class must implement Activate(). This method is called after an object has
|
||||
// been constructed and inserted into the object map. Until Activate() is called, a constructed
|
||||
// object may not do anything active such as sending/receiving notifications or creating other
|
||||
// objects. Active operations may be performed in the Activate() method proper however. Note that
|
||||
// Activate() is called prior to any references to the object being handed out. As such, it does
|
||||
// not need to worry about being thread safe while Activate() is in progress.
|
||||
//
|
||||
// Subclasses of this class must also implement Deactivate(). This method is called when the object
|
||||
// is at the end of its lifecycle. Once Deactivate() has been called, the object may no longer
|
||||
// perform active operations, including Deactivating other objects. This is based on the notion that
|
||||
// all the objects have a definite point at which they are considered dead to the outside world.
|
||||
// For example, an AudioDevice object is dead if it's hardware is unplugged. The point of death is
|
||||
// the notification the owner of the device gets to signal that it has been unplugged. Note that it
|
||||
// is both normal and expected that a dead object might still have outstanding references. Thus, an
|
||||
// object has to put in some care to do the right thing when these zombie references are used. The
|
||||
// best thing to do is to just have those queries return appropriate errors.
|
||||
//
|
||||
// Deactivate() itself needs to be thread safe with respect to other operations taking place on the
|
||||
// object. This also means taking care to handle the Deactivation of owned objects. For example, an
|
||||
// AudioDevice object will almost always own one or more AudioStream objects. If the stream is in a
|
||||
// separate lock domain from it's owning device, then the device has to be very careful about how
|
||||
// it deactivates the stream such that it doesn't try to lock the stream's lock while holding the
|
||||
// device's lock which will inevitably lead to a deadlock situation. There are two reasonable
|
||||
// approaches to dealing with this kind of situation. The first is to just not get into it by
|
||||
// making the device share a lock domain with all it's owned objects like streams and controls. The
|
||||
// other approach is to use dispatch queues to make the work of Deactivating owned objects take
|
||||
// place outside of the device's lock domain. For example, if the device needs to deactivate a
|
||||
// stream, it can remove the stream from any tracking in the device object and then dispatch
|
||||
// asynchronously the Deactivate() call on the stream and the release of the reference the device
|
||||
// has on the stream.
|
||||
//
|
||||
// Note that both Activate() and Deactivate() are called by objects at large. Typically,
|
||||
// Activate() is called by the creator of the object, usually right after the object has been
|
||||
// allocated. Deactivate() will usually be called by the owner of the object upon recognizing that
|
||||
// the object is dead to the outside world. Going back to the example of an AudioDevice getting
|
||||
// unplugged, the Deactivate() method will be called by whomever receives the notification about
|
||||
// the hardware going away, which is often the owner of the object.
|
||||
//
|
||||
// This class also defines methods to implement the portion of the
|
||||
// AudioServerPlugInDriverInterface that deals with properties. The five methods all have the same
|
||||
// basic arguments and semantics. The class also provides the implementation for
|
||||
// the minimum required properties for all AudioObjects. There is a detailed commentary about each
|
||||
// specific property in the GetPropertyData() method.
|
||||
//
|
||||
// It is important that a thread retain and hold a reference while it is using an VC_Object and
|
||||
// that the reference be released promptly when the thread is finished using the object. By
|
||||
// assuming this, a VC_Object can minimize the amount of locking it needs to do. In particular,
|
||||
// purely static or invariant data can be handled without any locking at all.
|
||||
//==================================================================================================
|
||||
|
||||
class VC_Object
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
public:
|
||||
VC_Object(AudioObjectID inObjectID, AudioClassID inClassID, AudioClassID inBaseClassID, AudioObjectID inOwnerObjectID);
|
||||
|
||||
virtual void Activate();
|
||||
virtual void Deactivate();
|
||||
|
||||
protected:
|
||||
virtual ~VC_Object();
|
||||
|
||||
private:
|
||||
VC_Object(const VC_Object&);
|
||||
VC_Object& operator=(const VC_Object&);
|
||||
|
||||
#pragma mark Attributes
|
||||
|
||||
public:
|
||||
AudioObjectID GetObjectID() const { return mObjectID; }
|
||||
void* GetObjectIDAsPtr() const { uintptr_t thePtr = mObjectID; return reinterpret_cast<void*>(thePtr); }
|
||||
AudioClassID GetClassID() const { return mClassID; }
|
||||
AudioClassID GetBaseClassID() const { return mBaseClassID; }
|
||||
AudioObjectID GetOwnerObjectID() const { return mOwnerObjectID; }
|
||||
bool IsActive() const { return mIsActive; }
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
public:
|
||||
virtual bool HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual bool IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const;
|
||||
virtual void GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const;
|
||||
virtual void SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
protected:
|
||||
AudioObjectID mObjectID;
|
||||
AudioClassID mClassID;
|
||||
AudioClassID mBaseClassID;
|
||||
AudioObjectID mOwnerObjectID;
|
||||
bool mIsActive;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __VCDriver__VC_Object__ */
|
||||
|
||||
232
driver/VC_PlugIn.cpp
Normal file
232
driver/VC_PlugIn.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_PlugIn.cpp
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016, 2017 Kyle Neideck
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_PlugIn.cpp from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "VC_PlugIn.h"
|
||||
|
||||
// Local Includes
|
||||
#include "VC_Device.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAException.h"
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAPropertyAddress.h"
|
||||
#include "CADispatchQueue.h"
|
||||
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
pthread_once_t VC_PlugIn::sStaticInitializer = PTHREAD_ONCE_INIT;
|
||||
VC_PlugIn* VC_PlugIn::sInstance = NULL;
|
||||
AudioServerPlugInHostRef VC_PlugIn::sHost = NULL;
|
||||
|
||||
VC_PlugIn& VC_PlugIn::GetInstance()
|
||||
{
|
||||
pthread_once(&sStaticInitializer, StaticInitializer);
|
||||
return *sInstance;
|
||||
}
|
||||
|
||||
void VC_PlugIn::StaticInitializer()
|
||||
{
|
||||
try
|
||||
{
|
||||
sInstance = new VC_PlugIn;
|
||||
sInstance->Activate();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
DebugMsg("VC_PlugIn::StaticInitializer: failed to create the plug-in");
|
||||
delete sInstance;
|
||||
sInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
VC_PlugIn::VC_PlugIn()
|
||||
:
|
||||
VC_Object(kAudioObjectPlugInObject, kAudioPlugInClassID, kAudioObjectClassID, 0),
|
||||
mMutex("VC_PlugIn")
|
||||
{
|
||||
}
|
||||
|
||||
VC_PlugIn::~VC_PlugIn()
|
||||
{
|
||||
}
|
||||
|
||||
void VC_PlugIn::Deactivate()
|
||||
{
|
||||
CAMutex::Locker theLocker(mMutex);
|
||||
VC_Object::Deactivate();
|
||||
// TODO:
|
||||
//_RemoveAllDevices();
|
||||
}
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
bool VC_PlugIn::HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
case kAudioPlugInPropertyDeviceList:
|
||||
case kAudioPlugInPropertyTranslateUIDToDevice:
|
||||
case kAudioPlugInPropertyResourceBundle:
|
||||
theAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::HasProperty(inObjectID, inClientPID, inAddress);
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool VC_PlugIn::IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
case kAudioPlugInPropertyDeviceList:
|
||||
case kAudioPlugInPropertyTranslateUIDToDevice:
|
||||
case kAudioPlugInPropertyResourceBundle:
|
||||
theAnswer = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::IsPropertySettable(inObjectID, inClientPID, inAddress);
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
UInt32 VC_PlugIn::GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const
|
||||
{
|
||||
UInt32 theAnswer = 0;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
theAnswer = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
case kAudioPlugInPropertyDeviceList:
|
||||
theAnswer = sizeof(AudioObjectID);
|
||||
break;
|
||||
|
||||
case kAudioPlugInPropertyTranslateUIDToDevice:
|
||||
theAnswer = sizeof(AudioObjectID);
|
||||
break;
|
||||
|
||||
case kAudioPlugInPropertyResourceBundle:
|
||||
theAnswer = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::GetPropertyDataSize(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData);
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void VC_PlugIn::GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const
|
||||
{
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
// This is the human readable name of the maker of the plug-in.
|
||||
ThrowIf(inDataSize < sizeof(CFStringRef), CAException(kAudioHardwareBadPropertySizeError), "VC_PlugIn::GetPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer");
|
||||
*reinterpret_cast<CFStringRef*>(outData) = CFSTR("VolumeControl");
|
||||
outDataSize = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
// Fall through because this plug-in object only owns the device.
|
||||
case kAudioPlugInPropertyDeviceList:
|
||||
{
|
||||
AudioObjectID* theReturnedDeviceList = reinterpret_cast<AudioObjectID*>(outData);
|
||||
if(inDataSize >= sizeof(AudioObjectID))
|
||||
{
|
||||
theReturnedDeviceList[0] = kObjectID_Device;
|
||||
outDataSize = sizeof(AudioObjectID);
|
||||
}
|
||||
else
|
||||
{
|
||||
outDataSize = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioPlugInPropertyTranslateUIDToDevice:
|
||||
{
|
||||
// This property translates the UID passed in the qualifier as a CFString into the
|
||||
// AudioObjectID for the device the UID refers to or kAudioObjectUnknown if no device
|
||||
// has the UID.
|
||||
ThrowIf(inQualifierDataSize < sizeof(CFStringRef), CAException(kAudioHardwareBadPropertySizeError), "VC_PlugIn::GetPropertyData: the qualifier size is too small for kAudioPlugInPropertyTranslateUIDToDevice");
|
||||
ThrowIf(inDataSize < sizeof(AudioObjectID), CAException(kAudioHardwareBadPropertySizeError), "VC_PlugIn::GetPropertyData: not enough space for the return value of kAudioPlugInPropertyTranslateUIDToDevice");
|
||||
|
||||
CFStringRef theUID = *reinterpret_cast<const CFStringRef*>(inQualifierData);
|
||||
AudioObjectID* outID = reinterpret_cast<AudioObjectID*>(outData);
|
||||
|
||||
if(CFEqual(theUID, VC_Device::GetInstance().CopyDeviceUID()))
|
||||
{
|
||||
DebugMsg("VC_PlugIn::GetPropertyData: Returning VCDevice for "
|
||||
"kAudioPlugInPropertyTranslateUIDToDevice");
|
||||
*outID = kObjectID_Device;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogWarning("VC_PlugIn::GetPropertyData: Returning kAudioObjectUnknown for "
|
||||
"kAudioPlugInPropertyTranslateUIDToDevice");
|
||||
*outID = kAudioObjectUnknown;
|
||||
}
|
||||
|
||||
outDataSize = sizeof(AudioObjectID);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioPlugInPropertyResourceBundle:
|
||||
// The resource bundle is a path relative to the path of the plug-in's bundle.
|
||||
// To specify that the plug-in bundle itself should be used, we just return the
|
||||
// empty string.
|
||||
ThrowIf(inDataSize < sizeof(AudioObjectID), CAException(kAudioHardwareBadPropertySizeError), "VC_GetPlugInPropertyData: not enough space for the return value of kAudioPlugInPropertyResourceBundle");
|
||||
*reinterpret_cast<CFStringRef*>(outData) = CFSTR("");
|
||||
outDataSize = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
default:
|
||||
VC_Object::GetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void VC_PlugIn::SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
|
||||
{
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
default:
|
||||
VC_Object::SetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
90
driver/VC_PlugIn.h
Normal file
90
driver/VC_PlugIn.h
Normal file
@@ -0,0 +1,90 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_PlugIn.h
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_PlugIn.h from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
#ifndef __VCDriver__VC_PlugIn__
|
||||
#define __VCDriver__VC_PlugIn__
|
||||
|
||||
// SuperClass Includes
|
||||
#include "VC_Object.h"
|
||||
|
||||
// Local Includes
|
||||
#include "VC_Types.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAMutex.h"
|
||||
|
||||
|
||||
class VC_PlugIn
|
||||
:
|
||||
public VC_Object
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
public:
|
||||
static VC_PlugIn& GetInstance();
|
||||
|
||||
protected:
|
||||
VC_PlugIn();
|
||||
virtual ~VC_PlugIn();
|
||||
virtual void Deactivate();
|
||||
|
||||
private:
|
||||
static void StaticInitializer();
|
||||
|
||||
#pragma mark Host Access
|
||||
|
||||
public:
|
||||
static void SetHost(AudioServerPlugInHostRef inHost) { sHost = inHost; }
|
||||
|
||||
static void Host_PropertiesChanged(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[]) { if(sHost != NULL) { sHost->PropertiesChanged(sHost, inObjectID, inNumberAddresses, inAddresses); } }
|
||||
static void Host_RequestDeviceConfigurationChange(AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo) { if(sHost != NULL) { sHost->RequestDeviceConfigurationChange(sHost, inDeviceObjectID, inChangeAction, inChangeInfo); } }
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
public:
|
||||
virtual bool HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual bool IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const;
|
||||
virtual void GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const;
|
||||
virtual void SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
public:
|
||||
const CFStringRef GetBundleID() const { return CFSTR(kVCDriverBundleID); }
|
||||
|
||||
private:
|
||||
CAMutex mMutex;
|
||||
|
||||
static pthread_once_t sStaticInitializer;
|
||||
static VC_PlugIn* sInstance;
|
||||
static AudioServerPlugInHostRef sHost;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __VCDriver__VC_PlugIn__ */
|
||||
|
||||
946
driver/VC_PlugInInterface.cpp
Normal file
946
driver/VC_PlugInInterface.cpp
Normal file
@@ -0,0 +1,946 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_PlugInInterface.cpp
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016, 2017 Kyle Neideck
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_PlugIn.cpp from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioServerPlugIn.h>
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
|
||||
// Local Includes
|
||||
#include "VC_Types.h"
|
||||
#include "VC_Object.h"
|
||||
#include "VC_PlugIn.h"
|
||||
#include "VC_Device.h"
|
||||
|
||||
|
||||
#pragma mark COM Prototypes
|
||||
|
||||
// Entry points for the COM methods
|
||||
extern "C" void* VC_Create(CFAllocatorRef inAllocator, CFUUIDRef inRequestedTypeUUID);
|
||||
static HRESULT VC_QueryInterface(void* inDriver, REFIID inUUID, LPVOID* outInterface);
|
||||
static ULONG VC_AddRef(void* inDriver);
|
||||
static ULONG VC_Release(void* inDriver);
|
||||
static OSStatus VC_Initialize(AudioServerPlugInDriverRef inDriver, AudioServerPlugInHostRef inHost);
|
||||
static OSStatus VC_CreateDevice(AudioServerPlugInDriverRef inDriver, CFDictionaryRef inDescription, const AudioServerPlugInClientInfo* inClientInfo, AudioObjectID* outDeviceObjectID);
|
||||
static OSStatus VC_DestroyDevice(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID);
|
||||
static OSStatus VC_AddDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo);
|
||||
static OSStatus VC_RemoveDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo);
|
||||
static OSStatus VC_PerformDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo);
|
||||
static OSStatus VC_AbortDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo);
|
||||
static Boolean VC_HasProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress);
|
||||
static OSStatus VC_IsPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable);
|
||||
static OSStatus VC_GetPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
|
||||
static OSStatus VC_GetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData);
|
||||
static OSStatus VC_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
|
||||
static OSStatus VC_StartIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID);
|
||||
static OSStatus VC_StopIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID);
|
||||
static OSStatus VC_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, Float64* outSampleTime, UInt64* outHostTime, UInt64* outSeed);
|
||||
static OSStatus VC_WillDoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, Boolean* outWillDo, Boolean* outWillDoInPlace);
|
||||
static OSStatus VC_BeginIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo);
|
||||
static OSStatus VC_DoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, AudioObjectID inStreamObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo, void* ioMainBuffer, void* ioSecondaryBuffer);
|
||||
static OSStatus VC_EndIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo);
|
||||
|
||||
#pragma mark The COM Interface
|
||||
|
||||
static AudioServerPlugInDriverInterface gAudioServerPlugInDriverInterface =
|
||||
{
|
||||
NULL,
|
||||
VC_QueryInterface,
|
||||
VC_AddRef,
|
||||
VC_Release,
|
||||
VC_Initialize,
|
||||
VC_CreateDevice,
|
||||
VC_DestroyDevice,
|
||||
VC_AddDeviceClient,
|
||||
VC_RemoveDeviceClient,
|
||||
VC_PerformDeviceConfigurationChange,
|
||||
VC_AbortDeviceConfigurationChange,
|
||||
VC_HasProperty,
|
||||
VC_IsPropertySettable,
|
||||
VC_GetPropertyDataSize,
|
||||
VC_GetPropertyData,
|
||||
VC_SetPropertyData,
|
||||
VC_StartIO,
|
||||
VC_StopIO,
|
||||
VC_GetZeroTimeStamp,
|
||||
VC_WillDoIOOperation,
|
||||
VC_BeginIOOperation,
|
||||
VC_DoIOOperation,
|
||||
VC_EndIOOperation
|
||||
};
|
||||
static AudioServerPlugInDriverInterface* gAudioServerPlugInDriverInterfacePtr = &gAudioServerPlugInDriverInterface;
|
||||
static AudioServerPlugInDriverRef gAudioServerPlugInDriverRef = &gAudioServerPlugInDriverInterfacePtr;
|
||||
static UInt32 gAudioServerPlugInDriverRefCount = 1;
|
||||
|
||||
// TODO: This name is a bit misleading because the devices are actually owned by the plug-in.
|
||||
static VC_Object& VC_LookUpOwnerObject(AudioObjectID inObjectID)
|
||||
{
|
||||
switch(inObjectID)
|
||||
{
|
||||
case kObjectID_PlugIn:
|
||||
return VC_PlugIn::GetInstance();
|
||||
|
||||
case kObjectID_Device:
|
||||
case kObjectID_Stream_Input:
|
||||
case kObjectID_Stream_Output:
|
||||
case kObjectID_Volume_Output_Master:
|
||||
case kObjectID_Mute_Output_Master:
|
||||
return VC_Device::GetInstance();
|
||||
}
|
||||
|
||||
DebugMsg("VC_LookUpOwnerObject: unknown object");
|
||||
Throw(CAException(kAudioHardwareBadObjectError));
|
||||
}
|
||||
|
||||
static VC_AbstractDevice& VC_LookUpDevice(AudioObjectID inObjectID)
|
||||
{
|
||||
switch(inObjectID)
|
||||
{
|
||||
case kObjectID_Device:
|
||||
return VC_Device::GetInstance();
|
||||
}
|
||||
|
||||
DebugMsg("VC_LookUpDevice: unknown device");
|
||||
Throw(CAException(kAudioHardwareBadDeviceError));
|
||||
}
|
||||
|
||||
#pragma mark Factory
|
||||
|
||||
extern "C"
|
||||
void* VC_Create(CFAllocatorRef inAllocator, CFUUIDRef inRequestedTypeUUID)
|
||||
{
|
||||
// This is the CFPlugIn factory function. Its job is to create the implementation for the given
|
||||
// type provided that the type is supported. Because this driver is simple and all its
|
||||
// initialization is handled via static initialization when the bundle is loaded, all that
|
||||
// needs to be done is to return the AudioServerPlugInDriverRef that points to the driver's
|
||||
// interface. A more complicated driver would create any base line objects it needs to satisfy
|
||||
// the IUnknown methods that are used to discover that actual interface to talk to the driver.
|
||||
// The majority of the driver's initialization should be handled in the Initialize() method of
|
||||
// the driver's AudioServerPlugInDriverInterface.
|
||||
|
||||
#pragma unused(inAllocator)
|
||||
|
||||
void* theAnswer = NULL;
|
||||
if(CFEqual(inRequestedTypeUUID, kAudioServerPlugInTypeUUID))
|
||||
{
|
||||
theAnswer = gAudioServerPlugInDriverRef;
|
||||
|
||||
VC_PlugIn::GetInstance();
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark Inheritence
|
||||
|
||||
static HRESULT VC_QueryInterface(void* inDriver, REFIID inUUID, LPVOID* outInterface)
|
||||
{
|
||||
// This function is called by the HAL to get the interface to talk to the plug-in through.
|
||||
// AudioServerPlugIns are required to support the IUnknown interface and the
|
||||
// AudioServerPlugInDriverInterface. As it happens, all interfaces must also provide the
|
||||
// IUnknown interface, so we can always just return the single interface we made with
|
||||
// gAudioServerPlugInDriverInterfacePtr regardless of which one is asked for.
|
||||
|
||||
HRESULT theAnswer = 0;
|
||||
CFUUIDRef theRequestedUUID = NULL;
|
||||
|
||||
try
|
||||
{
|
||||
// validate the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_QueryInterface: bad driver reference");
|
||||
ThrowIfNULL(outInterface, CAException(kAudioHardwareIllegalOperationError), "VC_QueryInterface: no place to store the returned interface");
|
||||
|
||||
// make a CFUUIDRef from inUUID
|
||||
theRequestedUUID = CFUUIDCreateFromUUIDBytes(NULL, inUUID);
|
||||
ThrowIf(theRequestedUUID == NULL, CAException(kAudioHardwareIllegalOperationError), "VC_QueryInterface: failed to create the CFUUIDRef");
|
||||
|
||||
// AudioServerPlugIns only support two interfaces, IUnknown (which has to be supported by all
|
||||
// CFPlugIns) and AudioServerPlugInDriverInterface (which is the actual interface the HAL will
|
||||
// use).
|
||||
ThrowIf(!CFEqual(theRequestedUUID, IUnknownUUID) && !CFEqual(theRequestedUUID, kAudioServerPlugInDriverInterfaceUUID), CAException(E_NOINTERFACE), "VC_QueryInterface: requested interface is unsupported");
|
||||
ThrowIf(gAudioServerPlugInDriverRefCount == UINT32_MAX, CAException(E_NOINTERFACE), "VC_QueryInterface: the ref count is maxxed out");
|
||||
|
||||
// do the work
|
||||
++gAudioServerPlugInDriverRefCount;
|
||||
*outInterface = gAudioServerPlugInDriverRef;
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
if(theRequestedUUID != NULL)
|
||||
{
|
||||
CFRelease(theRequestedUUID);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static ULONG VC_AddRef(void* inDriver)
|
||||
{
|
||||
// This call returns the resulting reference count after the increment.
|
||||
|
||||
ULONG theAnswer = 0;
|
||||
|
||||
// check the arguments
|
||||
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "VC_AddRef: bad driver reference");
|
||||
FailIf(gAudioServerPlugInDriverRefCount == UINT32_MAX, Done, "VC_AddRef: out of references");
|
||||
|
||||
// increment the refcount
|
||||
++gAudioServerPlugInDriverRefCount;
|
||||
theAnswer = gAudioServerPlugInDriverRefCount;
|
||||
|
||||
Done:
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static ULONG VC_Release(void* inDriver)
|
||||
{
|
||||
// This call returns the resulting reference count after the decrement.
|
||||
|
||||
ULONG theAnswer = 0;
|
||||
|
||||
// check the arguments
|
||||
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "VC_Release: bad driver reference");
|
||||
FailIf(gAudioServerPlugInDriverRefCount == UINT32_MAX, Done, "VC_Release: out of references");
|
||||
|
||||
// decrement the refcount
|
||||
// Note that we don't do anything special if the refcount goes to zero as the HAL
|
||||
// will never fully release a plug-in it opens. We keep managing the refcount so that
|
||||
// the API semantics are correct though.
|
||||
--gAudioServerPlugInDriverRefCount;
|
||||
theAnswer = gAudioServerPlugInDriverRefCount;
|
||||
|
||||
Done:
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark Basic Operations
|
||||
|
||||
static OSStatus VC_Initialize(AudioServerPlugInDriverRef inDriver, AudioServerPlugInHostRef inHost)
|
||||
{
|
||||
// The job of this method is, as the name implies, to get the driver initialized. One specific
|
||||
// thing that needs to be done is to store the AudioServerPlugInHostRef so that it can be used
|
||||
// later. Note that when this call returns, the HAL will scan the various lists the driver
|
||||
// maintains (such as the device list) to get the initial set of objects the driver is
|
||||
// publishing. So, there is no need to notifiy the HAL about any objects created as part of the
|
||||
// execution of this method.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Check the arguments.
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_Initialize: bad driver reference");
|
||||
|
||||
// Store the AudioServerPlugInHostRef.
|
||||
VC_PlugIn::GetInstance().SetHost(inHost);
|
||||
|
||||
// Init/activate the device.
|
||||
VC_Device::GetInstance();
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_CreateDevice(AudioServerPlugInDriverRef inDriver, CFDictionaryRef inDescription, const AudioServerPlugInClientInfo* inClientInfo, AudioObjectID* outDeviceObjectID)
|
||||
{
|
||||
// This method is used to tell a driver that implements the Transport Manager semantics to
|
||||
// create an AudioEndpointDevice from a set of AudioEndpoints. Since this driver is not a
|
||||
// Transport Manager, we just return kAudioHardwareUnsupportedOperationError.
|
||||
|
||||
#pragma unused(inDriver, inDescription, inClientInfo, outDeviceObjectID)
|
||||
|
||||
return kAudioHardwareUnsupportedOperationError;
|
||||
}
|
||||
|
||||
static OSStatus VC_DestroyDevice(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID)
|
||||
{
|
||||
// This method is used to tell a driver that implements the Transport Manager semantics to
|
||||
// destroy an AudioEndpointDevice. Since this driver is not a Transport Manager, we just check
|
||||
// the arguments and return kAudioHardwareUnsupportedOperationError.
|
||||
|
||||
#pragma unused(inDriver, inDeviceObjectID)
|
||||
|
||||
return kAudioHardwareUnsupportedOperationError;
|
||||
}
|
||||
|
||||
static OSStatus VC_AddDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo)
|
||||
{
|
||||
// This method is used to inform the driver about a new client that is using the given device.
|
||||
// This allows the device to act differently depending on who the client is.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Check the arguments.
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_AddDeviceClient: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_AddDeviceClient: unknown device");
|
||||
|
||||
// Inform the device.
|
||||
VC_LookUpDevice(inDeviceObjectID).AddClient(inClientInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_RemoveDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo)
|
||||
{
|
||||
// This method is used to inform the driver about a client that is no longer using the given
|
||||
// device.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Check the arguments.
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_RemoveDeviceClient: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_RemoveDeviceClient: unknown device");
|
||||
|
||||
// Inform the device.
|
||||
VC_LookUpDevice(inDeviceObjectID).RemoveClient(inClientInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_PerformDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo)
|
||||
{
|
||||
// This method is called to tell the device that it can perform the configuration change that it
|
||||
// had requested via a call to the host method, RequestDeviceConfigurationChange(). The
|
||||
// arguments, inChangeAction and inChangeInfo are the same as what was passed to
|
||||
// RequestDeviceConfigurationChange().
|
||||
//
|
||||
// The HAL guarantees that IO will be stopped while this method is in progress. The HAL will
|
||||
// also handle figuring out exactly what changed for the non-control related properties. This
|
||||
// means that the only notifications that would need to be sent here would be for either
|
||||
// custom properties the HAL doesn't know about or for controls.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_PerformDeviceConfigurationChange: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_PerformDeviceConfigurationChange: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).PerformConfigChange(inChangeAction, inChangeInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_AbortDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo)
|
||||
{
|
||||
// This method is called to tell the driver that a request for a config change has been denied.
|
||||
// This provides the driver an opportunity to clean up any state associated with the request.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_PerformDeviceConfigurationChange: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_PerformDeviceConfigurationChange: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).AbortConfigChange(inChangeAction, inChangeInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
static Boolean VC_HasProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress)
|
||||
{
|
||||
// This method returns whether or not the given object has the given property.
|
||||
|
||||
Boolean theAnswer = false;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_HasProperty: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "VC_HasProperty: no address");
|
||||
|
||||
theAnswer = VC_LookUpOwnerObject(inObjectID).HasProperty(inObjectID, inClientProcessID, *inAddress);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LogError("VC_PlugInInterface::VC_HasProperty: unknown exception. (object: %u, address: %u)",
|
||||
inObjectID,
|
||||
inAddress ? inAddress->mSelector : 0);
|
||||
theAnswer = false;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_IsPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable)
|
||||
{
|
||||
// This method returns whether or not the given property on the object can have its value
|
||||
// changed.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_IsPropertySettable: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "VC_IsPropertySettable: no address");
|
||||
ThrowIfNULL(outIsSettable, CAException(kAudioHardwareIllegalOperationError), "VC_IsPropertySettable: no place to put the return value");
|
||||
|
||||
VC_Object& theAudioObject = VC_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
*outIsSettable = theAudioObject.IsPropertySettable(inObjectID, inClientProcessID, *inAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LogError("VC_PlugInInterface::VC_IsPropertySettable: unknown exception. (object: %u, address: %u)",
|
||||
inObjectID,
|
||||
inAddress ? inAddress->mSelector : 0);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_GetPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize)
|
||||
{
|
||||
// This method returns the byte size of the property's data.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_GetPropertyDataSize: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "VC_GetPropertyDataSize: no address");
|
||||
ThrowIfNULL(outDataSize, CAException(kAudioHardwareIllegalOperationError), "VC_GetPropertyDataSize: no place to put the return value");
|
||||
|
||||
VC_Object& theAudioObject = VC_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
*outDataSize = theAudioObject.GetPropertyDataSize(inObjectID, inClientProcessID, *inAddress, inQualifierDataSize, inQualifierData);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LogError("VC_PlugInInterface::VC_GetPropertyDataSize: unknown exception. (object: %u, address: %u)",
|
||||
inObjectID,
|
||||
inAddress ? inAddress->mSelector : 0);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_GetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData)
|
||||
{
|
||||
// This method fetches the data for a given property
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_GetPropertyData: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "VC_GetPropertyData: no address");
|
||||
ThrowIfNULL(outDataSize, CAException(kAudioHardwareIllegalOperationError), "VC_GetPropertyData: no place to put the return value size");
|
||||
ThrowIfNULL(outData, CAException(kAudioHardwareIllegalOperationError), "VC_GetPropertyData: no place to put the return value");
|
||||
|
||||
VC_Object& theAudioObject = VC_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
theAudioObject.GetPropertyData(inObjectID, inClientProcessID, *inAddress, inQualifierDataSize, inQualifierData, inDataSize, *outDataSize, outData);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LogError("VC_PlugInInterface::VC_GetPropertyData: unknown exception. (object: %u, address: %u)",
|
||||
inObjectID,
|
||||
inAddress ? inAddress->mSelector : 0);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
|
||||
{
|
||||
// This method changes the value of the given property
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_SetPropertyData: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "VC_SetPropertyData: no address");
|
||||
ThrowIfNULL(inData, CAException(kAudioHardwareIllegalOperationError), "VC_SetPropertyData: no data");
|
||||
|
||||
VC_Object& theAudioObject = VC_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
if(theAudioObject.IsPropertySettable(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
theAudioObject.SetPropertyData(inObjectID, inClientProcessID, *inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnsupportedOperationError;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LogError("VC_PlugInInterface::VC_SetPropertyData: unknown exception. (object: %u, address: %u)",
|
||||
inObjectID,
|
||||
inAddress ? inAddress->mSelector : 0);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark IO Operations
|
||||
|
||||
static OSStatus VC_StartIO(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID)
|
||||
{
|
||||
// This call tells the device that IO is starting for the given client. When this routine
|
||||
// returns, the device's clock is running and it is ready to have data read/written. It is
|
||||
// important to note that multiple clients can have IO running on the device at the same time.
|
||||
// So, work only needs to be done when the first client starts. All subsequent starts simply
|
||||
// increment the counter.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_StartIO: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_StartIO: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).StartIO(inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_StopIO(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID)
|
||||
{
|
||||
// This call tells the device that the client has stopped IO. The driver can stop the hardware
|
||||
// once all clients have stopped.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_StopIO: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_StopIO: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).StopIO(inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID,
|
||||
Float64* outSampleTime,
|
||||
UInt64* outHostTime,
|
||||
UInt64* outSeed)
|
||||
{
|
||||
#pragma unused(inClientID)
|
||||
// This method returns the current zero time stamp for the device. The HAL models the timing of
|
||||
// a device as a series of time stamps that relate the sample time to a host time. The zero
|
||||
// time stamps are spaced such that the sample times are the value of
|
||||
// kAudioDevicePropertyZeroTimeStampPeriod apart. This is often modeled using a ring buffer
|
||||
// where the zero time stamp is updated when wrapping around the ring buffer.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_GetZeroTimeStamp: bad driver reference");
|
||||
ThrowIfNULL(outSampleTime,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_GetZeroTimeStamp: no place to put the sample time");
|
||||
ThrowIfNULL(outHostTime,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_GetZeroTimeStamp: no place to put the host time");
|
||||
ThrowIfNULL(outSeed,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_GetZeroTimeStamp: no place to put the seed");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_GetZeroTimeStamp: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).GetZeroTimeStamp(*outSampleTime, *outHostTime, *outSeed);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_WillDoIOOperation(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID,
|
||||
UInt32 inOperationID,
|
||||
Boolean* outWillDo,
|
||||
Boolean* outWillDoInPlace)
|
||||
{
|
||||
#pragma unused(inClientID)
|
||||
// This method returns whether or not the device will do a given IO operation.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_WillDoIOOperation: bad driver reference");
|
||||
ThrowIfNULL(outWillDo,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_WillDoIOOperation: no place to put the will-do return value");
|
||||
ThrowIfNULL(outWillDoInPlace,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_WillDoIOOperation: no place to put the in-place return value");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_WillDoIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
bool willDo = false;
|
||||
bool willDoInPlace = false;
|
||||
VC_LookUpDevice(inDeviceObjectID).WillDoIOOperation(inOperationID, willDo, willDoInPlace);
|
||||
|
||||
// set the return values
|
||||
*outWillDo = willDo;
|
||||
*outWillDoInPlace = willDoInPlace;
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_BeginIOOperation(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID,
|
||||
UInt32 inOperationID,
|
||||
UInt32 inIOBufferFrameSize,
|
||||
const AudioServerPlugInIOCycleInfo* inIOCycleInfo)
|
||||
{
|
||||
// This is called at the beginning of an IO operation.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_BeginIOOperation: bad driver reference");
|
||||
ThrowIfNULL(inIOCycleInfo,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_BeginIOOperation: no cycle info");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_BeginIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).BeginIOOperation(inOperationID,
|
||||
inIOBufferFrameSize,
|
||||
*inIOCycleInfo,
|
||||
inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
DebugMsg("VC_PlugInInterface::VC_BeginIOOperation: unknown exception. (device: %s, operation: %u)",
|
||||
(inDeviceObjectID == kObjectID_Device ? "VCDevice" : "other"),
|
||||
inOperationID);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_DoIOOperation(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
AudioObjectID inStreamObjectID,
|
||||
UInt32 inClientID,
|
||||
UInt32 inOperationID,
|
||||
UInt32 inIOBufferFrameSize,
|
||||
const AudioServerPlugInIOCycleInfo* inIOCycleInfo,
|
||||
void* ioMainBuffer,
|
||||
void* ioSecondaryBuffer)
|
||||
{
|
||||
// This is called to actually perform a given IO operation.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_EndIOOperation: bad driver reference");
|
||||
ThrowIfNULL(inIOCycleInfo,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_EndIOOperation: no cycle info");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_EndIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).DoIOOperation(inStreamObjectID,
|
||||
inClientID,
|
||||
inOperationID,
|
||||
inIOBufferFrameSize,
|
||||
*inIOCycleInfo,
|
||||
ioMainBuffer,
|
||||
ioSecondaryBuffer);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
DebugMsg("VC_PlugInInterface::VC_DoIOOperation: unknown exception. (device: %s, operation: %u)",
|
||||
(inDeviceObjectID == kObjectID_Device ? "VCDevice" : "other"),
|
||||
inOperationID);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_EndIOOperation(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID,
|
||||
UInt32 inOperationID,
|
||||
UInt32 inIOBufferFrameSize,
|
||||
const AudioServerPlugInIOCycleInfo* inIOCycleInfo)
|
||||
{
|
||||
// This is called at the end of an IO operation.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_EndIOOperation: bad driver reference");
|
||||
ThrowIfNULL(inIOCycleInfo,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_EndIOOperation: no cycle info");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_EndIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).EndIOOperation(inOperationID,
|
||||
inIOBufferFrameSize,
|
||||
*inIOCycleInfo,
|
||||
inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
DebugMsg("VC_PlugInInterface::VC_EndIOOperation: unknown exception. (device: %s, operation: %u)",
|
||||
(inDeviceObjectID == kObjectID_Device ? "VCDevice" : "other"),
|
||||
inOperationID);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
488
driver/VC_Stream.cpp
Normal file
488
driver/VC_Stream.cpp
Normal file
@@ -0,0 +1,488 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// VC_Stream.cpp
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2017 Kyle Neideck
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "VC_Stream.h"
|
||||
|
||||
// Local Includes
|
||||
#include "VC_Types.h"
|
||||
#include "VC_Utils.h"
|
||||
#include "VC_Device.h"
|
||||
#include "VC_PlugIn.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
#include "CAPropertyAddress.h"
|
||||
#include "CADispatchQueue.h"
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
VC_Stream::VC_Stream(AudioObjectID inObjectID,
|
||||
AudioDeviceID inOwnerDeviceID,
|
||||
bool inIsInput,
|
||||
Float64 inSampleRate,
|
||||
UInt32 inStartingChannel)
|
||||
:
|
||||
VC_Object(inObjectID, kAudioStreamClassID, kAudioObjectClassID, inOwnerDeviceID),
|
||||
mStateMutex(inIsInput ? "Input Stream State" : "Output Stream State"),
|
||||
mIsInput(inIsInput),
|
||||
mIsStreamActive(false),
|
||||
mSampleRate(inSampleRate),
|
||||
mStartingChannel(inStartingChannel)
|
||||
{
|
||||
}
|
||||
|
||||
VC_Stream::~VC_Stream()
|
||||
{
|
||||
}
|
||||
|
||||
bool VC_Stream::HasProperty(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
// For each object, this driver implements all the required properties plus a few extras that
|
||||
// are useful but not required. There is more detailed commentary about each property in the
|
||||
// GetPropertyData() method.
|
||||
|
||||
bool theAnswer = false;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioStreamPropertyIsActive:
|
||||
case kAudioStreamPropertyDirection:
|
||||
case kAudioStreamPropertyTerminalType:
|
||||
case kAudioStreamPropertyStartingChannel:
|
||||
case kAudioStreamPropertyLatency:
|
||||
case kAudioStreamPropertyVirtualFormat:
|
||||
case kAudioStreamPropertyPhysicalFormat:
|
||||
case kAudioStreamPropertyAvailableVirtualFormats:
|
||||
case kAudioStreamPropertyAvailablePhysicalFormats:
|
||||
theAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::HasProperty(inObjectID, inClientPID, inAddress);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool VC_Stream::IsPropertySettable(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
// For each object, this driver implements all the required properties plus a few extras that
|
||||
// are useful but not required. There is more detailed commentary about each property in the
|
||||
// GetPropertyData() method.
|
||||
|
||||
bool theAnswer = false;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioStreamPropertyDirection:
|
||||
case kAudioStreamPropertyTerminalType:
|
||||
case kAudioStreamPropertyStartingChannel:
|
||||
case kAudioStreamPropertyLatency:
|
||||
case kAudioStreamPropertyAvailableVirtualFormats:
|
||||
case kAudioStreamPropertyAvailablePhysicalFormats:
|
||||
theAnswer = false;
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyIsActive:
|
||||
case kAudioStreamPropertyVirtualFormat:
|
||||
case kAudioStreamPropertyPhysicalFormat:
|
||||
theAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::IsPropertySettable(inObjectID, inClientPID, inAddress);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
UInt32 VC_Stream::GetPropertyDataSize(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* __nullable inQualifierData) const
|
||||
{
|
||||
// For each object, this driver implements all the required properties plus a few extras that
|
||||
// are useful but not required. There is more detailed commentary about each property in the
|
||||
// GetPropertyData() method.
|
||||
|
||||
UInt32 theAnswer = 0;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioStreamPropertyIsActive:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyDirection:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyTerminalType:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyStartingChannel:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyLatency:
|
||||
theAnswer = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyVirtualFormat:
|
||||
case kAudioStreamPropertyPhysicalFormat:
|
||||
theAnswer = sizeof(AudioStreamBasicDescription);
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyAvailableVirtualFormats:
|
||||
case kAudioStreamPropertyAvailablePhysicalFormats:
|
||||
theAnswer = 1 * sizeof(AudioStreamRangedDescription);
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Object::GetPropertyDataSize(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void VC_Stream::GetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* __nullable inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
UInt32& outDataSize,
|
||||
void* outData) const
|
||||
{
|
||||
// Since most of the data that will get returned is static, there are few instances where it is
|
||||
// necessary to lock the state mutex.
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyBaseClass:
|
||||
// The base class for kAudioStreamClassID is kAudioObjectClassID
|
||||
ThrowIf(inDataSize < sizeof(AudioClassID),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Stream::GetPropertyData: not enough space for the return "
|
||||
"value of kAudioObjectPropertyBaseClass for the stream");
|
||||
*reinterpret_cast<AudioClassID*>(outData) = kAudioObjectClassID;
|
||||
outDataSize = sizeof(AudioClassID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyClass:
|
||||
// Streams are of the class kAudioStreamClassID
|
||||
ThrowIf(inDataSize < sizeof(AudioClassID),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Stream::GetPropertyData: not enough space for the return "
|
||||
"value of kAudioObjectPropertyClass for the stream");
|
||||
*reinterpret_cast<AudioClassID*>(outData) = kAudioStreamClassID;
|
||||
outDataSize = sizeof(AudioClassID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwner:
|
||||
// A stream's owner is a device object.
|
||||
{
|
||||
ThrowIf(inDataSize < sizeof(AudioObjectID),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Stream::GetPropertyData: not enough space for the return "
|
||||
"value of kAudioObjectPropertyOwner for the stream");
|
||||
|
||||
// Lock the state mutex to create a memory barrier, just in case a subclass ever
|
||||
// allows mOwnerObjectID to be modified.
|
||||
CAMutex::Locker theStateLocker(mStateMutex);
|
||||
|
||||
// Return the requested value.
|
||||
*reinterpret_cast<AudioObjectID*>(outData) = mOwnerObjectID;
|
||||
outDataSize = sizeof(AudioObjectID);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyIsActive:
|
||||
// This property tells the device whether or not the given stream is going to
|
||||
// be used for IO. Note that we need to take the state lock to examine this
|
||||
// value.
|
||||
{
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Stream::GetPropertyData: not enough space for the return "
|
||||
"value of kAudioStreamPropertyIsActive for the stream");
|
||||
|
||||
// Take the state lock.
|
||||
CAMutex::Locker theStateLocker(mStateMutex);
|
||||
|
||||
// Return the requested value.
|
||||
*reinterpret_cast<UInt32*>(outData) = mIsStreamActive;
|
||||
outDataSize = sizeof(UInt32);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyDirection:
|
||||
// This returns whether the stream is an input or output stream.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Stream::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioStreamPropertyDirection for the stream");
|
||||
*reinterpret_cast<UInt32*>(outData) = mIsInput ? 1 : 0;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyTerminalType:
|
||||
// This returns a value that indicates what is at the other end of the stream
|
||||
// such as a speaker or headphones or a microphone.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Stream::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioStreamPropertyTerminalType for the stream");
|
||||
*reinterpret_cast<UInt32*>(outData) =
|
||||
mIsInput ? kAudioStreamTerminalTypeMicrophone : kAudioStreamTerminalTypeSpeaker;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyStartingChannel:
|
||||
// This property returns the absolute channel number for the first channel in
|
||||
// the stream. For example, if a device has two output streams with two
|
||||
// channels each, then the starting channel number for the first stream is 1
|
||||
// and the starting channel number for the second stream is 3.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Stream::GetPropertyData: not enough space for the return "
|
||||
"value of kAudioStreamPropertyStartingChannel for the stream");
|
||||
*reinterpret_cast<UInt32*>(outData) = mStartingChannel;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyLatency:
|
||||
// This property returns any additional presentation latency the stream has.
|
||||
ThrowIf(inDataSize < sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Stream::GetPropertyData: not enough space for the return "
|
||||
"value of kAudioStreamPropertyLatency for the stream");
|
||||
*reinterpret_cast<UInt32*>(outData) = 0;
|
||||
outDataSize = sizeof(UInt32);
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyVirtualFormat:
|
||||
case kAudioStreamPropertyPhysicalFormat:
|
||||
// This returns the current format of the stream in an AudioStreamBasicDescription.
|
||||
// For devices that don't override the mix operation, the virtual format has to be the
|
||||
// same as the physical format.
|
||||
{
|
||||
ThrowIf(inDataSize < sizeof(AudioStreamBasicDescription),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Stream::GetPropertyData: not enough space for the return "
|
||||
"value of kAudioStreamPropertyVirtualFormat for the stream");
|
||||
|
||||
// This particular device always vends 32-bit native endian floats
|
||||
AudioStreamBasicDescription* outASBD =
|
||||
reinterpret_cast<AudioStreamBasicDescription*>(outData);
|
||||
|
||||
// Our streams have the same sample rate as the device they belong to.
|
||||
outASBD->mSampleRate = mSampleRate;
|
||||
outASBD->mFormatID = kAudioFormatLinearPCM;
|
||||
outASBD->mFormatFlags =
|
||||
kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
outASBD->mBytesPerPacket = 8;
|
||||
outASBD->mFramesPerPacket = 1;
|
||||
outASBD->mBytesPerFrame = 8;
|
||||
outASBD->mChannelsPerFrame = 2;
|
||||
outASBD->mBitsPerChannel = 32;
|
||||
|
||||
outDataSize = sizeof(AudioStreamBasicDescription);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyAvailableVirtualFormats:
|
||||
case kAudioStreamPropertyAvailablePhysicalFormats:
|
||||
// This returns an array of AudioStreamRangedDescriptions that describe what
|
||||
// formats are supported.
|
||||
if((inDataSize / sizeof(AudioStreamRangedDescription)) >= 1)
|
||||
{
|
||||
AudioStreamRangedDescription* outASRD =
|
||||
reinterpret_cast<AudioStreamRangedDescription*>(outData);
|
||||
|
||||
outASRD[0].mFormat.mSampleRate = mSampleRate;
|
||||
outASRD[0].mFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
outASRD[0].mFormat.mFormatFlags =
|
||||
kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
outASRD[0].mFormat.mBytesPerPacket = 8;
|
||||
outASRD[0].mFormat.mFramesPerPacket = 1;
|
||||
outASRD[0].mFormat.mBytesPerFrame = 8;
|
||||
outASRD[0].mFormat.mChannelsPerFrame = 2;
|
||||
outASRD[0].mFormat.mBitsPerChannel = 32;
|
||||
// These match kAudioDevicePropertyAvailableNominalSampleRates.
|
||||
outASRD[0].mSampleRateRange.mMinimum = 1.0;
|
||||
outASRD[0].mSampleRateRange.mMaximum = 1000000000.0;
|
||||
|
||||
// Report how much we wrote.
|
||||
outDataSize = sizeof(AudioStreamRangedDescription);
|
||||
}
|
||||
else
|
||||
{
|
||||
outDataSize = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
VC_Object::GetPropertyData(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData,
|
||||
inDataSize,
|
||||
outDataSize,
|
||||
outData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void VC_Stream::SetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* __nullable inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
const void* inData)
|
||||
{
|
||||
// There is more detailed commentary about each property in the GetPropertyData() method.
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioStreamPropertyIsActive:
|
||||
{
|
||||
// Changing the active state of a stream doesn't affect IO or change the structure
|
||||
// so we can just save the state and send the notification.
|
||||
ThrowIf(inDataSize != sizeof(UInt32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Stream::SetPropertyData: wrong size for the data for "
|
||||
"kAudioStreamPropertyIsActive");
|
||||
bool theNewIsActive = *reinterpret_cast<const UInt32*>(inData) != 0;
|
||||
|
||||
CAMutex::Locker theStateLocker(mStateMutex);
|
||||
|
||||
if(mIsStreamActive != theNewIsActive)
|
||||
{
|
||||
mIsStreamActive = theNewIsActive;
|
||||
|
||||
// Send the notification.
|
||||
CADispatchQueue::GetGlobalSerialQueue().Dispatch(false, ^{
|
||||
AudioObjectPropertyAddress theProperty[] = {
|
||||
CAPropertyAddress(kAudioStreamPropertyIsActive)
|
||||
};
|
||||
VC_PlugIn::Host_PropertiesChanged(inObjectID, 1, theProperty);
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioStreamPropertyVirtualFormat:
|
||||
case kAudioStreamPropertyPhysicalFormat:
|
||||
{
|
||||
// The device that owns the stream handles changing the stream format, as it needs
|
||||
// to be handled via the RequestConfigChange/PerformConfigChange machinery. The
|
||||
// stream only needs to validate the format at this point.
|
||||
//
|
||||
// Note that because our devices only supports 2 channel 32 bit float data, the only
|
||||
// thing that can change is the sample rate.
|
||||
ThrowIf(inDataSize != sizeof(AudioStreamBasicDescription),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_Stream::SetPropertyData: wrong size for the data for "
|
||||
"kAudioStreamPropertyPhysicalFormat");
|
||||
|
||||
const AudioStreamBasicDescription* theNewFormat =
|
||||
reinterpret_cast<const AudioStreamBasicDescription*>(inData);
|
||||
|
||||
ThrowIf(theNewFormat->mFormatID != kAudioFormatLinearPCM,
|
||||
CAException(kAudioDeviceUnsupportedFormatError),
|
||||
"VC_Stream::SetPropertyData: unsupported format ID for "
|
||||
"kAudioStreamPropertyPhysicalFormat");
|
||||
ThrowIf(theNewFormat->mFormatFlags !=
|
||||
(kAudioFormatFlagIsFloat |
|
||||
kAudioFormatFlagsNativeEndian |
|
||||
kAudioFormatFlagIsPacked),
|
||||
CAException(kAudioDeviceUnsupportedFormatError),
|
||||
"VC_Stream::SetPropertyData: unsupported format flags for "
|
||||
"kAudioStreamPropertyPhysicalFormat");
|
||||
ThrowIf(theNewFormat->mBytesPerPacket != 8,
|
||||
CAException(kAudioDeviceUnsupportedFormatError),
|
||||
"VC_Stream::SetPropertyData: unsupported bytes per packet for "
|
||||
"kAudioStreamPropertyPhysicalFormat");
|
||||
ThrowIf(theNewFormat->mFramesPerPacket != 1,
|
||||
CAException(kAudioDeviceUnsupportedFormatError),
|
||||
"VC_Stream::SetPropertyData: unsupported frames per packet for "
|
||||
"kAudioStreamPropertyPhysicalFormat");
|
||||
ThrowIf(theNewFormat->mBytesPerFrame != 8,
|
||||
CAException(kAudioDeviceUnsupportedFormatError),
|
||||
"VC_Stream::SetPropertyData: unsupported bytes per frame for "
|
||||
"kAudioStreamPropertyPhysicalFormat");
|
||||
ThrowIf(theNewFormat->mChannelsPerFrame != 2,
|
||||
CAException(kAudioDeviceUnsupportedFormatError),
|
||||
"VC_Stream::SetPropertyData: unsupported channels per frame for "
|
||||
"kAudioStreamPropertyPhysicalFormat");
|
||||
ThrowIf(theNewFormat->mBitsPerChannel != 32,
|
||||
CAException(kAudioDeviceUnsupportedFormatError),
|
||||
"VC_Stream::SetPropertyData: unsupported bits per channel for "
|
||||
"kAudioStreamPropertyPhysicalFormat");
|
||||
ThrowIf(theNewFormat->mSampleRate < 1.0,
|
||||
CAException(kAudioDeviceUnsupportedFormatError),
|
||||
"VC_Stream::SetPropertyData: unsupported sample rate for "
|
||||
"kAudioStreamPropertyPhysicalFormat");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
VC_Object::SetPropertyData(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData,
|
||||
inDataSize,
|
||||
inData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
#pragma mark Accessors
|
||||
|
||||
void VC_Stream::SetSampleRate(Float64 inSampleRate)
|
||||
{
|
||||
CAMutex::Locker theStateLocker(mStateMutex);
|
||||
mSampleRate = inSampleRate;
|
||||
}
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
112
driver/VC_Stream.h
Normal file
112
driver/VC_Stream.h
Normal file
@@ -0,0 +1,112 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// VC_Stream.h
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2017 Kyle Neideck
|
||||
//
|
||||
// An input or output audio stream. Each stream belongs to a device (see VC_AbstractDevice), which
|
||||
// in turn belongs to a plug-in (see VC_PlugIn).
|
||||
//
|
||||
// This class only handles the stream's HAL properties, i.e. the metadata about the stream, not the
|
||||
// audio data itself.
|
||||
//
|
||||
|
||||
#ifndef VCDriver__VC_Stream
|
||||
#define VCDriver__VC_Stream
|
||||
|
||||
// SuperClass Includes
|
||||
#include "VC_Object.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAMutex.h"
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioHardwareBase.h>
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
class VC_Stream
|
||||
:
|
||||
public VC_Object
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
public:
|
||||
VC_Stream(AudioObjectID inObjectID,
|
||||
AudioObjectID inOwnerDeviceID,
|
||||
bool inIsInput,
|
||||
Float64 inSampleRate,
|
||||
UInt32 inStartingChannel = 1);
|
||||
virtual ~VC_Stream();
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
public:
|
||||
bool HasProperty(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const;
|
||||
bool IsPropertySettable(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const;
|
||||
UInt32 GetPropertyDataSize(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* __nullable inQualifierData) const;
|
||||
void GetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* __nullable inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
UInt32& outDataSize,
|
||||
void* outData) const;
|
||||
void SetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* __nullable inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
const void* inData);
|
||||
|
||||
#pragma mark Accessors
|
||||
|
||||
void SetSampleRate(Float64 inSampleRate);
|
||||
|
||||
private:
|
||||
CAMutex mStateMutex;
|
||||
|
||||
bool mIsInput;
|
||||
Float64 mSampleRate;
|
||||
/*! True if the stream is enabled and doing IO. See kAudioStreamPropertyIsActive. */
|
||||
bool mIsStreamActive;
|
||||
/*!
|
||||
The absolute channel number for the first channel in the stream. For example, if a device has
|
||||
two output streams with two channels each, then the starting channel number for the first
|
||||
stream is 1 and the starting channel number for the second stream is 3. See
|
||||
kAudioStreamPropertyStartingChannel.
|
||||
*/
|
||||
UInt32 mStartingChannel;
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* VCDriver__VC_Stream */
|
||||
|
||||
405
driver/VC_TaskQueue.cpp
Normal file
405
driver/VC_TaskQueue.cpp
Normal file
@@ -0,0 +1,405 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_TaskQueue.cpp
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "VC_TaskQueue.h"
|
||||
|
||||
// Local Includes
|
||||
#include "VC_Types.h"
|
||||
#include "VC_Utils.h"
|
||||
#include "VC_PlugIn.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAException.h"
|
||||
#include "CADebugMacros.h"
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion"
|
||||
#include "CAAtomic.h"
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// System Includes
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <mach/task.h>
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
#pragma mark Construction/destruction
|
||||
|
||||
VC_TaskQueue::VC_TaskQueue()
|
||||
:
|
||||
// The inline documentation for thread_time_constraint_policy.period says "A value of 0 indicates that there is no
|
||||
// inherent periodicity in the computation". So I figure setting the period to 0 means the scheduler will take as long
|
||||
// as it wants to wake our real-time thread, which is fine for us, but once it has only other real-time threads can
|
||||
// preempt us. (And that's only if they won't make our computation take longer than kRealTimeThreadMaximumComputationNs).
|
||||
mRealTimeThread(&VC_TaskQueue::RealTimeThreadProc,
|
||||
this,
|
||||
/* inPeriod = */ 0,
|
||||
NanosToAbsoluteTime(kRealTimeThreadNominalComputationNs),
|
||||
NanosToAbsoluteTime(kRealTimeThreadMaximumComputationNs),
|
||||
/* inIsPreemptible = */ true),
|
||||
mNonRealTimeThread(&VC_TaskQueue::NonRealTimeThreadProc, this)
|
||||
{
|
||||
// Init the semaphores
|
||||
auto createSemaphore = [] () {
|
||||
semaphore_t theSemaphore;
|
||||
kern_return_t theError = semaphore_create(mach_task_self(), &theSemaphore, SYNC_POLICY_FIFO, 0);
|
||||
|
||||
VC_Utils::ThrowIfMachError("VC_TaskQueue::VC_TaskQueue", "semaphore_create", theError);
|
||||
|
||||
ThrowIf(theSemaphore == SEMAPHORE_NULL,
|
||||
CAException(kAudioHardwareUnspecifiedError),
|
||||
"VC_TaskQueue::VC_TaskQueue: Could not create semaphore");
|
||||
|
||||
return theSemaphore;
|
||||
};
|
||||
|
||||
mRealTimeThreadWorkQueuedSemaphore = createSemaphore();
|
||||
mNonRealTimeThreadWorkQueuedSemaphore = createSemaphore();
|
||||
mRealTimeThreadSyncTaskCompletedSemaphore = createSemaphore();
|
||||
mNonRealTimeThreadSyncTaskCompletedSemaphore = createSemaphore();
|
||||
|
||||
// Pre-allocate enough tasks in mNonRealTimeThreadTasksFreeList that the real-time threads should never have to
|
||||
// allocate memory when adding a task to the non-realtime queue.
|
||||
for(UInt32 i = 0; i < kNonRealTimeThreadTaskBufferSize; i++)
|
||||
{
|
||||
VC_Task* theTask = new VC_Task;
|
||||
mNonRealTimeThreadTasksFreeList.push_NA(theTask);
|
||||
}
|
||||
|
||||
// Start the worker threads
|
||||
mRealTimeThread.Start();
|
||||
mNonRealTimeThread.Start();
|
||||
}
|
||||
|
||||
VC_TaskQueue::~VC_TaskQueue()
|
||||
{
|
||||
// Join the worker threads
|
||||
VCLogAndSwallowExceptionsMsg("VC_TaskQueue::~VC_TaskQueue", "QueueSync", ([&] {
|
||||
QueueSync(kVCTaskStopWorkerThread, /* inRunOnRealtimeThread = */ true);
|
||||
QueueSync(kVCTaskStopWorkerThread, /* inRunOnRealtimeThread = */ false);
|
||||
}));
|
||||
|
||||
// Destroy the semaphores
|
||||
auto destroySemaphore = [] (semaphore_t inSemaphore) {
|
||||
kern_return_t theError = semaphore_destroy(mach_task_self(), inSemaphore);
|
||||
|
||||
VC_Utils::LogIfMachError("VC_TaskQueue::~VC_TaskQueue", "semaphore_destroy", theError);
|
||||
};
|
||||
|
||||
destroySemaphore(mRealTimeThreadWorkQueuedSemaphore);
|
||||
destroySemaphore(mNonRealTimeThreadWorkQueuedSemaphore);
|
||||
destroySemaphore(mRealTimeThreadSyncTaskCompletedSemaphore);
|
||||
destroySemaphore(mNonRealTimeThreadSyncTaskCompletedSemaphore);
|
||||
|
||||
VC_Task* theTask;
|
||||
|
||||
// Delete the tasks in the non-realtime tasks free list
|
||||
while((theTask = mNonRealTimeThreadTasksFreeList.pop_atomic()) != NULL)
|
||||
{
|
||||
delete theTask;
|
||||
}
|
||||
|
||||
// Delete any tasks left on the non-realtime queue that need to be
|
||||
while((theTask = mNonRealTimeThreadTasks.pop_atomic()) != NULL)
|
||||
{
|
||||
if(!theTask->IsSync())
|
||||
{
|
||||
delete theTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
UInt32 VC_TaskQueue::NanosToAbsoluteTime(UInt32 inNanos)
|
||||
{
|
||||
// Converts a duration from nanoseconds to absolute time (i.e. number of bus cycles). Used for calculating
|
||||
// the real-time thread's time constraint policy.
|
||||
|
||||
mach_timebase_info_data_t theTimebaseInfo;
|
||||
mach_timebase_info(&theTimebaseInfo);
|
||||
|
||||
Float64 theTicksPerNs = static_cast<Float64>(theTimebaseInfo.denom) / theTimebaseInfo.numer;
|
||||
return static_cast<UInt32>(inNanos * theTicksPerNs);
|
||||
}
|
||||
|
||||
#pragma mark Task queueing
|
||||
|
||||
void VC_TaskQueue::QueueAsync_SendPropertyNotification(AudioObjectPropertySelector inProperty, AudioObjectID inDeviceID)
|
||||
{
|
||||
DebugMsg("VC_TaskQueue::QueueAsync_SendPropertyNotification: Queueing property notification. inProperty=%u inDeviceID=%u",
|
||||
inProperty,
|
||||
inDeviceID);
|
||||
VC_Task theTask(kVCTaskSendPropertyNotification, /* inIsSync = */ false, inProperty, inDeviceID);
|
||||
QueueOnNonRealtimeThread(theTask);
|
||||
}
|
||||
|
||||
UInt64 VC_TaskQueue::QueueSync(VC_TaskID inTaskID, bool inRunOnRealtimeThread, UInt64 inTaskArg1, UInt64 inTaskArg2)
|
||||
{
|
||||
DebugMsg("VC_TaskQueue::QueueSync: Queueing task synchronously to be processed on the %s thread. inTaskID=%d inTaskArg1=%llu inTaskArg2=%llu",
|
||||
(inRunOnRealtimeThread ? "realtime" : "non-realtime"),
|
||||
inTaskID,
|
||||
inTaskArg1,
|
||||
inTaskArg2);
|
||||
|
||||
// Create the task
|
||||
VC_Task theTask(inTaskID, /* inIsSync = */ true, inTaskArg1, inTaskArg2);
|
||||
|
||||
// Add the task to the queue
|
||||
TAtomicStack<VC_Task>& theTasks = (inRunOnRealtimeThread ? mRealTimeThreadTasks : mNonRealTimeThreadTasks);
|
||||
theTasks.push_atomic(&theTask);
|
||||
|
||||
// Wake the worker thread so it'll process the task. (Note that semaphore_signal has an implicit barrier.)
|
||||
kern_return_t theError = semaphore_signal(inRunOnRealtimeThread ? mRealTimeThreadWorkQueuedSemaphore : mNonRealTimeThreadWorkQueuedSemaphore);
|
||||
VC_Utils::ThrowIfMachError("VC_TaskQueue::QueueSync", "semaphore_signal", theError);
|
||||
|
||||
// Wait until the task has been processed.
|
||||
//
|
||||
// The worker thread signals all threads waiting on this semaphore when it finishes a task. The comments in WorkerThreadProc
|
||||
// explain why we have to check the condition in a loop here.
|
||||
bool didLogTimeoutMessage = false;
|
||||
while(!theTask.IsComplete())
|
||||
{
|
||||
semaphore_t theTaskCompletedSemaphore =
|
||||
inRunOnRealtimeThread ? mRealTimeThreadSyncTaskCompletedSemaphore : mNonRealTimeThreadSyncTaskCompletedSemaphore;
|
||||
// TODO: Because the worker threads use semaphore_signal_all instead of semaphore_signal, a thread can miss the signal if
|
||||
// it isn't waiting at the right time. Using a timeout for now as a temporary fix so threads don't get stuck here.
|
||||
theError = semaphore_timedwait(theTaskCompletedSemaphore,
|
||||
(mach_timespec_t){ 0, kRealTimeThreadMaximumComputationNs * 4 });
|
||||
|
||||
if(theError == KERN_OPERATION_TIMED_OUT)
|
||||
{
|
||||
if(!didLogTimeoutMessage && inRunOnRealtimeThread)
|
||||
{
|
||||
DebugMsg("VC_TaskQueue::QueueSync: Task %d taking longer than expected.", theTask.GetTaskID());
|
||||
didLogTimeoutMessage = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VC_Utils::ThrowIfMachError("VC_TaskQueue::QueueSync", "semaphore_timedwait", theError);
|
||||
}
|
||||
|
||||
CAMemoryBarrier();
|
||||
}
|
||||
|
||||
if(didLogTimeoutMessage)
|
||||
{
|
||||
DebugMsg("VC_TaskQueue::QueueSync: Late task %d finished.", theTask.GetTaskID());
|
||||
}
|
||||
|
||||
if(theTask.GetReturnValue() != INT64_MAX)
|
||||
{
|
||||
DebugMsg("VC_TaskQueue::QueueSync: Task %d returned %llu.", theTask.GetTaskID(), theTask.GetReturnValue());
|
||||
}
|
||||
|
||||
return theTask.GetReturnValue();
|
||||
}
|
||||
|
||||
void VC_TaskQueue::QueueOnNonRealtimeThread(VC_Task inTask)
|
||||
{
|
||||
// Add the task to our task list
|
||||
VC_Task* freeTask = mNonRealTimeThreadTasksFreeList.pop_atomic();
|
||||
|
||||
if(freeTask == NULL)
|
||||
{
|
||||
LogWarning("VC_TaskQueue::QueueOnNonRealtimeThread: No pre-allocated tasks left in the free list. Allocating new task.");
|
||||
freeTask = new VC_Task;
|
||||
}
|
||||
|
||||
*freeTask = inTask;
|
||||
|
||||
mNonRealTimeThreadTasks.push_atomic(freeTask);
|
||||
|
||||
// Signal the worker thread to process the task. (Note that semaphore_signal has an implicit barrier.)
|
||||
kern_return_t theError = semaphore_signal(mNonRealTimeThreadWorkQueuedSemaphore);
|
||||
VC_Utils::ThrowIfMachError("VC_TaskQueue::QueueOnNonRealtimeThread", "semaphore_signal", theError);
|
||||
}
|
||||
|
||||
#pragma mark Worker threads
|
||||
|
||||
void VC_TaskQueue::AssertCurrentThreadIsRTWorkerThread(const char* inCallerMethodName)
|
||||
{
|
||||
#if DEBUG // This Assert macro always checks the condition, even in release builds if the compiler doesn't optimise it away
|
||||
if(!mRealTimeThread.IsCurrentThread())
|
||||
{
|
||||
DebugMsg("%s should only be called on the realtime worker thread.", inCallerMethodName);
|
||||
__ASSERT_STOP; // TODO: Figure out a better way to assert with a formatted message
|
||||
}
|
||||
|
||||
Assert(mRealTimeThread.IsTimeConstraintThread(), "mRealTimeThread should be in a time-constraint priority band.");
|
||||
#else
|
||||
#pragma unused (inCallerMethodName)
|
||||
#endif
|
||||
}
|
||||
|
||||
//static
|
||||
void* __nullable VC_TaskQueue::RealTimeThreadProc(void* inRefCon)
|
||||
{
|
||||
DebugMsg("VC_TaskQueue::RealTimeThreadProc: The realtime worker thread has started");
|
||||
|
||||
VC_TaskQueue* refCon = static_cast<VC_TaskQueue*>(inRefCon);
|
||||
refCon->WorkerThreadProc(refCon->mRealTimeThreadWorkQueuedSemaphore,
|
||||
refCon->mRealTimeThreadSyncTaskCompletedSemaphore,
|
||||
&refCon->mRealTimeThreadTasks,
|
||||
NULL,
|
||||
[&] (VC_Task* inTask) { return refCon->ProcessRealTimeThreadTask(inTask); });
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//static
|
||||
void* __nullable VC_TaskQueue::NonRealTimeThreadProc(void* inRefCon)
|
||||
{
|
||||
DebugMsg("VC_TaskQueue::NonRealTimeThreadProc: The non-realtime worker thread has started");
|
||||
|
||||
VC_TaskQueue* refCon = static_cast<VC_TaskQueue*>(inRefCon);
|
||||
refCon->WorkerThreadProc(refCon->mNonRealTimeThreadWorkQueuedSemaphore,
|
||||
refCon->mNonRealTimeThreadSyncTaskCompletedSemaphore,
|
||||
&refCon->mNonRealTimeThreadTasks,
|
||||
&refCon->mNonRealTimeThreadTasksFreeList,
|
||||
[&] (VC_Task* inTask) { return refCon->ProcessNonRealTimeThreadTask(inTask); });
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void VC_TaskQueue::WorkerThreadProc(semaphore_t inWorkQueuedSemaphore, semaphore_t inSyncTaskCompletedSemaphore, TAtomicStack<VC_Task>* inTasks, TAtomicStack2<VC_Task>* __nullable inFreeList, std::function<bool(VC_Task*)> inProcessTask)
|
||||
{
|
||||
bool theThreadShouldStop = false;
|
||||
|
||||
while(!theThreadShouldStop)
|
||||
{
|
||||
// Wait until a thread signals that it's added tasks to the queue.
|
||||
//
|
||||
// Note that we don't have to hold any lock before waiting. If the semaphore is signalled before we begin waiting we'll
|
||||
// still get the signal after we do.
|
||||
kern_return_t theError = semaphore_wait(inWorkQueuedSemaphore);
|
||||
VC_Utils::ThrowIfMachError("VC_TaskQueue::WorkerThreadProc", "semaphore_wait", theError);
|
||||
|
||||
// Fetch the tasks from the queue.
|
||||
//
|
||||
// The tasks need to be processed in the order they were added to the queue. Since pop_all_reversed is atomic, other threads
|
||||
// can't add new tasks while we're reading, which would mix up the order.
|
||||
VC_Task* theTask = inTasks->pop_all_reversed();
|
||||
|
||||
while(theTask != NULL &&
|
||||
!theThreadShouldStop) // Stop processing tasks if we're shutting down
|
||||
{
|
||||
VC_Task* theNextTask = theTask->mNext;
|
||||
|
||||
VCAssert(!theTask->IsComplete(),
|
||||
"VC_TaskQueue::WorkerThreadProc: Cannot process already completed task (ID %d)",
|
||||
theTask->GetTaskID());
|
||||
|
||||
VCAssert(theTask != theNextTask,
|
||||
"VC_TaskQueue::WorkerThreadProc: VC_Task %p (ID %d) was added to %s multiple times. arg1=%llu arg2=%llu",
|
||||
theTask,
|
||||
theTask->GetTaskID(),
|
||||
(inTasks == &mRealTimeThreadTasks ? "mRealTimeThreadTasks" : "mNonRealTimeThreadTasks"),
|
||||
theTask->GetArg1(),
|
||||
theTask->GetArg2());
|
||||
|
||||
// Process the task
|
||||
theThreadShouldStop = inProcessTask(theTask);
|
||||
|
||||
// If the task was queued synchronously, let the thread that queued it know we're finished
|
||||
if(theTask->IsSync())
|
||||
{
|
||||
// Marking the task as completed allows QueueSync to return, which means it's possible for theTask to point to
|
||||
// invalid memory after this point.
|
||||
CAMemoryBarrier();
|
||||
theTask->MarkCompleted();
|
||||
|
||||
// Signal any threads waiting for their task to be processed.
|
||||
//
|
||||
// We use semaphore_signal_all instead of semaphore_signal to avoid a race condition in QueueSync. It's possible
|
||||
// for threads calling QueueSync to wait on the semaphore in an order different to the order of the tasks they just
|
||||
// added to the queue. So after each task is completed we have every waiting thread check if it was theirs.
|
||||
//
|
||||
// Note that semaphore_signal_all has an implicit barrier.
|
||||
theError = semaphore_signal_all(inSyncTaskCompletedSemaphore);
|
||||
VC_Utils::ThrowIfMachError("VC_TaskQueue::WorkerThreadProc", "semaphore_signal_all", theError);
|
||||
}
|
||||
else if(inFreeList != NULL)
|
||||
{
|
||||
// After completing an async task, move it to the free list so the memory can be reused
|
||||
inFreeList->push_atomic(theTask);
|
||||
}
|
||||
|
||||
theTask = theNextTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool VC_TaskQueue::ProcessRealTimeThreadTask(VC_Task* inTask)
|
||||
{
|
||||
AssertCurrentThreadIsRTWorkerThread("VC_TaskQueue::ProcessRealTimeThreadTask");
|
||||
|
||||
switch(inTask->GetTaskID())
|
||||
{
|
||||
case kVCTaskStopWorkerThread:
|
||||
DebugMsg("VC_TaskQueue::ProcessRealTimeThreadTask: Stopping");
|
||||
// Return that the thread should stop itself
|
||||
return true;
|
||||
|
||||
default:
|
||||
Assert(false, "VC_TaskQueue::ProcessRealTimeThreadTask: Unexpected task ID");
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VC_TaskQueue::ProcessNonRealTimeThreadTask(VC_Task* inTask)
|
||||
{
|
||||
#if DEBUG // This Assert macro always checks the condition, if for some reason the compiler doesn't optimise it away, even in release builds
|
||||
Assert(mNonRealTimeThread.IsCurrentThread(), "ProcessNonRealTimeThreadTask should only be called on the non-realtime worker thread.");
|
||||
Assert(mNonRealTimeThread.IsTimeShareThread(), "mNonRealTimeThread should not be in a time-constraint priority band.");
|
||||
#endif
|
||||
|
||||
switch(inTask->GetTaskID())
|
||||
{
|
||||
case kVCTaskStopWorkerThread:
|
||||
DebugMsg("VC_TaskQueue::ProcessNonRealTimeThreadTask: Stopping");
|
||||
// Return that the thread should stop itself
|
||||
return true;
|
||||
|
||||
case kVCTaskSendPropertyNotification:
|
||||
DebugMsg("VC_TaskQueue::ProcessNonRealTimeThreadTask: Processing kVCTaskSendPropertyNotification");
|
||||
{
|
||||
AudioObjectPropertyAddress thePropertyAddress[] = {
|
||||
{ static_cast<UInt32>(inTask->GetArg1()), kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster } };
|
||||
VC_PlugIn::Host_PropertiesChanged(static_cast<AudioObjectID>(inTask->GetArg2()), 1, thePropertyAddress);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Assert(false, "VC_TaskQueue::ProcessNonRealTimeThreadTask: Unexpected task ID");
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
180
driver/VC_TaskQueue.h
Normal file
180
driver/VC_TaskQueue.h
Normal file
@@ -0,0 +1,180 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_TaskQueue.h
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
#ifndef __VCDriver__VC_TaskQueue__
|
||||
#define __VCDriver__VC_TaskQueue__
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAPThread.h"
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion"
|
||||
#include "CAAtomicStack.h"
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// STL Includes
|
||||
#include <functional>
|
||||
|
||||
// System Includes
|
||||
#include <mach/semaphore.h>
|
||||
#include <CoreAudio/AudioHardware.h>
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
//==================================================================================================
|
||||
// VC_TaskQueue
|
||||
//
|
||||
// This class has two worker threads, one with real-time priority and one with default priority,
|
||||
// that tasks can be dispatched to. The two main use cases are dispatching work from a real-time
|
||||
// thread to be done async, and dispatching work from a non-real-time thread that needs to run on
|
||||
// a real-time thread to avoid priority inversions.
|
||||
//==================================================================================================
|
||||
|
||||
class VC_TaskQueue
|
||||
{
|
||||
|
||||
private:
|
||||
enum VC_TaskID {
|
||||
kVCTaskUninitialized,
|
||||
kVCTaskStopWorkerThread,
|
||||
|
||||
// Non-realtime thread only
|
||||
kVCTaskSendPropertyNotification
|
||||
};
|
||||
|
||||
class VC_Task
|
||||
{
|
||||
public:
|
||||
VC_Task(VC_TaskID inTaskID = kVCTaskUninitialized, bool inIsSync = false, UInt64 inArg1 = 0, UInt64 inArg2 = 0) : mNext(NULL), mTaskID(inTaskID), mIsSync(inIsSync), mArg1(inArg1), mArg2(inArg2) { };
|
||||
|
||||
VC_TaskID GetTaskID() { return mTaskID; }
|
||||
|
||||
// True if the thread that queued this task is blocking until the task is completed
|
||||
bool IsSync() { return mIsSync; }
|
||||
|
||||
UInt64 GetArg1() { return mArg1; }
|
||||
UInt64 GetArg2() { return mArg2; }
|
||||
|
||||
UInt64 GetReturnValue() { return mReturnValue; }
|
||||
void SetReturnValue(UInt64 inReturnValue) { mReturnValue = inReturnValue; }
|
||||
|
||||
bool IsComplete() { return mIsComplete; }
|
||||
void MarkCompleted() { mIsComplete = true; }
|
||||
|
||||
// Used by TAtomicStack
|
||||
VC_Task* __nullable & next() { return mNext; }
|
||||
VC_Task* __nullable mNext;
|
||||
|
||||
private:
|
||||
VC_TaskID mTaskID;
|
||||
bool mIsSync;
|
||||
UInt64 mArg1;
|
||||
UInt64 mArg2;
|
||||
UInt64 mReturnValue = INT64_MAX;
|
||||
bool mIsComplete = false;
|
||||
};
|
||||
|
||||
public:
|
||||
VC_TaskQueue();
|
||||
~VC_TaskQueue();
|
||||
// Disallow copying
|
||||
VC_TaskQueue(const VC_TaskQueue&) = delete;
|
||||
VC_TaskQueue& operator=(const VC_TaskQueue&) = delete;
|
||||
|
||||
private:
|
||||
static UInt32 NanosToAbsoluteTime(UInt32 inNanos);
|
||||
|
||||
public:
|
||||
// Sends a property changed notification to the VCDevice host. Assumes the scope and element are kAudioObjectPropertyScopeGlobal and
|
||||
// kAudioObjectPropertyElementMaster because currently those are the only ones we use.
|
||||
void QueueAsync_SendPropertyNotification(AudioObjectPropertySelector inProperty, AudioObjectID inDeviceID);
|
||||
|
||||
private:
|
||||
UInt64 QueueSync(VC_TaskID inTaskID, bool inRunOnRealtimeThread, UInt64 inTaskArg1 = 0, UInt64 inTaskArg2 = 0);
|
||||
|
||||
void QueueOnNonRealtimeThread(VC_Task inTask);
|
||||
|
||||
public:
|
||||
void AssertCurrentThreadIsRTWorkerThread(const char* inCallerMethodName);
|
||||
|
||||
private:
|
||||
static void* __nullable RealTimeThreadProc(void* inRefCon);
|
||||
static void* __nullable NonRealTimeThreadProc(void* inRefCon);
|
||||
|
||||
void WorkerThreadProc(semaphore_t inWorkQueuedSemaphore, semaphore_t inSyncTaskCompletedSemaphore, TAtomicStack<VC_Task>* inTasks, TAtomicStack2<VC_Task>* __nullable inFreeList, std::function<bool(VC_Task*)> inProcessTask);
|
||||
|
||||
// These return true when the thread should be stopped
|
||||
bool ProcessRealTimeThreadTask(VC_Task* inTask);
|
||||
bool ProcessNonRealTimeThreadTask(VC_Task* inTask);
|
||||
|
||||
private:
|
||||
// The worker threads that perform the queued tasks
|
||||
CAPThread mRealTimeThread;
|
||||
CAPThread mNonRealTimeThread;
|
||||
|
||||
// The approximate amount of time we'll need whenever our real-time thread is scheduled. This is currently just
|
||||
// set to the minimum (see sched_prim.c) because our real-time tasks do very little work.
|
||||
//
|
||||
// TODO: Would it be better to specify these in absolute time, which would make them relative to the system's bus
|
||||
// speed? Or even calculate them from the system's CPU/RAM speed? Note that none of our tasks actually have
|
||||
// a deadline (though that might change). They just have to run with real-time priority to avoid causing
|
||||
// priority inversions on the IO thread.
|
||||
static const UInt32 kRealTimeThreadNominalComputationNs = 50 * NSEC_PER_USEC;
|
||||
// The maximum amount of time the real-time thread can take to finish its computation after being scheduled.
|
||||
static const UInt32 kRealTimeThreadMaximumComputationNs = 60 * NSEC_PER_USEC;
|
||||
|
||||
// We use Mach semaphores for communication with the worker threads because signalling them is real-time safe.
|
||||
|
||||
// Signalled to tell the worker threads when there are tasks for them process.
|
||||
semaphore_t mRealTimeThreadWorkQueuedSemaphore;
|
||||
semaphore_t mNonRealTimeThreadWorkQueuedSemaphore;
|
||||
// Signalled when a worker thread completes a task, if the thread that queued that task is blocking on it.
|
||||
semaphore_t mRealTimeThreadSyncTaskCompletedSemaphore;
|
||||
semaphore_t mNonRealTimeThreadSyncTaskCompletedSemaphore;
|
||||
|
||||
// When a task is queued we add it to one of these, depending on which worker thread it will run on. Using
|
||||
// TAtomicStack lets us safely add and remove tasks on real-time threads.
|
||||
//
|
||||
// We use TAtomicStack rather than TAtomicStack2 because we need pop_all_reversed() to make sure we process the
|
||||
// tasks in order. (It might have been better to use OSAtomicFifoEnqueue/OSAtomicFifoDequeue, but I only
|
||||
// recently found out about them.)
|
||||
TAtomicStack<VC_Task> mRealTimeThreadTasks;
|
||||
TAtomicStack<VC_Task> mNonRealTimeThreadTasks;
|
||||
|
||||
// The number of tasks to pre-allocate and add to the non-realtime task free list. Should be large enough that
|
||||
// the free list is never emptied. (At least not while IO could be running.)
|
||||
static const UInt32 kNonRealTimeThreadTaskBufferSize = 512;
|
||||
// Realtime threads can't safely allocate memory, so when they queue a task the memory for it comes from this
|
||||
// free list. We pre-allocate as many tasks as they should ever need in the constructor. (But if the free list
|
||||
// runs out of tasks somehow the realtime thread will allocate a new one.)
|
||||
//
|
||||
// There's a similar free list used in Apple's CAThreadSafeList.h.
|
||||
//
|
||||
// We can use TAtomicStack2 instead of TAtomicStack because we never call pop_all on the free list.
|
||||
TAtomicStack2<VC_Task> mNonRealTimeThreadTasksFreeList;
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* __VCDriver__VC_TaskQueue__ */
|
||||
|
||||
462
driver/VC_VolumeControl.cpp
Normal file
462
driver/VC_VolumeControl.cpp
Normal file
@@ -0,0 +1,462 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_VolumeControl.cpp
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016, 2017 Kyle Neideck
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "VC_VolumeControl.h"
|
||||
|
||||
// Local Includes
|
||||
#include "VC_PlugIn.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAException.h"
|
||||
#include "CADebugMacros.h"
|
||||
#include "CADispatchQueue.h"
|
||||
#include "VC_Utils.h"
|
||||
|
||||
// STL Includes
|
||||
#include <algorithm>
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioHardwareBase.h>
|
||||
#include <Accelerate/Accelerate.h>
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
VC_VolumeControl::VC_VolumeControl(AudioObjectID inObjectID,
|
||||
AudioObjectID inOwnerObjectID,
|
||||
AudioObjectPropertyScope inScope,
|
||||
AudioObjectPropertyElement inElement)
|
||||
:
|
||||
VC_Control(inObjectID,
|
||||
kAudioVolumeControlClassID,
|
||||
kAudioLevelControlClassID,
|
||||
inOwnerObjectID,
|
||||
inScope,
|
||||
inElement),
|
||||
mMutex("Volume Control"),
|
||||
mVolumeRaw(kDefaultMinRawVolume),
|
||||
mAmplitudeGain(0.0f),
|
||||
mMinVolumeRaw(kDefaultMinRawVolume),
|
||||
mMaxVolumeRaw(kDefaultMaxRawVolume),
|
||||
mMinVolumeDb(kDefaultMinDbVolume),
|
||||
mMaxVolumeDb(kDefaultMaxDbVolume),
|
||||
mWillApplyVolumeToAudio(false)
|
||||
{
|
||||
// Setup the volume curve with the one range
|
||||
mVolumeCurve.AddRange(mMinVolumeRaw, mMaxVolumeRaw, mMinVolumeDb, mMaxVolumeDb);
|
||||
}
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
bool VC_VolumeControl::HasProperty(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
bool theAnswer = false;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioLevelControlPropertyScalarValue:
|
||||
case kAudioLevelControlPropertyDecibelValue:
|
||||
case kAudioLevelControlPropertyDecibelRange:
|
||||
case kAudioLevelControlPropertyConvertScalarToDecibels:
|
||||
case kAudioLevelControlPropertyConvertDecibelsToScalar:
|
||||
theAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Control::HasProperty(inObjectID, inClientPID, inAddress);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool VC_VolumeControl::IsPropertySettable(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
bool theAnswer = false;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioLevelControlPropertyDecibelRange:
|
||||
case kAudioLevelControlPropertyConvertScalarToDecibels:
|
||||
case kAudioLevelControlPropertyConvertDecibelsToScalar:
|
||||
theAnswer = false;
|
||||
break;
|
||||
|
||||
case kAudioLevelControlPropertyScalarValue:
|
||||
case kAudioLevelControlPropertyDecibelValue:
|
||||
theAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Control::IsPropertySettable(inObjectID, inClientPID, inAddress);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
UInt32 VC_VolumeControl::GetPropertyDataSize(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
UInt32 theAnswer = 0;
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioLevelControlPropertyScalarValue:
|
||||
theAnswer = sizeof(Float32);
|
||||
break;
|
||||
|
||||
case kAudioLevelControlPropertyDecibelValue:
|
||||
theAnswer = sizeof(Float32);
|
||||
break;
|
||||
|
||||
case kAudioLevelControlPropertyDecibelRange:
|
||||
theAnswer = sizeof(AudioValueRange);
|
||||
break;
|
||||
|
||||
case kAudioLevelControlPropertyConvertScalarToDecibels:
|
||||
theAnswer = sizeof(Float32);
|
||||
break;
|
||||
|
||||
case kAudioLevelControlPropertyConvertDecibelsToScalar:
|
||||
theAnswer = sizeof(Float32);
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = VC_Control::GetPropertyDataSize(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData);
|
||||
break;
|
||||
};
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void VC_VolumeControl::GetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
UInt32& outDataSize,
|
||||
void* outData) const
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioLevelControlPropertyScalarValue:
|
||||
// This returns the value of the control in the normalized range of 0 to 1.
|
||||
{
|
||||
ThrowIf(inDataSize < sizeof(Float32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_VolumeControl::GetPropertyData: not enough space for the return value "
|
||||
"of kAudioLevelControlPropertyScalarValue for the volume control");
|
||||
|
||||
CAMutex::Locker theLocker(mMutex);
|
||||
|
||||
*reinterpret_cast<Float32*>(outData) = mVolumeCurve.ConvertRawToScalar(mVolumeRaw);
|
||||
outDataSize = sizeof(Float32);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioLevelControlPropertyDecibelValue:
|
||||
// This returns the dB value of the control.
|
||||
{
|
||||
ThrowIf(inDataSize < sizeof(Float32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_VolumeControl::GetPropertyData: not enough space for the return value "
|
||||
"of kAudioLevelControlPropertyDecibelValue for the volume control");
|
||||
|
||||
CAMutex::Locker theLocker(mMutex);
|
||||
|
||||
*reinterpret_cast<Float32*>(outData) = mVolumeCurve.ConvertRawToDB(mVolumeRaw);
|
||||
outDataSize = sizeof(Float32);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioLevelControlPropertyDecibelRange:
|
||||
// This returns the dB range of the control.
|
||||
ThrowIf(inDataSize < sizeof(AudioValueRange),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_VolumeControl::GetPropertyData: not enough space for the return value of "
|
||||
"kAudioLevelControlPropertyDecibelRange for the volume control");
|
||||
reinterpret_cast<AudioValueRange*>(outData)->mMinimum = mVolumeCurve.GetMinimumDB();
|
||||
reinterpret_cast<AudioValueRange*>(outData)->mMaximum = mVolumeCurve.GetMaximumDB();
|
||||
outDataSize = sizeof(AudioValueRange);
|
||||
break;
|
||||
|
||||
case kAudioLevelControlPropertyConvertScalarToDecibels:
|
||||
// This takes the scalar value in outData and converts it to dB.
|
||||
{
|
||||
ThrowIf(inDataSize < sizeof(Float32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_VolumeControl::GetPropertyData: not enough space for the return value "
|
||||
"of kAudioLevelControlPropertyConvertScalarToDecibels for the volume "
|
||||
"control");
|
||||
|
||||
// clamp the value to be between 0 and 1
|
||||
Float32 theVolumeValue = *reinterpret_cast<Float32*>(outData);
|
||||
theVolumeValue = std::min(1.0f, std::max(0.0f, theVolumeValue));
|
||||
|
||||
// do the conversion
|
||||
*reinterpret_cast<Float32*>(outData) =
|
||||
mVolumeCurve.ConvertScalarToDB(theVolumeValue);
|
||||
|
||||
// report how much we wrote
|
||||
outDataSize = sizeof(Float32);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioLevelControlPropertyConvertDecibelsToScalar:
|
||||
// This takes the dB value in outData and converts it to scalar.
|
||||
{
|
||||
ThrowIf(inDataSize < sizeof(Float32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_VolumeControl::GetPropertyData: not enough space for the return value "
|
||||
"of kAudioLevelControlPropertyConvertDecibelsToScalar for the volume "
|
||||
"control");
|
||||
|
||||
// clamp the value to be between mMinVolumeDb and mMaxVolumeDb
|
||||
Float32 theVolumeValue = *reinterpret_cast<Float32*>(outData);
|
||||
theVolumeValue = std::min(mMaxVolumeDb, std::max(mMinVolumeDb, theVolumeValue));
|
||||
|
||||
// do the conversion
|
||||
*reinterpret_cast<Float32*>(outData) =
|
||||
mVolumeCurve.ConvertDBToScalar(theVolumeValue);
|
||||
|
||||
// report how much we wrote
|
||||
outDataSize = sizeof(Float32);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
VC_Control::GetPropertyData(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData,
|
||||
inDataSize,
|
||||
outDataSize,
|
||||
outData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void VC_VolumeControl::SetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
const void* inData)
|
||||
{
|
||||
CheckObjectID(inObjectID);
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioLevelControlPropertyScalarValue:
|
||||
{
|
||||
ThrowIf(inDataSize != sizeof(Float32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_VolumeControl::SetPropertyData: wrong size for the data for "
|
||||
"kAudioLevelControlPropertyScalarValue");
|
||||
|
||||
// Read the new scalar volume.
|
||||
Float32 theNewVolumeScalar = *reinterpret_cast<const Float32*>(inData);
|
||||
SetVolumeScalar(theNewVolumeScalar);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioLevelControlPropertyDecibelValue:
|
||||
{
|
||||
ThrowIf(inDataSize != sizeof(Float32),
|
||||
CAException(kAudioHardwareBadPropertySizeError),
|
||||
"VC_VolumeControl::SetPropertyData: wrong size for the data for "
|
||||
"kAudioLevelControlPropertyDecibelValue");
|
||||
|
||||
// Read the new volume in dB.
|
||||
Float32 theNewVolumeDb = *reinterpret_cast<const Float32*>(inData);
|
||||
SetVolumeDb(theNewVolumeDb);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
VC_Control::SetPropertyData(inObjectID,
|
||||
inClientPID,
|
||||
inAddress,
|
||||
inQualifierDataSize,
|
||||
inQualifierData,
|
||||
inDataSize,
|
||||
inData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
#pragma mark Accessors
|
||||
|
||||
void VC_VolumeControl::SetVolumeScalar(Float32 inNewVolumeScalar)
|
||||
{
|
||||
// For the scalar volume, we clamp the new value to [0, 1]. Note that if this value changes, it
|
||||
// implies that the dB value changes too.
|
||||
inNewVolumeScalar = std::min(1.0f, std::max(0.0f, inNewVolumeScalar));
|
||||
|
||||
// Store the new volume.
|
||||
SInt32 theNewVolumeRaw = mVolumeCurve.ConvertScalarToRaw(inNewVolumeScalar);
|
||||
SetVolumeRaw(theNewVolumeRaw);
|
||||
}
|
||||
|
||||
void VC_VolumeControl::SetVolumeDb(Float32 inNewVolumeDb)
|
||||
{
|
||||
// For the dB value, we first convert it to a raw value since that is how the value is tracked.
|
||||
// Note that if this value changes, it implies that the scalar value changes as well.
|
||||
|
||||
// Clamp the new volume.
|
||||
inNewVolumeDb = std::min(mMaxVolumeDb, std::max(mMinVolumeDb, inNewVolumeDb));
|
||||
|
||||
// Store the new volume.
|
||||
SInt32 theNewVolumeRaw = mVolumeCurve.ConvertDBToRaw(inNewVolumeDb);
|
||||
SetVolumeRaw(theNewVolumeRaw);
|
||||
}
|
||||
|
||||
void VC_VolumeControl::SetWillApplyVolumeToAudio(bool inWillApplyVolumeToAudio)
|
||||
{
|
||||
mWillApplyVolumeToAudio = inWillApplyVolumeToAudio;
|
||||
}
|
||||
|
||||
#pragma mark IO Operations
|
||||
|
||||
bool VC_VolumeControl::WillApplyVolumeToAudioRT() const
|
||||
{
|
||||
return mWillApplyVolumeToAudio;
|
||||
}
|
||||
|
||||
void VC_VolumeControl::ApplyVolumeToAudioRT(Float32* ioBuffer, UInt32 inBufferFrameSize) const
|
||||
{
|
||||
ThrowIf(!mWillApplyVolumeToAudio,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_VolumeControl::ApplyVolumeToAudioRT: This control doesn't process audio data");
|
||||
|
||||
// Don't bother if the change is very unlikely to be perceptible.
|
||||
if((mAmplitudeGain < 0.99f) || (mAmplitudeGain > 1.01f))
|
||||
{
|
||||
// Apply the amount of gain/loss for the current volume to the audio signal by multiplying
|
||||
// each sample. This call to vDSP_vsmul is equivalent to
|
||||
//
|
||||
// for(UInt32 i = 0; i < inBufferFrameSize * 2; i++)
|
||||
// {
|
||||
// ioBuffer[i] *= mAmplitudeGain;
|
||||
// }
|
||||
//
|
||||
// but a bit faster on processors with newer SIMD instructions. However, it shouldn't take
|
||||
// more than a few microseconds either way. (Unless some of the samples were subnormal
|
||||
// numbers for some reason.)
|
||||
//
|
||||
// It would be a tiny bit faster still to not do this in-place, i.e. use separate input and
|
||||
// output buffers, but then we'd have to copy the data into the output buffer when the
|
||||
// volume is at 1.0. With our current use of this class, most people will leave the volume
|
||||
// at 1.0, so it wouldn't be worth it.
|
||||
vDSP_vsmul(ioBuffer, 1, &mAmplitudeGain, ioBuffer, 1, inBufferFrameSize * 2);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
void VC_VolumeControl::SetVolumeRaw(SInt32 inNewVolumeRaw)
|
||||
{
|
||||
CAMutex::Locker theLocker(mMutex);
|
||||
|
||||
// Make sure the new raw value is in the proper range.
|
||||
inNewVolumeRaw = std::min(std::max(mMinVolumeRaw, inNewVolumeRaw), mMaxVolumeRaw);
|
||||
|
||||
// Store the new volume.
|
||||
if(mVolumeRaw != inNewVolumeRaw)
|
||||
{
|
||||
mVolumeRaw = inNewVolumeRaw;
|
||||
|
||||
// CAVolumeCurve deals with volumes in three different scales: scalar, dB and raw. Raw
|
||||
// volumes are the number of steps along the dB curve, so dB and raw volumes are linearly
|
||||
// related.
|
||||
//
|
||||
// macOS uses the scalar volume to set the position of its volume sliders for the
|
||||
// device. We have to set the scalar volume to the position of our volume slider for a
|
||||
// device (more specifically, a linear mapping of it onto [0,1]) or macOS's volume sliders
|
||||
// or it will work differently to our own.
|
||||
//
|
||||
// When we set a new slider position as the device's scalar volume, we convert it to raw
|
||||
// with CAVolumeCurve::ConvertScalarToRaw, which will "undo the curve". However, we haven't
|
||||
// applied the curve at that point.
|
||||
//
|
||||
// So, to actually apply the curve, we use CAVolumeCurve::ConvertRawToScalar to get the
|
||||
// linear slider position back, map it onto the range of raw volumes and use
|
||||
// CAVolumeCurve::ConvertRawToScalar again to apply the curve.
|
||||
//
|
||||
// It might be that we should be using CAVolumeCurve with transfer functions x^n where
|
||||
// 0 < n < 1, but a lot more of the transfer functions it supports have n >= 1, including
|
||||
// the default one. So I'm a bit confused.
|
||||
//
|
||||
// TODO: I think this means the dB volume we report will be wrong. It also makes the code
|
||||
// pretty confusing.
|
||||
Float32 theSliderPosition = mVolumeCurve.ConvertRawToScalar(mVolumeRaw);
|
||||
|
||||
// TODO: This assumes the control should never boost the signal. (So, technically, it never
|
||||
// actually applies gain, only loss.)
|
||||
SInt32 theRawRange = mMaxVolumeRaw - mMinVolumeRaw;
|
||||
SInt32 theSliderPositionInRawSteps = static_cast<SInt32>(theSliderPosition * theRawRange);
|
||||
theSliderPositionInRawSteps += mMinVolumeRaw;
|
||||
|
||||
mAmplitudeGain = mVolumeCurve.ConvertRawToScalar(theSliderPositionInRawSteps);
|
||||
|
||||
VCAssert((mAmplitudeGain >= 0.0f) && (mAmplitudeGain <= 1.0f), "Gain not in [0,1]");
|
||||
|
||||
// Send notifications.
|
||||
CADispatchQueue::GetGlobalSerialQueue().Dispatch(false, ^{
|
||||
AudioObjectPropertyAddress theChangedProperties[2];
|
||||
theChangedProperties[0] = { kAudioLevelControlPropertyScalarValue, mScope, mElement };
|
||||
theChangedProperties[1] = { kAudioLevelControlPropertyDecibelValue, mScope, mElement };
|
||||
|
||||
VC_PlugIn::Host_PropertiesChanged(GetObjectID(), 2, theChangedProperties);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
168
driver/VC_VolumeControl.h
Normal file
168
driver/VC_VolumeControl.h
Normal file
@@ -0,0 +1,168 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_VolumeControl.h
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2017 Kyle Neideck
|
||||
//
|
||||
|
||||
#ifndef VCDriver__VC_VolumeControl
|
||||
#define VCDriver__VC_VolumeControl
|
||||
|
||||
// Superclass Includes
|
||||
#include "VC_Control.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAVolumeCurve.h"
|
||||
#include "CAMutex.h"
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
class VC_VolumeControl
|
||||
:
|
||||
public VC_Control
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
public:
|
||||
VC_VolumeControl(AudioObjectID inObjectID,
|
||||
AudioObjectID inOwnerObjectID,
|
||||
AudioObjectPropertyScope inScope =
|
||||
kAudioObjectPropertyScopeOutput,
|
||||
AudioObjectPropertyElement inElement =
|
||||
kAudioObjectPropertyElementMaster);
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
virtual bool HasProperty(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual bool IsPropertySettable(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData) const;
|
||||
virtual void GetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
UInt32& outDataSize,
|
||||
void* outData) const;
|
||||
virtual void SetPropertyData(AudioObjectID inObjectID,
|
||||
pid_t inClientPID,
|
||||
const AudioObjectPropertyAddress& inAddress,
|
||||
UInt32 inQualifierDataSize,
|
||||
const void* inQualifierData,
|
||||
UInt32 inDataSize,
|
||||
const void* inData);
|
||||
|
||||
#pragma mark Accessors
|
||||
|
||||
/*!
|
||||
@return The curve used by this control to convert volume values from scalar into signal gain
|
||||
and/or decibels. A continuous 2D function.
|
||||
*/
|
||||
CAVolumeCurve& GetVolumeCurve() { return mVolumeCurve; }
|
||||
|
||||
/*!
|
||||
Set the volume of this control to a given position along its volume curve. (See
|
||||
GetVolumeCurve.)
|
||||
|
||||
Passing 1.0 sets the volume to the maximum and 0.0 sets it to the minimum. The gain/loss the
|
||||
control applies (and/or reports to apply) to the audio it controls is given by the y-position
|
||||
of the curve at the x-position inNewVolumeScalar.
|
||||
|
||||
In general, since the control's volume curve will be applied to the given value, it should be
|
||||
linearly related to a volume input by the user.
|
||||
|
||||
@param inNewVolumeScalar The volume to set. Will be clamped to [0.0, 1.0].
|
||||
*/
|
||||
void SetVolumeScalar(Float32 inNewVolumeScalar);
|
||||
/*!
|
||||
Set the volume of this control in decibels.
|
||||
|
||||
@param inNewVolumeDb The volume to set. Will be clamped to the minimum/maximum dB volumes of
|
||||
the control. See GetVolumeCurve.
|
||||
*/
|
||||
void SetVolumeDb(Float32 inNewVolumeDb);
|
||||
|
||||
/*!
|
||||
Set this volume control to apply its volume to audio data, which allows clients to call
|
||||
ApplyVolumeToAudioRT. When this is set true, WillApplyVolumeToAudioRT will return true. Set to
|
||||
false initially.
|
||||
*/
|
||||
void SetWillApplyVolumeToAudio(bool inWillApplyVolumeToAudio);
|
||||
|
||||
#pragma mark IO Operations
|
||||
|
||||
/*!
|
||||
@return True if clients should use ApplyVolumeToAudioRT to apply this volume control's volume
|
||||
to their audio data while doing IO.
|
||||
*/
|
||||
bool WillApplyVolumeToAudioRT() const;
|
||||
/*!
|
||||
Apply this volume control's volume to the samples in ioBuffer. That is, increase/decrease the
|
||||
volumes of the samples by the current volume of this control.
|
||||
|
||||
@param ioBuffer The audio sample buffer to process.
|
||||
@param inBufferFrameSize The number of sample frames in ioBuffer. The audio is assumed to be in
|
||||
stereo, i.e. two samples per frame. (Though, hopefully we'll support
|
||||
more at some point.)
|
||||
@throws CAException If SetWillApplyVolumeToAudio hasn't been used to set this control to apply
|
||||
its volume to audio data.
|
||||
*/
|
||||
void ApplyVolumeToAudioRT(Float32* ioBuffer, UInt32 inBufferFrameSize) const;
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
protected:
|
||||
void SetVolumeRaw(SInt32 inNewVolumeRaw);
|
||||
|
||||
private:
|
||||
const SInt32 kDefaultMinRawVolume = 0;
|
||||
const SInt32 kDefaultMaxRawVolume = 96;
|
||||
const Float32 kDefaultMinDbVolume = -96.0f;
|
||||
const Float32 kDefaultMaxDbVolume = 0.0f;
|
||||
|
||||
CAMutex mMutex;
|
||||
|
||||
SInt32 mVolumeRaw;
|
||||
SInt32 mMinVolumeRaw;
|
||||
SInt32 mMaxVolumeRaw;
|
||||
Float32 mMinVolumeDb;
|
||||
Float32 mMaxVolumeDb;
|
||||
|
||||
CAVolumeCurve mVolumeCurve;
|
||||
// The gain (or loss) to apply to an audio signal to increase/decrease its volume by the current
|
||||
// volume of this control.
|
||||
Float32 mAmplitudeGain;
|
||||
|
||||
bool mWillApplyVolumeToAudio;
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* VCDriver__VC_VolumeControl */
|
||||
|
||||
Reference in New Issue
Block a user