Initial commit

This commit is contained in:
2026-03-15 06:02:56 +09:00
commit 010c235e46
113 changed files with 28943 additions and 0 deletions

24
app/Info.plist Normal file
View File

@@ -0,0 +1,24 @@
<?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>CFBundleExecutable</key>
<string>VolumeControl</string>
<key>CFBundleIdentifier</key>
<string>com.volumecontrol.App</string>
<key>CFBundleName</key>
<string>VolumeControl</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>LSUIElement</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>10.14</string>
<key>NSMicrophoneUsageDescription</key>
<string>VolumeControl reads system audio through a virtual input device to provide software volume control for HDMI and other outputs.</string>
</dict>
</plist>

View File

@@ -0,0 +1,318 @@
/*
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__

View 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

View 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

View 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;
}

View 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

View 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;
}

View 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__

View 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);
}

View 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

View File

@@ -0,0 +1,110 @@
/*
File: CACFString.cpp
Abstract: CACFString.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 "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;
}
}
}

View File

@@ -0,0 +1,196 @@
/*
File: CACFString.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(__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) {}
explicit CACFString(CFStringRef inCFString) : mCFString(inCFString), mWillRelease(true) {}
CACFString(const char* inCString) : mCFString(CFStringCreateWithCString(NULL, inCString, kCFStringEncodingASCII)), mWillRelease(true) {}
CACFString(CFStringRef inCFString, bool inWillRelease) : mCFString(inCFString), mWillRelease(inWillRelease) {}
CACFString(const char* inCString, bool inWillRelease) : 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)); }
inline bool operator<(const CACFString& x, CFStringRef y) { return CFStringCompare(x.GetCFString(), y, 0) == kCFCompareLessThan; }
inline bool operator==(const CACFString& x, CFStringRef y) { return CFStringCompare(x.GetCFString(), y, 0) == kCFCompareEqualTo; }
inline bool operator!=(const CACFString& x, CFStringRef y) { return !(x == y); }
inline bool operator<=(const CACFString& x, CFStringRef y) { return (x < y) || (x == y); }
inline bool operator>=(const CACFString& x, CFStringRef y) { return !(x < y); }
inline bool operator>(const CACFString& x, CFStringRef y) { return !((x < y) || (x == y)); }
inline bool operator<(CFStringRef x, const CACFString& y) { return CFStringCompare(x, y.GetCFString(), 0) == kCFCompareLessThan; }
inline bool operator==(CFStringRef x, const CACFString& y) { return CFStringCompare(x, y.GetCFString(), 0) == kCFCompareEqualTo; }
inline bool operator!=(CFStringRef x, const CACFString& y) { return !(x == y); }
inline bool operator<=(CFStringRef x, const CACFString& y) { return (x < y) || (x == y); }
inline bool operator>=(CFStringRef x, const CACFString& y) { return !(x < y); }
inline bool operator>(CFStringRef 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

View File

@@ -0,0 +1,134 @@
// 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/>.
//
// CADebugMacros.cpp
// PublicUtility
//
// Copyright (C) 2014 Apple Inc. All Rights Reserved.
// Copyright © 2016, 2017, 2020 Kyle Neideck
//
// Original license header follows.
//
/*
File: CADebugMacros.cpp
Abstract: CADebugMacros.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 "CADebugMacros.h"
#include <stdio.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);
vLogError(fmt, args);
va_end(args);
}
void vLogError(const char *fmt, va_list args)
{
#if (DEBUG || !TARGET_API_MAC_OSX) && !CoreAudio_UseSysLog
printf("[ERROR] ");
vprintf(fmt, args);
printf("\n");
#else
vsyslog(LOG_ERR, fmt, args);
#endif
#if DEBUG
CADebuggerStop();
#endif
}
void LogWarning(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vLogWarning(fmt, args);
va_end(args);
}
void vLogWarning(const char *fmt, va_list args)
{
#if (DEBUG || !TARGET_API_MAC_OSX) && !CoreAudio_UseSysLog
printf("[WARNING] ");
vprintf(fmt, args);
printf("\n");
#else
vsyslog(LOG_WARNING, fmt, args);
#endif
#if DEBUG
//CADebuggerStop(); // TODO: Add a toggle for this to the project file (under "Preprocessor Macros"). Default to off.
#endif
}

View File

@@ -0,0 +1,612 @@
// 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/>.
//
// CADebugMacros.h
// PublicUtility
//
// Copyright (C) 2014 Apple Inc. All Rights Reserved.
// Copyright © 2016, 2020 Kyle Neideck
//
// Original license header follows.
//
/*
File: CADebugMacros.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(__CADebugMacros_h__)
#define __CADebugMacros_h__
//=============================================================================
// Includes
//=============================================================================
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
#include <CoreAudio/CoreAudioTypes.h>
#else
#include "CoreAudioTypes.h"
#endif
#include "CADebugPrintf.h"
#include <stdarg.h>
//=============================================================================
// 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
// 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
#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
#if DEBUG || CoreAudio_Debug
// can be used to break into debugger immediately, also see CADebugger
#define BusError() { long* p=NULL; *p=0; }
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
#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 and va_list versions.
void LogError(const char *fmt, ...) __printflike(1, 2); // writes to syslog (and stderr if debugging)
void vLogError(const char *fmt, va_list args);
void LogWarning(const char *fmt, ...) __printflike(1, 2); // writes to syslog (and stderr if debugging)
void vLogWarning(const char *fmt, va_list args);
#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); \
}
#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

View File

@@ -0,0 +1,114 @@
// 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/>.
//
// CADebugPrintf.cpp
// PublicUtility
//
// Copyright (C) 2014 Apple Inc. All Rights Reserved.
// Copyright © 2020 Kyle Neideck
//
// Original license header follows.
//
/*
File: CADebugPrintf.cpp
Abstract: CADebugPrintf.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 "CADebugPrintf.h"
#if TARGET_OS_WIN32
#include <stdarg.h>
#include <stdio.h>
#include <Windows.h>
extern "C"
int CAWin32DebugPrintf(char* inFormat, ...)
{
if (VCDebugLoggingIsEnabled()) {
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

View File

@@ -0,0 +1,113 @@
/*
File: CADebugPrintf.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(__CADebugPrintf_h__)
#define __CADebugPrintf_h__
//=============================================================================
// Includes
//=============================================================================
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
#include <CoreAudio/CoreAudioTypes.h>
#else
#include "CoreAudioTypes.h"
#endif
#include "VCDebugLogging.h"
//=============================================================================
// Macros to redirect debugging output to various logging services
//=============================================================================
//#define CoreAudio_UseSysLog 1
//#define CoreAudio_UseSideFile "/CoreAudio-%d.txt"
#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, ...) \
do { \
if (VCDebugLoggingIsEnabled()) { \
DebugPrintfRtn(DebugPrintfFileComma inFormat DebugPrintfLineEnding, ## __VA_ARGS__); \
} \
} while (0)
#endif

View 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
}

View File

@@ -0,0 +1,78 @@
/*
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
//=============================================================================
// VC edit: Added extern "C" so CADebugger (and headers that include it) can be used in Obj-C.
#ifdef __cplusplus
extern "C" {
#endif
#if TARGET_API_MAC_OSX
extern bool CAIsDebuggerAttached(void);
#endif
extern void CADebuggerStop(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,83 @@
/*
File: CAException.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(__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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,238 @@
/*
File: CAHALAudioDevice.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(__CAHALAudioDevice_h__)
#define __CAHALAudioDevice_h__
//==================================================================================================
// Includes
//==================================================================================================
// Super Class Includes
#include "CAHALAudioObject.h"
// PublicUtility Includes
#include "CADebugMacros.h"
#include "CAException.h"
//==================================================================================================
// CAHALAudioDevice
//==================================================================================================
class CAHALAudioDevice
:
public CAHALAudioObject
{
// Construction/Destruction
public:
CAHALAudioDevice(AudioObjectID inAudioDevice);
CAHALAudioDevice(CFStringRef inUID);
virtual ~CAHALAudioDevice();
// General Stuff
public:
CFStringRef CopyDeviceUID() const;
bool HasModelUID() const;
CFStringRef CopyModelUID() const;
CFStringRef CopyConfigurationApplicationBundleID() const;
CFURLRef CopyIconLocation() const;
UInt32 GetTransportType() const;
bool CanBeDefaultDevice(bool inIsInput, bool inIsSystem) const;
bool HasDevicePlugInStatus() const;
OSStatus GetDevicePlugInStatus() const;
bool IsAlive() const;
bool IsHidden() const;
pid_t GetHogModeOwner() const;
bool IsHogModeSettable() const;
bool TakeHogMode();
void ReleaseHogMode();
bool HasPreferredStereoChannels(bool inIsInput) const;
void GetPreferredStereoChannels(bool inIsInput, UInt32& outLeft, UInt32& outRight) const;
void SetPreferredStereoChannels(bool inIsInput, UInt32 inLeft, UInt32 inRight);
bool HasPreferredChannelLayout(bool inIsInput) const;
void GetPreferredChannelLayout(bool inIsInput, AudioChannelLayout& outChannelLayout) const;
void SetPreferredStereoChannels(bool inIsInput, AudioChannelLayout& inChannelLayout);
UInt32 GetNumberRelatedAudioDevices() const;
void GetRelatedAudioDevices(UInt32& ioNumberRelatedDevices, AudioObjectID* outRelatedDevices) const;
AudioObjectID GetRelatedAudioDeviceByIndex(UInt32 inIndex) const;
// Stream Stuff
public:
UInt32 GetNumberStreams(bool inIsInput) const;
void GetStreams(bool inIsInput, UInt32& ioNumberStreams, AudioObjectID* outStreamList) const;
AudioObjectID GetStreamByIndex(bool inIsInput, UInt32 inIndex) const;
UInt32 GetTotalNumberChannels(bool inIsInput) const;
void GetCurrentVirtualFormats(bool inIsInput, UInt32& ioNumberStreams, AudioStreamBasicDescription* outFormats) const;
void GetCurrentPhysicalFormats(bool inIsInput, UInt32& ioNumberStreams, AudioStreamBasicDescription* outFormats) const;
// IO Stuff
public:
bool IsRunning() const;
bool IsRunningSomewhere() const;
UInt32 GetLatency(bool inIsInput) const;
UInt32 GetSafetyOffset(bool inIsInput) const;
bool HasClockDomain() const;
UInt32 GetClockDomain() const;
Float64 GetActualSampleRate() const;
Float64 GetNominalSampleRate() const;
void SetNominalSampleRate(Float64 inSampleRate);
UInt32 GetNumberAvailableNominalSampleRateRanges() const;
void GetAvailableNominalSampleRateRanges(UInt32& ioNumberRanges, AudioValueRange* outRanges) const;
void GetAvailableNominalSampleRateRangeByIndex(UInt32 inIndex, Float64& outMinimum, Float64& outMaximum) const;
bool IsValidNominalSampleRate(Float64 inSampleRate) const;
bool IsIOBufferSizeSettable() const;
UInt32 GetIOBufferSize() const;
void SetIOBufferSize(UInt32 inBufferSize);
bool UsesVariableIOBufferSizes() const;
UInt32 GetMaximumVariableIOBufferSize() const;
bool HasIOBufferSizeRange() const;
void GetIOBufferSizeRange(UInt32& outMinimum, UInt32& outMaximum) const;
AudioDeviceIOProcID CreateIOProcID(AudioDeviceIOProc inIOProc, void* inClientData);
AudioDeviceIOProcID CreateIOProcIDWithBlock(dispatch_queue_t inDispatchQueue, AudioDeviceIOBlock inIOBlock);
void DestroyIOProcID(AudioDeviceIOProcID inIOProcID);
void StartIOProc(AudioDeviceIOProcID inIOProcID);
void StartIOProcAtTime(AudioDeviceIOProcID inIOProcID, AudioTimeStamp& ioStartTime, bool inIsInput, bool inIgnoreHardware);
void StopIOProc(AudioDeviceIOProcID inIOProcID);
void GetIOProcStreamUsage(AudioDeviceIOProcID inIOProcID, bool inIsInput, bool* outStreamUsage) const;
void SetIOProcStreamUsage(AudioDeviceIOProcID inIOProcID, bool inIsInput, const bool* inStreamUsage);
Float32 GetIOCycleUsage() const;
void SetIOCycleUsage(Float32 inValue);
// Time Operations
public:
void GetCurrentTime(AudioTimeStamp& outTime);
void TranslateTime(const AudioTimeStamp& inTime, AudioTimeStamp& outTime);
void GetNearestStartTime(AudioTimeStamp& ioTime, bool inIsInput, bool inIgnoreHardware);
// Controls
public:
bool HasVolumeControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool VolumeControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
Float32 GetVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
Float32 GetVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
void SetVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue);
void SetVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue);
Float32 GetVolumeControlScalarForDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const;
Float32 GetVolumeControlDecibelForScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const;
bool HasSubVolumeControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool SubVolumeControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
Float32 GetSubVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
Float32 GetSubVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
void SetSubVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue);
void SetSubVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue);
Float32 GetSubVolumeControlScalarForDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const;
Float32 GetSubVolumeControlDecibelForScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const;
bool HasMuteControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool MuteControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool GetMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
void SetMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue);
bool HasSoloControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool SoloControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool GetSoloControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
void SetSoloControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue);
bool HasStereoPanControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool StereoPanControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
Float32 GetStereoPanControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
void SetStereoPanControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue);
void GetStereoPanControlChannels(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32& outLeftChannel, UInt32& outRightChannel) const;
bool HasJackControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool GetJackControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool HasSubMuteControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool SubMuteControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool GetSubMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
void SetSubMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue);
bool HasiSubOwnerControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool iSubOwnerControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool GetiSubOwnerControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
void SetiSubOwnerControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue);
bool HasDataSourceControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool DataSourceControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
UInt32 GetCurrentDataSourceID(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
void SetCurrentDataSourceByID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID);
UInt32 GetNumberAvailableDataSources(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
void GetAvailableDataSources(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32& ioNumberSources, UInt32* outSources) const;
UInt32 GetAvailableDataSourceByIndex(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inIndex) const;
CFStringRef CopyDataSourceNameForID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID) const;
bool HasDataDestinationControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
bool DataDestinationControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
UInt32 GetCurrentDataDestinationID(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
void SetCurrentDataDestinationByID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID);
UInt32 GetNumberAvailableDataDestinations(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
void GetAvailableDataDestinations(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32& ioNumberDestinations, UInt32* outDestinations) const;
UInt32 GetAvailableDataDestinationByIndex(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inIndex) const;
CFStringRef CopyDataDestinationNameForID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID) const;
bool HasClockSourceControl() const;
bool ClockSourceControlIsSettable() const;
UInt32 GetCurrentClockSourceID() const;
void SetCurrentClockSourceByID(UInt32 inID);
UInt32 GetNumberAvailableClockSources() const;
void GetAvailableClockSources(UInt32& ioNumberSources, UInt32* outSources) const;
UInt32 GetAvailableClockSourceByIndex(UInt32 inIndex) const;
CFStringRef CopyClockSourceNameForID(UInt32 inID) const;
UInt32 GetClockSourceKindForID(UInt32 inID) const;
};
inline AudioDeviceIOProcID CAHALAudioDevice::CreateIOProcIDWithBlock(dispatch_queue_t inDispatchQueue, AudioDeviceIOBlock inIOBlock)
{
AudioDeviceIOProcID theAnswer = NULL;
OSStatus theError = AudioDeviceCreateIOProcIDWithBlock(&theAnswer, mObjectID, inDispatchQueue, inIOBlock);
ThrowIfError(theError, CAException(theError), "CAHALAudioDevice::CreateIOProcIDWithBlock: got an error creating the IOProc ID");
return theAnswer;
}
#endif

View File

@@ -0,0 +1,373 @@
/*
File: CAHALAudioObject.cpp
Abstract: CAHALAudioObject.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 "CAHALAudioObject.h"
// PublicUtility Includes
#include "CAAutoDisposer.h"
#include "CADebugMacros.h"
#include "CAException.h"
#include "CAPropertyAddress.h"
//==================================================================================================
// CAHALAudioObject
//==================================================================================================
CAHALAudioObject::CAHALAudioObject(AudioObjectID inObjectID)
:
mObjectID(inObjectID)
{
}
CAHALAudioObject::~CAHALAudioObject()
{
}
AudioObjectID CAHALAudioObject::GetObjectID() const
{
return mObjectID;
}
void CAHALAudioObject::SetObjectID(AudioObjectID inObjectID)
{
mObjectID = inObjectID;
}
AudioClassID CAHALAudioObject::GetClassID() const
{
// set up the return value
AudioClassID theAnswer = 0;
// set up the property address
CAPropertyAddress theAddress(kAudioObjectPropertyClass);
// make sure the property exists
if(HasProperty(theAddress))
{
UInt32 theSize = sizeof(AudioClassID);
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
}
return theAnswer;
}
AudioObjectID CAHALAudioObject::GetOwnerObjectID() const
{
// set up the return value
AudioObjectID theAnswer = 0;
// set up the property address
CAPropertyAddress theAddress(kAudioObjectPropertyOwner);
// make sure the property exists
if(HasProperty(theAddress))
{
// get the property data
UInt32 theSize = sizeof(AudioObjectID);
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
}
return theAnswer;
}
CFStringRef CAHALAudioObject::CopyOwningPlugInBundleID() const
{
// set up the return value
CFStringRef theAnswer = NULL;
// set up the property address
CAPropertyAddress theAddress(kAudioObjectPropertyCreator);
// make sure the property exists
if(HasProperty(theAddress))
{
// get the property data
UInt32 theSize = sizeof(CFStringRef);
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
}
return theAnswer;
}
CFStringRef CAHALAudioObject::CopyName() const
{
// set up the return value
CFStringRef theAnswer = NULL;
// set up the property address
CAPropertyAddress theAddress(kAudioObjectPropertyName);
// make sure the property exists
if(HasProperty(theAddress))
{
// get the property data
UInt32 theSize = sizeof(CFStringRef);
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
}
return theAnswer;
}
CFStringRef CAHALAudioObject::CopyManufacturer() const
{
// set up the return value
CFStringRef theAnswer = NULL;
// set up the property address
CAPropertyAddress theAddress(kAudioObjectPropertyManufacturer);
// make sure the property exists
if(HasProperty(theAddress))
{
// get the property data
UInt32 theSize = sizeof(CFStringRef);
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
}
return theAnswer;
}
CFStringRef CAHALAudioObject::CopyNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const
{
// set up the return value
CFStringRef theAnswer = NULL;
// set up the property address
CAPropertyAddress theAddress(kAudioObjectPropertyElementName, inScope, inElement);
// make sure the property exists
if(HasProperty(theAddress))
{
// get the property data
UInt32 theSize = sizeof(CFStringRef);
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
}
return theAnswer;
}
CFStringRef CAHALAudioObject::CopyCategoryNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const
{
// set up the return value
CFStringRef theAnswer = NULL;
// set up the property address
CAPropertyAddress theAddress(kAudioObjectPropertyElementCategoryName, inScope, inElement);
// make sure the property exists
if(HasProperty(theAddress))
{
// get the property data
UInt32 theSize = sizeof(CFStringRef);
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
}
return theAnswer;
}
CFStringRef CAHALAudioObject::CopyNumberNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const
{
// set up the return value
CFStringRef theAnswer = NULL;
// set up the property address
CAPropertyAddress theAddress(kAudioObjectPropertyElementNumberName, inScope, inElement);
// make sure the property exists
if(HasProperty(theAddress))
{
// get the property data
UInt32 theSize = sizeof(CFStringRef);
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
}
return theAnswer;
}
bool CAHALAudioObject::ObjectExists(AudioObjectID inObjectID)
{
Boolean isSettable;
CAPropertyAddress theAddress(kAudioObjectPropertyClass);
// VC edit: Negated the expression returned. Seems to have been a bug.
//return (inObjectID == 0) || (AudioObjectIsPropertySettable(inObjectID, &theAddress, &isSettable) != 0);
return (inObjectID != kAudioObjectUnknown) && (AudioObjectIsPropertySettable(inObjectID, &theAddress, &isSettable) == kAudioHardwareNoError);
// VC edit end
}
UInt32 CAHALAudioObject::GetNumberOwnedObjects(AudioClassID inClass) const
{
// set up the return value
UInt32 theAnswer = 0;
// set up the property address
CAPropertyAddress theAddress(kAudioObjectPropertyOwnedObjects);
// figure out the qualifier
UInt32 theQualifierSize = 0;
void* theQualifierData = NULL;
if(inClass != 0)
{
theQualifierSize = sizeof(AudioObjectID);
theQualifierData = &inClass;
}
// get the property data size
theAnswer = GetPropertyDataSize(theAddress, theQualifierSize, theQualifierData);
// calculate the number of object IDs
theAnswer /= SizeOf32(AudioObjectID);
return theAnswer;
}
void CAHALAudioObject::GetAllOwnedObjects(AudioClassID inClass, UInt32& ioNumberObjects, AudioObjectID* ioObjectIDs) const
{
// set up the property address
CAPropertyAddress theAddress(kAudioObjectPropertyOwnedObjects);
// figure out the qualifier
UInt32 theQualifierSize = 0;
void* theQualifierData = NULL;
if(inClass != 0)
{
theQualifierSize = sizeof(AudioObjectID);
theQualifierData = &inClass;
}
// get the property data
UInt32 theDataSize = ioNumberObjects * SizeOf32(AudioClassID);
GetPropertyData(theAddress, theQualifierSize, theQualifierData, theDataSize, ioObjectIDs);
// set the number of object IDs being returned
ioNumberObjects = theDataSize / SizeOf32(AudioObjectID);
}
AudioObjectID CAHALAudioObject::GetOwnedObjectByIndex(AudioClassID inClass, UInt32 inIndex)
{
// set up the property address
CAPropertyAddress theAddress(kAudioObjectPropertyOwnedObjects);
// figure out the qualifier
UInt32 theQualifierSize = 0;
void* theQualifierData = NULL;
if(inClass != 0)
{
theQualifierSize = sizeof(AudioObjectID);
theQualifierData = &inClass;
}
// figure out how much space to allocate
UInt32 theDataSize = GetPropertyDataSize(theAddress, theQualifierSize, theQualifierData);
UInt32 theNumberObjectIDs = theDataSize / SizeOf32(AudioObjectID);
// set up the return value
AudioObjectID theAnswer = 0;
// maker sure the index is in range
if(inIndex < theNumberObjectIDs)
{
// allocate it
CAAutoArrayDelete<AudioObjectID> theObjectList(theDataSize / sizeof(AudioObjectID));
// get the property data
GetPropertyData(theAddress, theQualifierSize, theQualifierData, theDataSize, theObjectList);
// get the return value
theAnswer = theObjectList[inIndex];
}
return theAnswer;
}
bool CAHALAudioObject::HasProperty(const AudioObjectPropertyAddress& inAddress) const
{
return AudioObjectHasProperty(mObjectID, &inAddress);
}
bool CAHALAudioObject::IsPropertySettable(const AudioObjectPropertyAddress& inAddress) const
{
Boolean isSettable = false;
OSStatus theError = AudioObjectIsPropertySettable(mObjectID, &inAddress, &isSettable);
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::IsPropertySettable: got an error getting info about a property");
return isSettable != 0;
}
UInt32 CAHALAudioObject::GetPropertyDataSize(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const
{
UInt32 theDataSize = 0;
OSStatus theError = AudioObjectGetPropertyDataSize(mObjectID, &inAddress, inQualifierDataSize, inQualifierData, &theDataSize);
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::GetPropertyDataSize: got an error getting the property data size");
return theDataSize;
}
void CAHALAudioObject::GetPropertyData(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32& ioDataSize, void* outData) const
{
OSStatus theError = AudioObjectGetPropertyData(mObjectID, &inAddress, inQualifierDataSize, inQualifierData, &ioDataSize, outData);
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::GetPropertyData: got an error getting the property data");
}
void CAHALAudioObject::SetPropertyData(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
{
OSStatus theError = AudioObjectSetPropertyData(mObjectID, &inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData);
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::SetPropertyData: got an error setting the property data");
}
void CAHALAudioObject::AddPropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData)
{
OSStatus theError = AudioObjectAddPropertyListener(mObjectID, &inAddress, inListenerProc, inClientData);
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::AddPropertyListener: got an error adding a property listener");
}
void CAHALAudioObject::RemovePropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData)
{
OSStatus theError = AudioObjectRemovePropertyListener(mObjectID, &inAddress, inListenerProc, inClientData);
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::RemovePropertyListener: got an error removing a property listener");
}

View File

@@ -0,0 +1,155 @@
/*
File: CAHALAudioObject.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(__CAHALAudioObject_h__)
#define __CAHALAudioObject_h__
//==================================================================================================
// Includes
//==================================================================================================
// PublicUtility Includes
#include "CADebugMacros.h"
#include "CAException.h"
// System Includes
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
#include <CoreAudio/CoreAudio.h>
#include <CoreFoundation/CoreFoundation.h>
#else
#include <CoreAudio.h>
#include <CoreFoundation.h>
#endif
//==================================================================================================
// CAHALAudioObject
//==================================================================================================
class CAHALAudioObject
{
// Construction/Destruction
public:
CAHALAudioObject(AudioObjectID inObjectID);
virtual ~CAHALAudioObject();
// Attributes
public:
AudioObjectID GetObjectID() const;
void SetObjectID(AudioObjectID inObjectID);
AudioClassID GetClassID() const;
AudioObjectID GetOwnerObjectID() const;
CFStringRef CopyOwningPlugInBundleID() const;
CFStringRef CopyName() const;
CFStringRef CopyManufacturer() const;
CFStringRef CopyNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const;
CFStringRef CopyCategoryNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const;
CFStringRef CopyNumberNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const;
static bool ObjectExists(AudioObjectID inObjectID);
// Owned Objects
public:
UInt32 GetNumberOwnedObjects(AudioClassID inClass) const;
void GetAllOwnedObjects(AudioClassID inClass, UInt32& ioNumberObjects, AudioObjectID* ioObjectIDs) const;
AudioObjectID GetOwnedObjectByIndex(AudioClassID inClass, UInt32 inIndex);
// Property Operations
public:
bool HasProperty(const AudioObjectPropertyAddress& inAddress) const;
bool IsPropertySettable(const AudioObjectPropertyAddress& inAddress) const;
UInt32 GetPropertyDataSize(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const;
void GetPropertyData(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32& ioDataSize, void* outData) const;
void SetPropertyData(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
UInt32 GetPropertyData_UInt32(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { UInt32 theAnswer = 0; UInt32 theDataSize = SizeOf32(UInt32); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &theAnswer); return theAnswer; }
void SetPropertyData_UInt32(const AudioObjectPropertyAddress& inAddress, UInt32 inValue, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(UInt32), &inValue); }
Float32 GetPropertyData_Float32(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { Float32 theAnswer = 0; UInt32 theDataSize = SizeOf32(Float32); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &theAnswer); return theAnswer; }
void SetPropertyData_Float32(const AudioObjectPropertyAddress& inAddress, Float32 inValue, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(Float32), &inValue); }
Float64 GetPropertyData_Float64(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { Float64 theAnswer = 0; UInt32 theDataSize = SizeOf32(Float64); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &theAnswer); return theAnswer; }
void SetPropertyData_Float64(const AudioObjectPropertyAddress& inAddress, Float64 inValue, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(Float64), &inValue); }
CFTypeRef GetPropertyData_CFType(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { CFTypeRef theAnswer = NULL; UInt32 theDataSize = SizeOf32(CFTypeRef); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &theAnswer); return theAnswer; }
void SetPropertyData_CFType(const AudioObjectPropertyAddress& inAddress, CFTypeRef inValue, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(CFTypeRef), &inValue); }
CFStringRef GetPropertyData_CFString(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { CFStringRef theAnswer = NULL; UInt32 theDataSize = SizeOf32(CFStringRef); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &theAnswer); return theAnswer; }
void SetPropertyData_CFString(const AudioObjectPropertyAddress& inAddress, CFStringRef inValue, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(CFStringRef), &inValue); }
template <class T> void GetPropertyData_Struct(const AudioObjectPropertyAddress& inAddress, T& outStruct, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { UInt32 theDataSize = SizeOf32(T); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &outStruct); }
template <class T> void SetPropertyData_Struct(const AudioObjectPropertyAddress& inAddress, T& inStruct, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(T), &inStruct); }
template <class T> UInt32 GetPropertyData_ArraySize(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { return GetPropertyDataSize(inAddress, inQualifierDataSize, inQualifierData) / SizeOf32(T); }
template <class T> void GetPropertyData_Array(const AudioObjectPropertyAddress& inAddress, UInt32& ioNumberItems, T* outArray, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { UInt32 theDataSize = ioNumberItems * SizeOf32(T); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, outArray); ioNumberItems = theDataSize / SizeOf32(T); }
template <class T> void SetPropertyData_Array(const AudioObjectPropertyAddress& inAddress, UInt32 inNumberItems, T* inArray, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, inNumberItems * SizeOf32(T), inArray); }
void AddPropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData);
void RemovePropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData);
void AddPropertyListenerBlock(const AudioObjectPropertyAddress& inAddress, dispatch_queue_t inDispatchQueue, AudioObjectPropertyListenerBlock inListenerBlock);
void RemovePropertyListenerBlock(const AudioObjectPropertyAddress& inAddress, dispatch_queue_t inDispatchQueue, AudioObjectPropertyListenerBlock inListenerBlock);
// Implementation
protected:
AudioObjectID mObjectID;
};
inline void CAHALAudioObject::AddPropertyListenerBlock(const AudioObjectPropertyAddress& inAddress, dispatch_queue_t inDispatchQueue, AudioObjectPropertyListenerBlock inListenerBlock)
{
OSStatus theError = AudioObjectAddPropertyListenerBlock(mObjectID, &inAddress, inDispatchQueue, inListenerBlock);
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::AddPropertyListenerBlock: got an error adding a property listener");
}
inline void CAHALAudioObject::RemovePropertyListenerBlock(const AudioObjectPropertyAddress& inAddress, dispatch_queue_t inDispatchQueue, AudioObjectPropertyListenerBlock inListenerBlock)
{
OSStatus theError = AudioObjectRemovePropertyListenerBlock(mObjectID, &inAddress, inDispatchQueue, inListenerBlock);
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::RemovePropertyListener: got an error removing a property listener");
}
#endif

View File

@@ -0,0 +1,182 @@
/*
File: CAHALAudioStream.cpp
Abstract: CAHALAudioStream.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 "CAHALAudioStream.h"
// PublicUtility Includes
#include "CAAutoDisposer.h"
#include "CADebugMacros.h"
#include "CAException.h"
#include "CAPropertyAddress.h"
//==================================================================================================
// CAHALAudioStream
//==================================================================================================
CAHALAudioStream::CAHALAudioStream(AudioObjectID inAudioStream)
:
CAHALAudioObject(inAudioStream)
{
}
CAHALAudioStream::~CAHALAudioStream()
{
}
UInt32 CAHALAudioStream::GetDirection() const
{
CAPropertyAddress theAddress(kAudioStreamPropertyDirection);
return GetPropertyData_UInt32(theAddress, 0, NULL);
}
UInt32 CAHALAudioStream::GetTerminalType() const
{
CAPropertyAddress theAddress(kAudioStreamPropertyTerminalType);
return GetPropertyData_UInt32(theAddress, 0, NULL);
}
UInt32 CAHALAudioStream::GetStartingChannel() const
{
CAPropertyAddress theAddress(kAudioStreamPropertyStartingChannel);
return GetPropertyData_UInt32(theAddress, 0, NULL);
}
UInt32 CAHALAudioStream::GetLatency() const
{
CAPropertyAddress theAddress(kAudioStreamPropertyLatency);
return GetPropertyData_UInt32(theAddress, 0, NULL);
}
void CAHALAudioStream::GetCurrentVirtualFormat(AudioStreamBasicDescription& outFormat) const
{
CAPropertyAddress theAddress(kAudioStreamPropertyVirtualFormat);
UInt32 theSize = sizeof(AudioStreamBasicDescription);
GetPropertyData(theAddress, 0, NULL, theSize, &outFormat);
}
void CAHALAudioStream::SetCurrentVirtualFormat(const AudioStreamBasicDescription& inFormat)
{
CAPropertyAddress theAddress(kAudioStreamPropertyVirtualFormat);
SetPropertyData(theAddress, 0, NULL, sizeof(AudioStreamBasicDescription), &inFormat);
}
UInt32 CAHALAudioStream::GetNumberAvailableVirtualFormats() const
{
CAPropertyAddress theAddress(kAudioStreamPropertyAvailableVirtualFormats);
UInt32 theAnswer = GetPropertyDataSize(theAddress, 0, NULL);
theAnswer /= SizeOf32(AudioStreamRangedDescription);
return theAnswer;
}
void CAHALAudioStream::GetAvailableVirtualFormats(UInt32& ioNumberFormats, AudioStreamRangedDescription* outFormats) const
{
CAPropertyAddress theAddress(kAudioStreamPropertyAvailableVirtualFormats);
UInt32 theSize = ioNumberFormats * SizeOf32(AudioStreamRangedDescription);
GetPropertyData(theAddress, 0, NULL, theSize, outFormats);
ioNumberFormats = theSize / SizeOf32(AudioStreamRangedDescription);
}
void CAHALAudioStream::GetAvailableVirtualFormatByIndex(UInt32 inIndex, AudioStreamRangedDescription& outFormat) const
{
UInt32 theNumberFormats = GetNumberAvailableVirtualFormats();
if((theNumberFormats > 0) && (inIndex < theNumberFormats))
{
CAAutoArrayDelete<AudioStreamRangedDescription> theFormats(theNumberFormats);
GetAvailableVirtualFormats(theNumberFormats, theFormats);
if((theNumberFormats > 0) && (inIndex < theNumberFormats))
{
outFormat = theFormats[inIndex];
}
}
}
void CAHALAudioStream::GetCurrentPhysicalFormat(AudioStreamBasicDescription& outFormat) const
{
CAPropertyAddress theAddress(kAudioStreamPropertyPhysicalFormat);
UInt32 theSize = sizeof(AudioStreamBasicDescription);
GetPropertyData(theAddress, 0, NULL, theSize, &outFormat);
}
void CAHALAudioStream::SetCurrentPhysicalFormat(const AudioStreamBasicDescription& inFormat)
{
CAPropertyAddress theAddress(kAudioStreamPropertyPhysicalFormat);
SetPropertyData(theAddress, 0, NULL, sizeof(AudioStreamBasicDescription), &inFormat);
}
UInt32 CAHALAudioStream::GetNumberAvailablePhysicalFormats() const
{
CAPropertyAddress theAddress(kAudioStreamPropertyAvailablePhysicalFormats);
UInt32 theAnswer = GetPropertyDataSize(theAddress, 0, NULL);
theAnswer /= SizeOf32(AudioStreamRangedDescription);
return theAnswer;
}
void CAHALAudioStream::GetAvailablePhysicalFormats(UInt32& ioNumberFormats, AudioStreamRangedDescription* outFormats) const
{
CAPropertyAddress theAddress(kAudioStreamPropertyAvailablePhysicalFormats);
UInt32 theSize = ioNumberFormats * SizeOf32(AudioStreamRangedDescription);
GetPropertyData(theAddress, 0, NULL, theSize, outFormats);
ioNumberFormats = theSize / SizeOf32(AudioStreamRangedDescription);
}
void CAHALAudioStream::GetAvailablePhysicalFormatByIndex(UInt32 inIndex, AudioStreamRangedDescription& outFormat) const
{
UInt32 theNumberFormats = GetNumberAvailablePhysicalFormats();
if((theNumberFormats > 0) && (inIndex < theNumberFormats))
{
CAAutoArrayDelete<AudioStreamRangedDescription> theFormats(theNumberFormats);
GetAvailablePhysicalFormats(theNumberFormats, theFormats);
if((theNumberFormats > 0) && (inIndex < theNumberFormats))
{
outFormat = theFormats[inIndex];
}
}
}

View File

@@ -0,0 +1,94 @@
/*
File: CAHALAudioStream.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(__CAHALAudioStream_h__)
#define __CAHALAudioStream_h__
//==================================================================================================
// Includes
//==================================================================================================
// Super Class Includes
#include "CAHALAudioObject.h"
//==================================================================================================
// CAHALAudioStream
//==================================================================================================
class CAHALAudioStream
:
public CAHALAudioObject
{
// Construction/Destruction
public:
CAHALAudioStream(AudioObjectID inAudioStream);
virtual ~CAHALAudioStream();
// Attributes
public:
UInt32 GetDirection() const;
UInt32 GetTerminalType() const;
UInt32 GetStartingChannel() const;
UInt32 GetLatency() const;
// Format Info
public:
void GetCurrentVirtualFormat(AudioStreamBasicDescription& outFormat) const;
void SetCurrentVirtualFormat(const AudioStreamBasicDescription& inFormat);
UInt32 GetNumberAvailableVirtualFormats() const;
void GetAvailableVirtualFormats(UInt32& ioNumberFormats, AudioStreamRangedDescription* outFormats) const;
void GetAvailableVirtualFormatByIndex(UInt32 inIndex, AudioStreamRangedDescription& outFormat) const;
void GetCurrentPhysicalFormat(AudioStreamBasicDescription& outFormat) const;
void SetCurrentPhysicalFormat(const AudioStreamBasicDescription& inFormat);
UInt32 GetNumberAvailablePhysicalFormats() const;
void GetAvailablePhysicalFormats(UInt32& ioNumberFormats, AudioStreamRangedDescription* outFormats) const;
void GetAvailablePhysicalFormatByIndex(UInt32 inIndex, AudioStreamRangedDescription& outFormat) const;
};
#endif

View File

@@ -0,0 +1,181 @@
/*
File: CAHALAudioSystemObject.cpp
Abstract: CAHALAudioSystemObject.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 "CAHALAudioSystemObject.h"
// PublicUtility Includes
#include "CAAutoDisposer.h"
#include "CACFString.h"
#include "CAHALAudioDevice.h"
#include "CAPropertyAddress.h"
//==================================================================================================
// CAHALAudioSystemObject
//==================================================================================================
CAHALAudioSystemObject::CAHALAudioSystemObject()
:
CAHALAudioObject(kAudioObjectSystemObject)
{
}
CAHALAudioSystemObject::~CAHALAudioSystemObject()
{
}
UInt32 CAHALAudioSystemObject::GetNumberAudioDevices() const
{
CAPropertyAddress theAddress(kAudioHardwarePropertyDevices);
UInt32 theAnswer = GetPropertyDataSize(theAddress, 0, NULL);
theAnswer /= SizeOf32(AudioObjectID);
return theAnswer;
}
void CAHALAudioSystemObject::GetAudioDevices(UInt32& ioNumberAudioDevices, AudioObjectID* outAudioDevices) const
{
CAPropertyAddress theAddress(kAudioHardwarePropertyDevices);
UInt32 theSize = ioNumberAudioDevices * SizeOf32(AudioObjectID);
GetPropertyData(theAddress, 0, NULL, theSize, outAudioDevices);
ioNumberAudioDevices = theSize / SizeOf32(AudioObjectID);
}
AudioObjectID CAHALAudioSystemObject::GetAudioDeviceAtIndex(UInt32 inIndex) const
{
AudioObjectID theAnswer = kAudioObjectUnknown;
UInt32 theNumberDevices = GetNumberAudioDevices();
if((theNumberDevices > 0) && (inIndex < theNumberDevices))
{
CAAutoArrayDelete<AudioObjectID> theDeviceList(theNumberDevices);
GetAudioDevices(theNumberDevices, theDeviceList);
if((theNumberDevices > 0) && (inIndex < theNumberDevices))
{
theAnswer = theDeviceList[inIndex];
}
}
return theAnswer;
}
AudioObjectID CAHALAudioSystemObject::GetAudioDeviceForUID(CFStringRef inUID) const
{
AudioObjectID theAnswer = kAudioObjectUnknown;
AudioValueTranslation theValue = { &inUID, sizeof(CFStringRef), &theAnswer, sizeof(AudioObjectID) };
CAPropertyAddress theAddress(kAudioHardwarePropertyDeviceForUID);
UInt32 theSize = sizeof(AudioValueTranslation);
GetPropertyData(theAddress, 0, NULL, theSize, &theValue);
return theAnswer;
}
void CAHALAudioSystemObject::LogBasicDeviceInfo()
{
UInt32 theNumberDevices = GetNumberAudioDevices();
CAAutoArrayDelete<AudioObjectID> theDeviceList(theNumberDevices);
GetAudioDevices(theNumberDevices, theDeviceList);
DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: %d devices", (int)theNumberDevices);
for(UInt32 theDeviceIndex = 0; theDeviceIndex < theNumberDevices; ++theDeviceIndex)
{
char theCString[256];
UInt32 theCStringSize = sizeof(theCString);
DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: Device %d", (int)theDeviceIndex);
CAHALAudioDevice theDevice(theDeviceList[theDeviceIndex]);
DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: Object ID: %d", (int)theDeviceList[theDeviceIndex]);
CACFString theDeviceName(theDevice.CopyName());
theCStringSize = sizeof(theCString);
theDeviceName.GetCString(theCString, theCStringSize);
DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: Name: %s", theCString);
CACFString theDeviceUID(theDevice.CopyDeviceUID());
theCStringSize = sizeof(theCString);
theDeviceUID.GetCString(theCString, theCStringSize);
DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: UID: %s", theCString);
}
}
static inline AudioObjectPropertySelector CAHALAudioSystemObject_CalculateDefaultDeviceProperySelector(bool inIsInput, bool inIsSystem)
{
AudioObjectPropertySelector theAnswer = kAudioHardwarePropertyDefaultOutputDevice;
if(inIsInput)
{
theAnswer = kAudioHardwarePropertyDefaultInputDevice;
}
else if(inIsSystem)
{
theAnswer = kAudioHardwarePropertyDefaultSystemOutputDevice;
}
return theAnswer;
}
AudioObjectID CAHALAudioSystemObject::GetDefaultAudioDevice(bool inIsInput, bool inIsSystem) const
{
AudioObjectID theAnswer = kAudioObjectUnknown;
CAPropertyAddress theAddress(CAHALAudioSystemObject_CalculateDefaultDeviceProperySelector(inIsInput, inIsSystem));
UInt32 theSize = sizeof(AudioObjectID);
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
return theAnswer;
}
void CAHALAudioSystemObject::SetDefaultAudioDevice(bool inIsInput, bool inIsSystem, AudioObjectID inNewDefaultDevice)
{
CAPropertyAddress theAddress(CAHALAudioSystemObject_CalculateDefaultDeviceProperySelector(inIsInput, inIsSystem));
UInt32 theSize = sizeof(AudioObjectID);
SetPropertyData(theAddress, 0, NULL, theSize, &inNewDefaultDevice);
}
AudioObjectID CAHALAudioSystemObject::GetAudioPlugInForBundleID(CFStringRef inUID) const
{
AudioObjectID theAnswer = kAudioObjectUnknown;
AudioValueTranslation theValue = { &inUID, sizeof(CFStringRef), &theAnswer, sizeof(AudioObjectID) };
CAPropertyAddress theAddress(kAudioHardwarePropertyPlugInForBundleID);
UInt32 theSize = sizeof(AudioValueTranslation);
GetPropertyData(theAddress, 0, NULL, theSize, &theValue);
return theAnswer;
}

View File

@@ -0,0 +1,90 @@
/*
File: CAHALAudioSystemObject.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(__CAHALAudioSystemObject_h__)
#define __CAHALAudioSystemObject_h__
//==================================================================================================
// Includes
//==================================================================================================
// Super Class Includes
#include "CAHALAudioObject.h"
//==================================================================================================
// CAHALAudioSystemObject
//==================================================================================================
class CAHALAudioSystemObject
:
public CAHALAudioObject
{
// Construction/Destruction
public:
CAHALAudioSystemObject();
virtual ~CAHALAudioSystemObject();
// Audio Device List Management
public:
UInt32 GetNumberAudioDevices() const;
void GetAudioDevices(UInt32& ioNumberAudioDevices, AudioObjectID* outAudioDevices) const;
AudioObjectID GetAudioDeviceAtIndex(UInt32 inIndex) const;
AudioObjectID GetAudioDeviceForUID(CFStringRef inUID) const;
void LogBasicDeviceInfo();
// Default Device Management
public:
AudioObjectID GetDefaultAudioDevice(bool inIsInput, bool inIsSystem) const;
void SetDefaultAudioDevice(bool inIsInput, bool inIsSystem, AudioObjectID inNewDefaultDevice);
// PlugIns
public:
AudioObjectID GetAudioPlugInForBundleID(CFStringRef inBundleID) const;
};
#endif

View File

@@ -0,0 +1,99 @@
/*
File: CAHostTimeBase.cpp
Abstract: CAHostTimeBase.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 "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
}

View File

@@ -0,0 +1,234 @@
/*
File: CAHostTimeBase.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(__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

View File

@@ -0,0 +1,345 @@
/*
File: CAMutex.cpp
Abstract: CAMutex.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 "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();
}
}

192
app/PublicUtility/CAMutex.h Normal file
View File

@@ -0,0 +1,192 @@
// 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/>.
//
// CAMutex.h
// PublicUtility
//
// Copyright (C) 2014 Apple Inc. All Rights Reserved.
// Copyright © 2020 Kyle Neideck
//
// Original license header follows.
//
/*
File: CAMutex.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 __CAMutex_h__
#define __CAMutex_h__
//==================================================================================================
// Includes
//==================================================================================================
#include "VCThreadSafetyAnalysis.h"
// 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 CAPABILITY("mutex") CAMutex
{
// Construction/Destruction
public:
CAMutex(const char* inName);
virtual ~CAMutex();
// Actions
public:
virtual bool Lock() ACQUIRE();
virtual void Unlock() RELEASE();
virtual bool Try(bool& outWasLocked) TRY_ACQUIRE(true); // 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 SCOPED_CAPABILITY Locker
{
// Construction/Destruction
public:
Locker(CAMutex& inMutex) ACQUIRE(inMutex) : mMutex(&inMutex), mNeedsRelease(false) { mNeedsRelease = mMutex->Lock(); }
Locker(CAMutex* inMutex) ACQUIRE(inMutex) : mMutex(inMutex), mNeedsRelease(false) { mNeedsRelease = (mMutex != NULL && mMutex->Lock()); }
// in this case the mutex can be null
~Locker() RELEASE() { if(mNeedsRelease) { mMutex->Unlock(); } }
private:
Locker(const Locker&);
Locker& operator=(const Locker&);
// Implementation
private:
CAMutex* mMutex;
bool mNeedsRelease;
};
// Clang's static analysis doesn't work for unlocker classes. See
// <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#no-alias-analysis>.
// 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 SCOPED_CAPABILITY Tryer {
// Construction/Destruction
public:
Tryer (CAMutex &mutex) TRY_ACQUIRE(true, mutex) : mMutex(mutex), mNeedsRelease(false), mHasLock(false) { mHasLock = mMutex.Try (mNeedsRelease); }
~Tryer () RELEASE() { 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__

View 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

View 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

View File

@@ -0,0 +1,292 @@
/*
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; }
};
//==================================================================================================
// 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()); }
bool HasItem(const AudioObjectPropertyAddress& inAddress) const { AddressList::const_iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), [&inAddress](const CAPropertyAddress& addr) { return CAPropertyAddress::IsCongruentAddress(addr, inAddress); }); return theIterator != mAddressList.end(); }
bool HasExactItem(const AudioObjectPropertyAddress& inAddress) const { AddressList::const_iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), [&inAddress](const CAPropertyAddress& addr) { return CAPropertyAddress::IsSameAddress(addr, inAddress); }); return theIterator != mAddressList.end(); }
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(), [&inAddress](const CAPropertyAddress& addr) { return CAPropertyAddress::IsSameAddress(addr, 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

View 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;
}

View 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

View File

@@ -0,0 +1,50 @@
// 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/>.
//
// VCDebugLogging.c
// PublicUtility
//
// Copyright © 2020, 2024 Kyle Neideck
//
// Self Include
#include "VCDebugLogging.h"
#pragma clang assume_nonnull begin
// It's probably not ideal to use a global variable for this, but it's a lot easier.
#if DEBUG || CoreAudio_Debug
// Enable debug logging by default in debug builds.
int gDebugLoggingIsEnabled = 1;
#else
int gDebugLoggingIsEnabled = 0;
#endif
// We don't bother synchronising accesses of gDebugLoggingIsEnabled because it isn't really
// necessary and would complicate code that accesses it on realtime threads.
int VCDebugLoggingIsEnabled(void)
{
return gDebugLoggingIsEnabled;
}
void VCSetDebugLoggingEnabled(int inEnabled)
{
gDebugLoggingIsEnabled = inEnabled;
}
#pragma clang assume_nonnull end

View File

@@ -0,0 +1,63 @@
// 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/>.
//
// VCDebugLogging.h
// PublicUtility
//
// Copyright © 2020 Kyle Neideck
//
// Functions to globally enable/disable debug logging, i.e. more detailed logging to help diagnose
// bugs. If debug logging is enabled, the DebugMsg macro from CADebugMacros.h (and possibly others)
// will log messages. If not, it won't do anything.
//
// If the preprocessor macro CoreAudio_UseSysLog is true, which is currently the case for all build
// variants (see VCApp/VCApp.xcodeproj/project.pbxproj and
// VCDriver/VCDriver.xcodeproj/project.pbxproj), those messages will be logged using syslog and
// can be read using Console.app. Try searching for "background music", "bgm" or "coreaudiod".
//
// Debug logging is enabled by default in debug builds, but in release builds you have to enable it
// by option-clicking the status bar icon and then checking the Debug Logging menu item. Enabling
// debug logging probably won't cause glitches, but we don't try to guarantee that and it's not
// well tested.
//
#ifndef PublicUtility__VCDebugLogging
#define PublicUtility__VCDebugLogging
#pragma clang assume_nonnull begin
/*!
* @return Non-zero if debug logging is globally enabled. (Probably -- it's not synchronised.)
* Real-time safe.
*/
#if defined(__cplusplus)
extern "C"
#endif
int VCDebugLoggingIsEnabled(void);
/*!
* @param inEnabled Non-zero to globally enable debug logging, zero to disable it. The change might
* not be visible to other threads immediately.
*/
#if defined(__cplusplus)
extern "C"
#endif
void VCSetDebugLoggingEnabled(int inEnabled);
#pragma clang assume_nonnull end
#endif /* PublicUtility__VCDebugLogging */

View File

@@ -0,0 +1,104 @@
// 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/>.
// Original licence at the end of this file.
//
// VCThreadSafetyAnalysis.h
// PublicUtility
//
// © Copyright 2007-2020, The Clang Team
// Copyright © 2020 Kyle Neideck
//
// Macros that wrap Clang's attributes for statically checking concurrency properties. From
// <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutexheader>.
//
#ifndef PublicUtility__VCThreadSafetyAnalysis
#define PublicUtility__VCThreadSafetyAnalysis
// Enable thread safety attributes only with clang.
// The attributes can be safely erased when compiling with other compilers.
#if defined(__clang__) && (!defined(SWIG))
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif
#define CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
#define SCOPED_CAPABILITY \
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
#define GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
#define PT_GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
#define ACQUIRED_BEFORE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
#define ACQUIRED_AFTER(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
#define REQUIRES(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
#define REQUIRES_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
#define ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
#define ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
#define RELEASE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
#define RELEASE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
#define TRY_ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
#define TRY_ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
#define EXCLUDES(...) \
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
#define ASSERT_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
#define ASSERT_SHARED_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
#define RETURN_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
#endif /* PublicUtility__VCThreadSafetyAnalysis */
/*
This file is derived from "mutex.h" from the Clang documentation at
<https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>. See the LLVM license at
<https://llvm.org/docs/DeveloperPolicy.html#legacy>.
*/

387
app/VCAudioDevice.cpp Normal file
View File

@@ -0,0 +1,387 @@
// 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/>.
//
// VCAudioDevice.cpp
// VCApp
//
// Copyright © 2017 Kyle Neideck
//
// Self Include
#include "VCAudioDevice.h"
// Local Includes
#include "VC_Types.h"
// System Includes
#include <AudioToolbox/AudioServices.h>
#pragma mark Construction/Destruction
VCAudioDevice::VCAudioDevice(AudioObjectID inAudioDevice)
:
CAHALAudioDevice(inAudioDevice)
{
}
VCAudioDevice::VCAudioDevice(CFStringRef inUID)
:
CAHALAudioDevice(inUID)
{
}
VCAudioDevice::VCAudioDevice(const CAHALAudioDevice& inDevice)
:
VCAudioDevice(inDevice.GetObjectID())
{
}
VCAudioDevice::~VCAudioDevice()
{
}
bool VCAudioDevice::CanBeOutputDeviceInVCApp() const
{
CFStringRef uid = CopyDeviceUID();
assert(uid != nullptr);
CFRelease(uid);
bool hasOutputChannels = GetTotalNumberChannels(/* inIsInput = */ false) > 0;
bool canBeDefault = CanBeDefaultDevice(/* inIsInput = */ false, /* inIsSystem = */ false);
return !IsVCDeviceInstance() &&
!IsHidden() &&
hasOutputChannels &&
canBeDefault;
}
#pragma mark Available Controls
bool VCAudioDevice::HasSettableMasterVolume(AudioObjectPropertyScope inScope) const
{
return HasVolumeControl(inScope, kMasterChannel) &&
VolumeControlIsSettable(inScope, kMasterChannel);
}
bool VCAudioDevice::HasSettableVirtualMasterVolume(AudioObjectPropertyScope inScope) const
{
AudioObjectPropertyAddress virtualMasterVolumeAddress = {
kAudioHardwareServiceDeviceProperty_VirtualMainVolume,
inScope,
kAudioObjectPropertyElementMaster
};
// TODO: Replace these calls deprecated AudioToolbox functions. There are more below.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
Boolean virtualMasterVolumeIsSettable;
OSStatus err = AudioHardwareServiceIsPropertySettable(GetObjectID(),
&virtualMasterVolumeAddress,
&virtualMasterVolumeIsSettable);
virtualMasterVolumeIsSettable &= (err == kAudioServicesNoError);
bool hasVirtualMasterVolume =
AudioHardwareServiceHasProperty(GetObjectID(), &virtualMasterVolumeAddress);
#pragma clang diagnostic pop
return hasVirtualMasterVolume && virtualMasterVolumeIsSettable;
}
bool VCAudioDevice::HasSettableMasterMute(AudioObjectPropertyScope inScope) const
{
return HasMuteControl(inScope, kMasterChannel) &&
MuteControlIsSettable(inScope, kMasterChannel);
}
#pragma mark Control Values Accessors
void VCAudioDevice::CopyMuteFrom(const VCAudioDevice inDevice,
AudioObjectPropertyScope inScope)
{
// TODO: Support for devices that have per-channel mute controls but no master mute control
if(HasSettableMasterMute(inScope) && inDevice.HasMuteControl(inScope, kMasterChannel))
{
SetMuteControlValue(inScope,
kMasterChannel,
inDevice.GetMuteControlValue(inScope, kMasterChannel));
}
}
void VCAudioDevice::CopyVolumeFrom(const VCAudioDevice inDevice,
AudioObjectPropertyScope inScope)
{
// Get the volume of the other device.
bool didGetVolume = false;
Float32 volume = FLT_MIN;
if(inDevice.HasVolumeControl(inScope, kMasterChannel))
{
volume = inDevice.GetVolumeControlScalarValue(inScope, kMasterChannel);
didGetVolume = true;
}
// Use the average channel volume of the other device if it has no master volume.
if(!didGetVolume)
{
UInt32 numChannels =
inDevice.GetTotalNumberChannels(inScope == kAudioObjectPropertyScopeInput);
volume = 0;
for(UInt32 channel = 1; channel <= numChannels; channel++)
{
if(inDevice.HasVolumeControl(inScope, channel))
{
volume += inDevice.GetVolumeControlScalarValue(inScope, channel);
didGetVolume = true;
}
}
if(numChannels > 0) // Avoid divide by zero.
{
volume /= numChannels;
}
}
// Set the volume of this device.
if(didGetVolume && volume != FLT_MIN)
{
bool didSetVolume = false;
try
{
didSetVolume = SetMasterVolumeScalar(inScope, volume);
}
catch(CAException e)
{
OSStatus err = e.GetError();
char err4CC[5] = CA4CCToCString(err);
CFStringRef uid = CopyDeviceUID();
LogWarning("VCAudioDevice::CopyVolumeFrom: CAException '%s' trying to set master "
"volume of %s",
err4CC,
CFStringGetCStringPtr(uid, kCFStringEncodingUTF8));
CFRelease(uid);
}
if(!didSetVolume)
{
// Couldn't find a master volume control to set, so try to find a virtual one
Float32 virtualMasterVolume;
bool success = inDevice.GetVirtualMasterVolumeScalar(inScope, virtualMasterVolume);
if(success)
{
didSetVolume = SetVirtualMasterVolumeScalar(inScope, virtualMasterVolume);
}
}
if(!didSetVolume)
{
// Couldn't set a master or virtual master volume, so as a fallback try to set each
// channel individually.
UInt32 numChannels = GetTotalNumberChannels(inScope == kAudioObjectPropertyScopeInput);
for(UInt32 channel = 1; channel <= numChannels; channel++)
{
if(HasVolumeControl(inScope, channel) && VolumeControlIsSettable(inScope, channel))
{
SetVolumeControlScalarValue(inScope, channel, volume);
}
}
}
}
}
bool VCAudioDevice::SetMasterVolumeScalar(AudioObjectPropertyScope inScope, Float32 inVolume)
{
if(HasSettableMasterVolume(inScope))
{
SetVolumeControlScalarValue(inScope, kMasterChannel, inVolume);
return true;
}
return false;
}
bool VCAudioDevice::GetVirtualMasterVolumeScalar(AudioObjectPropertyScope inScope,
Float32& outVirtualMasterVolume) const
{
AudioObjectPropertyAddress virtualMasterVolumeAddress = {
kAudioHardwareServiceDeviceProperty_VirtualMainVolume,
inScope,
kAudioObjectPropertyElementMaster
};
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if(!AudioHardwareServiceHasProperty(GetObjectID(), &virtualMasterVolumeAddress))
{
return false;
}
#pragma clang diagnostic pop
UInt32 virtualMasterVolumePropertySize = sizeof(Float32);
return kAudioServicesNoError == AHSGetPropertyData(GetObjectID(),
&virtualMasterVolumeAddress,
&virtualMasterVolumePropertySize,
&outVirtualMasterVolume);
}
bool VCAudioDevice::SetVirtualMasterVolumeScalar(AudioObjectPropertyScope inScope,
Float32 inVolume)
{
// TODO: For me, setting the virtual master volume sets all the device's channels to the same volume, meaning you can't
// keep any channels quieter than the others. The expected behaviour is to scale the channel volumes
// proportionally. So to do this properly I think we'd have to store VCDevice's previous volume and calculate
// each channel's new volume from its current volume and the distance between VCDevice's old and new volumes.
//
// The docs kAudioHardwareServiceDeviceProperty_VirtualMasterVolume for say
// "If the device has individual channel volume controls, this property will apply to those identified by the
// device's preferred multi-channel layout (or preferred stereo pair if the device is stereo only). Note that
// this control maintains the relative balance between all the channels it affects.
// so I'm not sure why that's not working here. As a workaround we take the to device's (virtual master) balance
// before changing the volume and set it back after, but of course that'll only work for stereo devices.
bool didSetVolume = false;
if(HasSettableVirtualMasterVolume(inScope))
{
// Not sure why, but setting the virtual master volume sets all channels to the same volume. As a workaround, we store
// the current balance here so we can reset it after setting the volume.
Float32 virtualMasterBalance;
bool didGetVirtualMasterBalance = GetVirtualMasterBalance(inScope, virtualMasterBalance);
AudioObjectPropertyAddress virtualMasterVolumeAddress = {
kAudioHardwareServiceDeviceProperty_VirtualMainVolume,
inScope,
kAudioObjectPropertyElementMaster
};
didSetVolume = (kAudioServicesNoError == AHSSetPropertyData(GetObjectID(),
&virtualMasterVolumeAddress,
sizeof(Float32),
&inVolume));
// Reset the balance
AudioObjectPropertyAddress virtualMasterBalanceAddress = {
kAudioHardwareServiceDeviceProperty_VirtualMainBalance,
inScope,
kAudioObjectPropertyElementMaster
};
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if(didSetVolume &&
didGetVirtualMasterBalance &&
AudioHardwareServiceHasProperty(GetObjectID(), &virtualMasterBalanceAddress))
{
Boolean balanceIsSettable;
OSStatus err = AudioHardwareServiceIsPropertySettable(GetObjectID(),
&virtualMasterBalanceAddress,
&balanceIsSettable);
if(err == kAudioServicesNoError && balanceIsSettable)
{
AHSSetPropertyData(GetObjectID(),
&virtualMasterBalanceAddress,
sizeof(Float32),
&virtualMasterBalance);
}
}
#pragma clang diagnostic pop
}
return didSetVolume;
}
bool VCAudioDevice::GetVirtualMasterBalance(AudioObjectPropertyScope inScope,
Float32& outVirtualMasterBalance) const
{
AudioObjectPropertyAddress virtualMasterBalanceAddress = {
kAudioHardwareServiceDeviceProperty_VirtualMainBalance,
inScope,
kAudioObjectPropertyElementMaster
};
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if(!AudioHardwareServiceHasProperty(GetObjectID(), &virtualMasterBalanceAddress))
{
return false;
}
#pragma clang diagnostic pop
UInt32 virtualMasterVolumePropertySize = sizeof(Float32);
return kAudioServicesNoError == AHSGetPropertyData(GetObjectID(),
&virtualMasterBalanceAddress,
&virtualMasterVolumePropertySize,
&outVirtualMasterBalance);
}
#pragma mark Implementation
bool VCAudioDevice::IsVCDevice(bool inIncludeUISoundsInstance) const
{
bool isVCDevice = false;
if(GetObjectID() != kAudioObjectUnknown)
{
// Check the device's UID to see whether it's VCDevice.
CFStringRef uid = CopyDeviceUID();
if (uid == nullptr) {
return isVCDevice;
}
isVCDevice = CFEqual(uid, CFSTR(kVCDeviceUID));
CFRelease(uid);
}
return isVCDevice;
}
// static
OSStatus VCAudioDevice::AHSGetPropertyData(AudioObjectID inObjectID,
const AudioObjectPropertyAddress* inAddress,
UInt32* ioDataSize,
void* outData)
{
// The docs for AudioHardwareServiceGetPropertyData specifically allow passing NULL for
// inQualifierData as we do here, but it's declared in an assume_nonnull section so we have to
// disable the warning here. I'm not sure why inQualifierData isn't __nullable. I'm assuming
// it's either a backwards compatibility thing or just a bug.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnonnull"
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// The non-depreciated version of this (and the setter below) doesn't seem to support devices
// other than the default
return AudioHardwareServiceGetPropertyData(inObjectID, inAddress, 0, NULL, ioDataSize, outData);
#pragma clang diagnostic pop
}
// static
OSStatus VCAudioDevice::AHSSetPropertyData(AudioObjectID inObjectID,
const AudioObjectPropertyAddress* inAddress,
UInt32 inDataSize,
const void* inData)
{
// See the explanation about these pragmas in AHSGetPropertyData
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnonnull"
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return AudioHardwareServiceSetPropertyData(inObjectID, inAddress, 0, NULL, inDataSize, inData);
#pragma clang diagnostic pop
}

121
app/VCAudioDevice.h Normal file
View File

@@ -0,0 +1,121 @@
// 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/>.
// VCAudioDevice.h
// VCApp
//
// Copyright © 2017, 2020 Kyle Neideck
//
// A HAL audio device. Note that this class's only state is the AudioObjectID of the device.
//
#ifndef VCApp__VCAudioDevice
#define VCApp__VCAudioDevice
// PublicUtility Includes
#include "CAHALAudioDevice.h"
class VCAudioDevice
:
public CAHALAudioDevice
{
#pragma mark Construction/Destruction
public:
VCAudioDevice(AudioObjectID inAudioDevice);
/*!
Creates a VCAudioDevice with the Audio Object ID of the device whose UID is inUID or, if no
such device is found, kAudioObjectUnknown.
@throws CAException If the HAL returns an error when queried for the device's ID.
@see kAudioPlugInPropertyTranslateUIDToDevice in AudioHardwareBase.h.
*/
VCAudioDevice(CFStringRef inUID);
VCAudioDevice(const CAHALAudioDevice& inDevice);
virtual ~VCAudioDevice();
#if defined(__OBJC__)
// Hack/workaround for Objective-C classes so we don't have to use pointers for instance
// variables.
VCAudioDevice() : VCAudioDevice(kAudioObjectUnknown) { }
#endif /* defined(__OBJC__) */
operator AudioObjectID() const { return GetObjectID(); }
/*!
@return True if this device is VCDevice. (Specifically, the main instance of VCDevice, not
the instance used for UI sounds.)
@throws CAException If the HAL returns an error when queried.
*/
bool IsVCDevice() const { return IsVCDevice(false); };
/*!
@return True if this device is either the main instance of VCDevice (the device named
"Background Music") or the instance used for UI sounds (the device named "Background
Music (UI Sounds)").
@throws CAException If the HAL returns an error when queried.
*/
bool IsVCDeviceInstance() const { return IsVCDevice(true); };
/*!
@return True if this device can be set as the output device in VCApp.
@throws CAException If the HAL returns an error when queried.
*/
bool CanBeOutputDeviceInVCApp() const;
#pragma mark Available Controls
bool HasSettableMasterVolume(AudioObjectPropertyScope inScope) const;
bool HasSettableVirtualMasterVolume(AudioObjectPropertyScope inScope) const;
bool HasSettableMasterMute(AudioObjectPropertyScope inScope) const;
#pragma mark Control Values Accessors
void CopyMuteFrom(const VCAudioDevice inDevice,
AudioObjectPropertyScope inScope);
void CopyVolumeFrom(const VCAudioDevice inDevice,
AudioObjectPropertyScope inScope);
bool SetMasterVolumeScalar(AudioObjectPropertyScope inScope, Float32 inVolume);
bool GetVirtualMasterVolumeScalar(AudioObjectPropertyScope inScope,
Float32& outVirtualMasterVolume) const;
bool SetVirtualMasterVolumeScalar(AudioObjectPropertyScope inScope,
Float32 inVolume);
bool GetVirtualMasterBalance(AudioObjectPropertyScope inScope,
Float32& outVirtualMasterBalance) const;
#pragma mark Implementation
private:
bool IsVCDevice(bool inIncludingUISoundsInstance) const;
static OSStatus AHSGetPropertyData(AudioObjectID inObjectID,
const AudioObjectPropertyAddress* inAddress,
UInt32* ioDataSize,
void* outData);
static OSStatus AHSSetPropertyData(AudioObjectID inObjectID,
const AudioObjectPropertyAddress* inAddress,
UInt32 inDataSize,
const void* inData);
};
#endif /* VCApp__VCAudioDevice */

View File

@@ -0,0 +1,55 @@
// 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.
#if defined(__cplusplus)
// Local Includes
#import "VCVirtualDevice.h"
// PublicUtility Includes
#import "CAHALAudioDevice.h"
#endif /* defined(__cplusplus) */
// System Includes
#import <Foundation/Foundation.h>
#import <CoreAudio/AudioHardwareBase.h>
#pragma clang assume_nonnull begin
@interface VCAudioDeviceManager : NSObject
// Called on any volume change (scroll, keyboard, system slider).
@property (nonatomic, copy) void (^onVolumeChanged)(void);
// Returns nil if the virtual device driver isn't installed.
- (instancetype) init;
- (NSError* __nullable) setVCDeviceAsOSDefault;
- (NSError* __nullable) unsetVCDeviceAsOSDefault;
#ifdef __cplusplus
- (VCVirtualDevice) vcDevice;
- (CAHALAudioDevice) outputDevice;
#endif
- (BOOL) isVirtualDeviceActive;
// Scans connected devices. If any lack hardware volume, activates the virtual device and starts
// playthrough. Otherwise bypasses. Called at launch and on device hotplug.
- (void) evaluateAndActivate;
// Universal volume control — works on both virtual device and native hardware.
- (float) volume;
- (void) setVolume:(float)vol;
- (BOOL) isMuted;
- (void) setMuted:(BOOL)muted;
@end
#pragma clang assume_nonnull end

465
app/VCAudioDeviceManager.mm Normal file
View File

@@ -0,0 +1,465 @@
// 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.
#import "VCAudioDeviceManager.h"
#import "VC_Types.h"
#import "VC_Utils.h"
#import "VCAudioDevice.h"
#import "VCPlayThrough.h"
#import "CAAtomic.h"
#import "CAAutoDisposer.h"
#import "CAHALAudioSystemObject.h"
#import "CAPropertyAddress.h"
static NSString* const kVCVolumeKey = @"VCVolume";
#pragma clang assume_nonnull begin
@implementation VCAudioDeviceManager {
VCVirtualDevice* vcDevice;
VCAudioDevice outputDevice;
VCPlayThrough playThrough;
NSRecursiveLock* stateLock;
BOOL virtualDeviceActive;
AudioObjectPropertyListenerBlock deviceListListener;
AudioObjectPropertyListenerBlock defaultDeviceListener;
AudioObjectPropertyListenerBlock volumeListenerBlock;
AudioObjectID listenedDeviceID;
dispatch_source_t debounceTimer;
}
#pragma mark Init / Dealloc
- (instancetype) init {
if ((self = [super init])) {
stateLock = [NSRecursiveLock new];
outputDevice = kAudioObjectUnknown;
listenedDeviceID = kAudioObjectUnknown;
virtualDeviceActive = NO;
try {
vcDevice = new VCVirtualDevice;
} catch (const CAException& e) {
LogError("VCAudioDeviceManager::init: VCDevice not found. (%d)", e.GetError());
self = nil;
return self;
}
[self listenForDeviceChanges];
}
return self;
}
- (void) dealloc {
@try {
[stateLock lock];
[self stopVolumeListener];
if (deviceListListener) {
VCLogAndSwallowExceptions("VCAudioDeviceManager::dealloc[devices]", [&] {
CAHALAudioSystemObject().RemovePropertyListenerBlock(
CAPropertyAddress(kAudioHardwarePropertyDevices),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
deviceListListener);
});
}
if (defaultDeviceListener) {
VCLogAndSwallowExceptions("VCAudioDeviceManager::dealloc[defaultDevice]", [&] {
CAHALAudioSystemObject().RemovePropertyListenerBlock(
CAPropertyAddress(kAudioHardwarePropertyDefaultOutputDevice),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
defaultDeviceListener);
});
}
if (vcDevice) {
delete vcDevice;
vcDevice = nullptr;
}
} @finally {
[stateLock unlock];
}
}
#pragma mark Default Device
- (NSError* __nullable) setVCDeviceAsOSDefault {
try {
CAMemoryBarrier();
vcDevice->SetAsOSDefault();
} catch (const CAException& e) {
VCLogExceptionIn("VCAudioDeviceManager::setVCDeviceAsOSDefault", e);
return [NSError errorWithDomain:@kVCAppBundleID code:e.GetError() userInfo:nil];
}
return nil;
}
- (NSError* __nullable) unsetVCDeviceAsOSDefault {
@try {
[stateLock lock];
AudioDeviceID outputDeviceID = outputDevice.GetObjectID();
if (outputDeviceID == kAudioObjectUnknown) {
return nil;
}
try {
vcDevice->UnsetAsOSDefault(outputDeviceID);
} catch (const CAException& e) {
VCLogExceptionIn("VCAudioDeviceManager::unsetVCDeviceAsOSDefault", e);
return [NSError errorWithDomain:@kVCAppBundleID code:e.GetError() userInfo:nil];
}
} @finally {
[stateLock unlock];
}
return nil;
}
#pragma mark Accessors
- (VCVirtualDevice) vcDevice { return *vcDevice; }
- (CAHALAudioDevice) outputDevice { return outputDevice; }
- (BOOL) isVirtualDeviceActive {
@try {
[stateLock lock];
return virtualDeviceActive;
} @finally {
[stateLock unlock];
}
}
#pragma mark Device Evaluation
- (BOOL) deviceHasHardwareVolume:(const VCAudioDevice&)device {
AudioObjectPropertyScope scope = kAudioDevicePropertyScopeOutput;
try {
if (device.HasSettableMasterVolume(scope) ||
device.HasSettableVirtualMasterVolume(scope)) {
return YES;
}
} catch (const CAException& e) {
VCLogException(e);
}
return NO;
}
- (void) listenForDeviceChanges {
VCAudioDeviceManager* __weak weakSelf = self;
deviceListListener = ^(UInt32 inNumberAddresses,
const AudioObjectPropertyAddress* inAddresses) {
#pragma unused (inNumberAddresses, inAddresses)
// Debounce: coalesce rapid hotplug events into one evaluation.
VCAudioDeviceManager* strongSelf = weakSelf;
if (!strongSelf) return;
@synchronized (strongSelf) {
if (strongSelf->debounceTimer) {
dispatch_source_cancel(strongSelf->debounceTimer);
}
strongSelf->debounceTimer = dispatch_source_create(
DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
dispatch_source_set_timer(strongSelf->debounceTimer,
dispatch_time(DISPATCH_TIME_NOW, 200 * NSEC_PER_MSEC),
DISPATCH_TIME_FOREVER, 50 * NSEC_PER_MSEC);
dispatch_source_set_event_handler(strongSelf->debounceTimer, ^{
[weakSelf evaluateAndActivate];
});
dispatch_resume(strongSelf->debounceTimer);
}
};
VCLogAndSwallowExceptions("VCAudioDeviceManager::listenForDeviceChanges", [&] {
CAHALAudioSystemObject().AddPropertyListenerBlock(
CAPropertyAddress(kAudioHardwarePropertyDevices),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
deviceListListener);
});
// When the default output device changes, re-attach volume listener to the new device.
defaultDeviceListener = ^(UInt32 inNumberAddresses,
const AudioObjectPropertyAddress* inAddresses) {
#pragma unused (inNumberAddresses, inAddresses)
VCAudioDeviceManager* strongSelf = weakSelf;
if (!strongSelf) return;
AudioObjectID newDefault = [strongSelf currentDefaultOutputDevice];
if (newDefault != kAudioObjectUnknown && newDefault != strongSelf->listenedDeviceID) {
[strongSelf startVolumeListenerOnDevice:newDefault];
}
if (strongSelf.onVolumeChanged) {
dispatch_async(dispatch_get_main_queue(), ^{
strongSelf.onVolumeChanged();
});
}
};
VCLogAndSwallowExceptions("VCAudioDeviceManager::listenForDefaultDeviceChanges", [&] {
CAHALAudioSystemObject().AddPropertyListenerBlock(
CAPropertyAddress(kAudioHardwarePropertyDefaultOutputDevice),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
defaultDeviceListener);
});
}
- (void) evaluateAndActivate {
@try {
[stateLock lock];
CAHALAudioSystemObject audioSystem;
UInt32 numDevices = audioSystem.GetNumberAudioDevices();
if (numDevices == 0) {
return;
}
CAAutoArrayDelete<AudioObjectID> devices(numDevices);
audioSystem.GetAudioDevices(numDevices, devices);
// Find a device needing software volume, and a fallback with hardware volume.
AudioObjectID deviceNeedingSoftwareVolume = kAudioObjectUnknown;
AudioObjectID fallbackDevice = kAudioObjectUnknown;
for (UInt32 i = 0; i < numDevices; i++) {
VCAudioDevice device(devices[i]);
VCLogAndSwallowExceptions("evaluateAndActivate", [&] {
if (!device.CanBeOutputDeviceInVCApp()) {
return;
}
if (![self deviceHasHardwareVolume:device]) {
deviceNeedingSoftwareVolume = devices[i];
} else if (fallbackDevice == kAudioObjectUnknown) {
fallbackDevice = devices[i];
}
});
}
AudioObjectID targetDevice;
BOOL needsVirtualDevice;
if (deviceNeedingSoftwareVolume != kAudioObjectUnknown) {
targetDevice = deviceNeedingSoftwareVolume;
needsVirtualDevice = YES;
} else if (fallbackDevice != kAudioObjectUnknown) {
targetDevice = fallbackDevice;
needsVirtualDevice = NO;
} else {
return;
}
// No change needed?
if (targetDevice == outputDevice.GetObjectID() &&
needsVirtualDevice == virtualDeviceActive) {
return;
}
VCAudioDevice newOutputDevice(targetDevice);
if (needsVirtualDevice) {
NSLog(@"VolumeControl: Activating for device %u (no hardware volume)", targetDevice);
try {
vcDevice->SetHidden(false);
playThrough.Deactivate();
playThrough.SetDevices(vcDevice, &newOutputDevice);
playThrough.Activate();
outputDevice = newOutputDevice;
virtualDeviceActive = YES;
[self setVCDeviceAsOSDefault];
[self restoreVolume];
[self startVolumeListenerOnDevice:vcDevice->GetObjectID()];
playThrough.Start();
playThrough.StopIfIdle();
} catch (const CAException& e) {
NSLog(@"VolumeControl: Failed to start playthrough for device %u (error %d)",
targetDevice, e.GetError());
}
} else {
NSLog(@"VolumeControl: Bypassing for device %u (has hardware volume)", targetDevice);
[self saveVolume];
playThrough.Deactivate();
virtualDeviceActive = NO;
outputDevice = newOutputDevice;
[self unsetVCDeviceAsOSDefault];
vcDevice->SetHidden(true);
[self startVolumeListenerOnDevice:targetDevice];
}
} @finally {
[stateLock unlock];
}
}
#pragma mark Universal Volume
// Returns the current system default output device.
- (AudioObjectID) currentDefaultOutputDevice {
AudioObjectID defaultDevice = kAudioObjectUnknown;
VCLogAndSwallowExceptions("currentDefaultOutputDevice", [&] {
CAHALAudioSystemObject audioSystem;
defaultDevice = audioSystem.GetDefaultAudioDevice(false, false);
});
return defaultDevice;
}
- (float) volume {
float vol = 0.5f;
VCLogAndSwallowExceptions("volume", [&] {
AudioObjectID devID = [self currentDefaultOutputDevice];
if (devID == kAudioObjectUnknown) return;
CAHALAudioDevice device(devID);
AudioObjectPropertyScope scope = kAudioDevicePropertyScopeOutput;
if (device.HasVolumeControl(scope, kMasterChannel)) {
vol = device.GetVolumeControlScalarValue(scope, kMasterChannel);
}
});
return vol;
}
- (void) setVolume:(float)vol {
if (vol < 0.0f) vol = 0.0f;
if (vol > 1.0f) vol = 1.0f;
VCLogAndSwallowExceptions("setVolume", [&] {
AudioObjectID devID = [self currentDefaultOutputDevice];
if (devID == kAudioObjectUnknown) return;
CAHALAudioDevice device(devID);
AudioObjectPropertyScope scope = kAudioDevicePropertyScopeOutput;
if (device.HasVolumeControl(scope, kMasterChannel)) {
device.SetVolumeControlScalarValue(scope, kMasterChannel, vol);
}
});
}
- (BOOL) isMuted {
BOOL muted = NO;
VCLogAndSwallowExceptions("isMuted", [&] {
AudioObjectID devID = [self currentDefaultOutputDevice];
if (devID == kAudioObjectUnknown) return;
CAHALAudioDevice device(devID);
AudioObjectPropertyScope scope = kAudioDevicePropertyScopeOutput;
if (device.HasMuteControl(scope, kMasterChannel)) {
muted = device.GetMuteControlValue(scope, kMasterChannel);
}
});
return muted;
}
- (void) setMuted:(BOOL)muted {
VCLogAndSwallowExceptions("setMuted", [&] {
AudioObjectID devID = [self currentDefaultOutputDevice];
if (devID == kAudioObjectUnknown) return;
CAHALAudioDevice device(devID);
AudioObjectPropertyScope scope = kAudioDevicePropertyScopeOutput;
if (device.HasMuteControl(scope, kMasterChannel)) {
device.SetMuteControlValue(scope, kMasterChannel, muted);
}
});
}
#pragma mark Volume Persistence
- (void) restoreVolume {
Float32 saved = [[NSUserDefaults standardUserDefaults] floatForKey:kVCVolumeKey];
if (saved <= 0.0f) {
saved = 0.5f; // Default if never saved.
}
VCLogAndSwallowExceptions("restoreVolume", [&] {
AudioObjectPropertyScope scope = kAudioDevicePropertyScopeOutput;
if (vcDevice->HasVolumeControl(scope, kMasterChannel)) {
vcDevice->SetVolumeControlScalarValue(scope, kMasterChannel, saved);
}
});
}
- (void) saveVolume {
VCLogAndSwallowExceptions("saveVolume", [&] {
AudioObjectPropertyScope scope = kAudioDevicePropertyScopeOutput;
if (vcDevice->HasVolumeControl(scope, kMasterChannel)) {
Float32 vol = vcDevice->GetVolumeControlScalarValue(scope, kMasterChannel);
[[NSUserDefaults standardUserDefaults] setFloat:vol forKey:kVCVolumeKey];
}
});
}
- (void) startVolumeListenerOnDevice:(AudioObjectID)deviceID {
[self stopVolumeListener];
VCAudioDeviceManager* __weak weakSelf = self;
volumeListenerBlock = ^(UInt32 inNumberAddresses,
const AudioObjectPropertyAddress* inAddresses) {
#pragma unused (inNumberAddresses, inAddresses)
VCAudioDeviceManager* strongSelf = weakSelf;
if (!strongSelf) return;
if (strongSelf->virtualDeviceActive) {
[strongSelf saveVolume];
}
if (strongSelf.onVolumeChanged) {
dispatch_async(dispatch_get_main_queue(), ^{
strongSelf.onVolumeChanged();
});
}
};
listenedDeviceID = deviceID;
VCLogAndSwallowExceptions("startVolumeListener", [&] {
CAHALAudioDevice device(deviceID);
device.AddPropertyListenerBlock(
CAPropertyAddress(kAudioDevicePropertyVolumeScalar,
kAudioDevicePropertyScopeOutput,
kMasterChannel),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
volumeListenerBlock);
});
}
- (void) stopVolumeListener {
if (!volumeListenerBlock) return;
VCLogAndSwallowExceptions("stopVolumeListener", [&] {
if (CAHALAudioObject::ObjectExists(listenedDeviceID)) {
CAHALAudioDevice device(listenedDeviceID);
device.RemovePropertyListenerBlock(
CAPropertyAddress(kAudioDevicePropertyVolumeScalar,
kAudioDevicePropertyScopeOutput,
kMasterChannel),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
volumeListenerBlock);
}
});
volumeListenerBlock = nil;
listenedDeviceID = kAudioObjectUnknown;
}
@end
#pragma clang assume_nonnull end

1250
app/VCPlayThrough.cpp Normal file

File diff suppressed because it is too large Load Diff

238
app/VCPlayThrough.h Normal file
View File

@@ -0,0 +1,238 @@
// 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/>.
//
// VCPlayThrough.h
// VCApp
//
// Copyright © 2016, 2017, 2020 Kyle Neideck
//
// Reads audio from an input device and immediately writes it to an output device. We currently use this class with the input
// device always set to VCDevice and the output device set to the one selected in the preferences menu.
//
// Apple's CAPlayThrough sample code (https://developer.apple.com/library/mac/samplecode/CAPlayThrough/Introduction/Intro.html)
// has a similar class, but I couldn't get it fast enough to use here. Soundflower also has a similar class
// (https://github.com/mattingalls/Soundflower/blob/master/SoundflowerBed/AudioThruEngine.h) that seems to be based on Apple
// sample code from 2004. This class's main addition is pausing playthrough when idle to save CPU.
//
// Playing audio with this class uses more CPU, mostly in the coreaudiod process, than playing audio normally because we need
// an input IOProc as well as an output one, and VCDriver is running in addition to the output device's driver. For me, it
// usually adds around 1-2% (as a percentage of total usage -- it doesn't seem to be relative to the CPU used when playing
// audio normally).
//
// This class will hopefully not be needed after CoreAudio's aggregate devices get support for controls, which is planned for
// a future release.
//
#ifndef VCApp__VCPlayThrough
#define VCApp__VCPlayThrough
// Local Includes
#include "VCAudioDevice.h"
#include "VCPlayThroughRTLogger.h"
// PublicUtility Includes
#include "CAMutex.h"
#include "CARingBuffer.h"
#include "VCThreadSafetyAnalysis.h"
// STL Includes
#include <atomic>
#include <algorithm>
#include <memory>
// System Includes
#include <mach/semaphore.h>
#pragma clang assume_nonnull begin
class VCPlayThrough
{
public:
// Error codes
static const OSStatus kDeviceNotStarting = 100;
public:
VCPlayThrough(VCAudioDevice inInputDevice, VCAudioDevice inOutputDevice);
~VCPlayThrough();
// Disallow copying
VCPlayThrough(const VCPlayThrough&) = delete;
VCPlayThrough& operator=(const VCPlayThrough&) = delete;
#ifdef __OBJC__
// Only intended as a convenience (hack) for Objective-C instance vars. Call
// SetDevices to initialise the instance before using it.
VCPlayThrough() { }
#endif
private:
/*! @throws CAException */
void Init(VCAudioDevice inInputDevice, VCAudioDevice inOutputDevice)
REQUIRES(mStateMutex);
public:
/*! @throws CAException */
void Activate();
/*! @throws CAException */
void Deactivate();
private:
void AllocateBuffer() REQUIRES(mStateMutex);
void DeallocateBuffer();
/*! @throws CAException */
void CreateIOProcIDs();
/*! @throws CAException */
void DestroyIOProcIDs();
/*!
@return True if both IOProcs are stopped.
@nonthreadsafe
*/
bool CheckIOProcsAreStopped() const noexcept REQUIRES(mStateMutex);
public:
/*!
Pass null for either param to only change one of the devices.
@throws CAException
*/
void SetDevices(const VCAudioDevice* __nullable inInputDevice,
const VCAudioDevice* __nullable inOutputDevice);
/*! @throws CAException */
void Start();
// Blocks until the output device has started our IOProc. Returns one of the error constants
// from AudioHardwareBase.h (e.g. kAudioHardwareNoError).
OSStatus WaitForOutputDeviceToStart() noexcept;
private:
/*! Real-time safe. */
void ReleaseThreadsWaitingForOutputToStart();
public:
OSStatus Stop();
void StopIfIdle();
private:
static OSStatus VCDeviceListenerProc(AudioObjectID inObjectID,
UInt32 inNumberAddresses,
const AudioObjectPropertyAddress* inAddresses,
void* __nullable inClientData);
static void HandleVCDeviceIsRunning(VCPlayThrough* refCon);
static void HandleVCDeviceIsRunningSomewhereOtherThanVCApp(VCPlayThrough* refCon);
static bool IsRunningSomewhereOtherThanVCApp(const VCAudioDevice& inVCDevice);
static OSStatus InputDeviceIOProc(AudioObjectID inDevice,
const AudioTimeStamp* inNow,
const AudioBufferList* inInputData,
const AudioTimeStamp* inInputTime,
AudioBufferList* outOutputData,
const AudioTimeStamp* inOutputTime,
void* __nullable inClientData);
static OSStatus OutputDeviceIOProc(AudioObjectID inDevice,
const AudioTimeStamp* inNow,
const AudioBufferList* inInputData,
const AudioTimeStamp* inInputTime,
AudioBufferList* outOutputData,
const AudioTimeStamp* inOutputTime,
void* __nullable inClientData);
/*! Fills the given ABL with zeroes to make it silent. */
static inline void FillWithSilence(AudioBufferList* ioBuffer);
// The state of an IOProc. Used by the IOProc to tell other threads when it's finished starting. Used by other
// threads to tell the IOProc to stop itself. (Probably used for other things as well.)
enum class IOState
{
Stopped, Starting, Running, Stopping
};
// The IOProcs call this to update their IOState member. Also stops the IOProc if its state has been set to Stopping.
// Returns true if it changes the state.
static bool UpdateIOProcState(const char* inCallerName,
VCPlayThroughRTLogger& inRTLogger,
std::atomic<IOState>& inState,
AudioDeviceIOProcID __nullable inIOProcID,
VCAudioDevice& inDevice,
IOState& outNewState);
private:
std::unique_ptr<CARingBuffer> mBuffer PT_GUARDED_BY(mBufferInputMutex)
PT_GUARDED_BY(mBufferOutputMutex) { nullptr };
AudioDeviceIOProcID __nullable mInputDeviceIOProcID { nullptr };
AudioDeviceIOProcID __nullable mOutputDeviceIOProcID { nullptr };
VCAudioDevice mInputDevice { kAudioObjectUnknown };
VCAudioDevice mOutputDevice { kAudioObjectUnknown };
// mStateMutex is the general purpose mutex. mBufferInputMutex and mBufferOutputMutex are
// just used to make sure mBuffer, the ring buffer, is allocated when the IOProcs access it. See
// the comments in the IOProcs for details.
//
// If a thread might lock more than one of these mutexes, it *must* take them in this order:
// 1. mStateMutex
// 2. mBufferInputMutex
// 3. mBufferOutputMutex
//
// The ACQUIRED_BEFORE annotations don't do anything yet. From clang's docs: "ACQUIRED_BEFORE(…)
// and ACQUIRED_AFTER(…) are currently unimplemented. To be fixed in a future update." After
// they've fixed that, the compiler will enforce the ordering statically.
//
// TODO: We can't use std::shared_lock because we're still on C++11, but we could use std::lock
// to help ensure the locks are always taken in the right order.
// TODO: It would be better to have a separate class for the buffer and its mutexes.
CAMutex mStateMutex ACQUIRED_BEFORE(mBufferInputMutex)
ACQUIRED_BEFORE(mBufferOutputMutex) { "Playthrough state" };
CAMutex mBufferInputMutex ACQUIRED_BEFORE(mBufferOutputMutex)
{ "Playthrough ring buffer input" };
CAMutex mBufferOutputMutex { "Playthrough ring buffer output" };
// Signalled when the output IOProc runs. We use it to tell VCDriver when the output device is ready to receive audio data.
semaphore_t mOutputDeviceIOProcSemaphore { SEMAPHORE_NULL };
bool mActive = false;
bool mPlayingThrough = false;
UInt64 mLastNotifiedIOStoppedOnVCDevice { 0 };
std::atomic<IOState> mInputDeviceIOProcState { IOState::Stopped };
std::atomic<IOState> mOutputDeviceIOProcState { IOState::Stopped };
// For debug logging.
UInt64 mToldOutputDeviceToStartAt { 0 };
// IOProc vars. (Should only be used inside IOProcs.)
// The earliest/latest sample times seen by the IOProcs since starting playthrough. -1 for unset.
Float64 mFirstInputSampleTime = -1;
Float64 mLastInputSampleTime = -1;
Float64 mLastOutputSampleTime = -1;
// Subtract this from the output time to get the input time.
Float64 mInToOutSampleOffset { 0.0 };
VCPlayThroughRTLogger mRTLogger;
};
#pragma clang assume_nonnull end
#endif /* VCApp__VCPlayThrough */

View File

@@ -0,0 +1,521 @@
// 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/>.
//
// VCPlayThroughRTLogger.cpp
// VCApp
//
// Copyright © 2020 Kyle Neideck
//
// Self Include
#include "VCPlayThroughRTLogger.h"
// Local Includes
#include "VC_Utils.h"
// PublicUtility Includes
#include "CADebugMacros.h"
// STL Includes
#include <atomic>
// System Includes
#include <CoreAudio/CoreAudio.h>
#include <mach/mach_init.h>
#include <mach/task.h>
#include <unistd.h>
#pragma clang assume_nonnull begin
// Track the number of messages logged when built for the unit tests.
#if VC_UnitTest
#define LogSync_Debug(inFormat, ...) do { \
mNumDebugMessagesLogged++; \
DebugMsg(inFormat, ## __VA_ARGS__); \
} while (0)
#else
#define LogSync_Debug(inFormat, ...) DebugMsg(inFormat, ## __VA_ARGS__)
#endif
#pragma mark Construction/Destruction
VCPlayThroughRTLogger::VCPlayThroughRTLogger()
{
// Create the semaphore we use to wake up the logging thread when it has messages to log.
mWakeUpLoggingThreadSemaphore = CreateSemaphore();
// Create the logging thread last because it starts immediately and expects the other member
// variables to be initialised.
mLoggingThread = std::thread(&VCPlayThroughRTLogger::LoggingThreadEntry, this);
}
// static
semaphore_t VCPlayThroughRTLogger::CreateSemaphore()
{
// TODO: Make a VCMachSemaphore class to reduce some of this repetitive semaphore code.
// Create the semaphore.
semaphore_t semaphore;
kern_return_t error =
semaphore_create(mach_task_self(), &semaphore, SYNC_POLICY_FIFO, 0);
// Check the error code.
VC_Utils::ThrowIfMachError("VCPlayThroughRTLogger::CreateSemaphore",
"semaphore_create",
error);
ThrowIf(semaphore == SEMAPHORE_NULL,
CAException(kAudioHardwareUnspecifiedError),
"VCPlayThroughRTLogger::CreateSemaphore: Failed to create semaphore");
return semaphore;
}
VCPlayThroughRTLogger::~VCPlayThroughRTLogger()
{
// Stop the logging thread.
mLoggingThreadShouldExit = true;
kern_return_t error = semaphore_signal(mWakeUpLoggingThreadSemaphore);
VC_Utils::LogIfMachError("VCPlayThroughRTLogger::~VCPlayThroughRTLogger",
"semaphore_signal",
error);
if(error == KERN_SUCCESS)
{
// Wait for it to stop.
mLoggingThread.join();
// Destroy the semaphore.
error = semaphore_destroy(mach_task_self(), mWakeUpLoggingThreadSemaphore);
VC_Utils::LogIfMachError("VCPlayThroughRTLogger::~VCPlayThroughRTLogger",
"semaphore_destroy",
error);
}
else
{
// If we couldn't tell it to wake up, it's not safe to wait for it to stop or to destroy the
// semaphore. We have to detach it so its destructor doesn't cause a crash.
mLoggingThread.detach();
}
}
#pragma mark Log Messages
void VCPlayThroughRTLogger::LogReleasingWaitingThreads()
{
if(!VCDebugLoggingIsEnabled())
{
return;
}
if(!mLogReleasingWaitingThreadsMsg.is_lock_free())
{
// Modifying mLogReleasingWaitingThreadsMsg might cause the thread to lock a mutex that
// isn't safe to lock on a realtime thread, so just give up.
return;
}
// Set the flag that tells the logging thread to log the message.
mLogReleasingWaitingThreadsMsg = true;
// Wake the logging thread so it can log the message.
WakeLoggingThread();
}
void VCPlayThroughRTLogger::LogIfMachError_ReleaseWaitingThreadsSignal(mach_error_t inError)
{
if(inError == KERN_SUCCESS)
{
// No error.
return;
}
if(!mReleaseWaitingThreadsSignalError.is_lock_free())
{
// Modifying mReleaseWaitingThreadsSignalError might cause the thread to lock a mutex that
// isn't safe to lock on a realtime thread, so just give up.
return;
}
mReleaseWaitingThreadsSignalError = inError;
WakeLoggingThread();
}
void VCPlayThroughRTLogger::LogIfDroppedFrames(Float64 inFirstInputSampleTime,
Float64 inLastInputSampleTime)
{
if(inFirstInputSampleTime == inLastInputSampleTime || !VCDebugLoggingIsEnabled())
{
// Either we didn't drop any initial frames or we don't need to log a message about it.
return;
}
LogAsync(mDroppedFrames, [&]()
{
// Store the data to include in the log message.
mDroppedFrames.firstInputSampleTime = inFirstInputSampleTime;
mDroppedFrames.lastInputSampleTime = inLastInputSampleTime;
});
}
void VCPlayThroughRTLogger::LogNoSamplesReady(CARingBuffer::SampleTime inLastInputSampleTime,
CARingBuffer::SampleTime inReadHeadSampleTime,
Float64 inInToOutSampleOffset)
{
if(!VCDebugLoggingIsEnabled())
{
return;
}
LogAsync(mNoSamplesReady, [&]()
{
// Store the data to include in the log message.
mNoSamplesReady.lastInputSampleTime = inLastInputSampleTime;
mNoSamplesReady.readHeadSampleTime = inReadHeadSampleTime;
mNoSamplesReady.inToOutSampleOffset = inInToOutSampleOffset;
});
}
void VCPlayThroughRTLogger::LogExceptionStoppingIOProc(const char* inCallerName,
OSStatus inError,
bool inErrorKnown)
{
LogAsync(mExceptionStoppingIOProc, [&]()
{
// Store the data to include in the log message.
mExceptionStoppingIOProc.callerName = inCallerName;
mExceptionStoppingIOProc.error = inError;
mExceptionStoppingIOProc.errorKnown = inErrorKnown;
});
}
void VCPlayThroughRTLogger::LogUnexpectedIOStateAfterStopping(const char* inCallerName,
int inIOState)
{
LogAsync(mUnexpectedIOStateAfterStopping, [&]()
{
// Store the data to include in the log message.
mUnexpectedIOStateAfterStopping.callerName = inCallerName;
mUnexpectedIOStateAfterStopping.ioState = inIOState;
});
}
void VCPlayThroughRTLogger::LogRingBufferUnavailable(const char* inCallerName, bool inGotLock)
{
LogAsync(mRingBufferUnavailable, [&]()
{
// Store the data to include in the log message.
mRingBufferUnavailable.callerName = inCallerName;
mRingBufferUnavailable.gotLock = inGotLock;
});
}
void VCPlayThroughRTLogger::LogIfRingBufferError(CARingBufferError inError,
std::atomic<CARingBufferError>& outError)
{
if(inError == kCARingBufferError_OK)
{
// No error.
return;
}
if(!outError.is_lock_free())
{
// Modifying outError might cause the thread to lock a mutex that isn't safe to lock on
// a realtime thread, so just give up.
return;
}
// Store the error.
outError = inError;
// Wake the logging thread so it can log the error.
WakeLoggingThread();
}
template <typename T, typename F>
void VCPlayThroughRTLogger::LogAsync(T& inMessageData, F&& inStoreMessageData)
{
if(!inMessageData.shouldLogMessage.is_lock_free())
{
// Modifying shouldLogMessage might cause the thread to lock a mutex that isn't safe to
// lock on a realtime thread, so just give up.
return;
}
if(inMessageData.shouldLogMessage)
{
// The logging thread could be reading inMessageData.
return;
}
// Store the data to include in the log message.
//
// std::forward lets the compiler treat inStoreMessageData as an rvalue if the caller gave it as
// an rvalue. No idea if that actually does anything.
std::forward<F>(inStoreMessageData)();
// shouldLogMessage is a std::atomic, so this store also makes sure that the non-atomic stores
// in inStoreMessageData will be visible to the logger thread (since the default memory order is
// memory_order_seq_cst).
inMessageData.shouldLogMessage = true;
WakeLoggingThread();
}
void VCPlayThroughRTLogger::LogSync_Warning(const char* inFormat, ...)
{
va_list args;
va_start(args, inFormat);
#if VC_UnitTest
mNumWarningMessagesLogged++;
#endif
vLogWarning(inFormat, args);
va_end(args);
}
void VCPlayThroughRTLogger::LogSync_Error(const char* inFormat, ...)
{
va_list args;
va_start(args, inFormat);
#if VC_UnitTest
mNumErrorMessagesLogged++;
if(!mContinueOnErrorLogged)
{
vLogError(inFormat, args);
}
#else
vLogError(inFormat, args);
#endif
va_end(args);
}
#pragma mark Logging Thread
void VCPlayThroughRTLogger::WakeLoggingThread()
{
kern_return_t error = semaphore_signal(mWakeUpLoggingThreadSemaphore);
VCAssert(error == KERN_SUCCESS, "semaphore_signal (%d)", error);
// We can't do anything useful with the error in release builds. At least, not easily.
(void)error;
}
void VCPlayThroughRTLogger::LogMessages()
{
// Log the messages/errors from the realtime threads (if any).
LogSync_ReleasingWaitingThreads();
LogSync_ReleaseWaitingThreadsSignalError();
LogSync_DroppedFrames();
LogSync_NoSamplesReady();
LogSync_ExceptionStoppingIOProc();
LogSync_UnexpectedIOStateAfterStopping();
LogSync_RingBufferUnavailable();
LogSync_RingBufferError(mRingBufferStoreError, "InputDeviceIOProc");
LogSync_RingBufferError(mRingBufferFetchError, "OutputDeviceIOProc");
}
void VCPlayThroughRTLogger::LogSync_ReleasingWaitingThreads()
{
if(mLogReleasingWaitingThreadsMsg)
{
LogSync_Debug("VCPlayThrough::ReleaseThreadsWaitingForOutputToStart: "
"Releasing waiting threads");
// Reset it.
mLogReleasingWaitingThreadsMsg = false;
}
}
void VCPlayThroughRTLogger::LogSync_ReleaseWaitingThreadsSignalError()
{
if(mReleaseWaitingThreadsSignalError != KERN_SUCCESS)
{
VC_Utils::LogIfMachError("VCPlayThrough::ReleaseThreadsWaitingForOutputToStart",
"semaphore_signal_all",
mReleaseWaitingThreadsSignalError);
// Reset it.
mReleaseWaitingThreadsSignalError = KERN_SUCCESS;
}
}
void VCPlayThroughRTLogger::LogSync_DroppedFrames()
{
if(mDroppedFrames.shouldLogMessage)
{
LogSync_Debug("VCPlayThrough::OutputDeviceIOProc: "
"Dropped %f frames before output started. %s%f %s%f",
(mDroppedFrames.lastInputSampleTime - mDroppedFrames.firstInputSampleTime),
"mFirstInputSampleTime=",
mDroppedFrames.firstInputSampleTime,
"mLastInputSampleTime=",
mDroppedFrames.lastInputSampleTime);
mDroppedFrames.shouldLogMessage = false;
}
}
void VCPlayThroughRTLogger::LogSync_NoSamplesReady()
{
if(mNoSamplesReady.shouldLogMessage)
{
LogSync_Debug("VCPlayThrough::OutputDeviceIOProc: "
"No input samples ready at output sample time. %s%lld %s%lld %s%f",
"lastInputSampleTime=", mNoSamplesReady.lastInputSampleTime,
"readHeadSampleTime=", mNoSamplesReady.readHeadSampleTime,
"mInToOutSampleOffset=", mNoSamplesReady.inToOutSampleOffset);
mNoSamplesReady.shouldLogMessage = false;
}
}
void VCPlayThroughRTLogger::LogSync_ExceptionStoppingIOProc()
{
if(mExceptionStoppingIOProc.shouldLogMessage)
{
const char error4CC[5] = CA4CCToCString(mExceptionStoppingIOProc.error);
LogSync_Error("VCPlayThrough::UpdateIOProcState: "
"Exception while stopping IOProc %s: %s (%d)",
mExceptionStoppingIOProc.callerName,
mExceptionStoppingIOProc.errorKnown ? error4CC : "unknown",
mExceptionStoppingIOProc.error);
mExceptionStoppingIOProc.shouldLogMessage = false;
}
}
void VCPlayThroughRTLogger::LogSync_UnexpectedIOStateAfterStopping()
{
if(mUnexpectedIOStateAfterStopping.shouldLogMessage)
{
LogSync_Warning("VCPlayThrough::UpdateIOProcState: "
"%s IO state changed since last read. state = %d",
mUnexpectedIOStateAfterStopping.callerName,
mUnexpectedIOStateAfterStopping.ioState);
mUnexpectedIOStateAfterStopping.shouldLogMessage = false;
}
}
void VCPlayThroughRTLogger::LogSync_RingBufferUnavailable()
{
if(mRingBufferUnavailable.shouldLogMessage)
{
LogSync_Warning("VCPlayThrough::%s: Ring buffer unavailable. %s",
mRingBufferUnavailable.callerName,
mRingBufferUnavailable.gotLock ?
"No buffer currently allocated." :
"Buffer locked for allocation/deallocation by another thread.");
mRingBufferUnavailable.shouldLogMessage = false;
}
}
void VCPlayThroughRTLogger::LogSync_RingBufferError(
std::atomic<CARingBufferError>& ioRingBufferError,
const char* inMethodName)
{
CARingBufferError error = ioRingBufferError;
switch(error)
{
case kCARingBufferError_OK:
// No error.
return;
case kCARingBufferError_CPUOverload:
// kCARingBufferError_CPUOverload might not be our fault, so just log a warning.
LogSync_Warning("VCPlayThrough::%s: Ring buffer error: "
"kCARingBufferError_CPUOverload (%d)",
inMethodName,
error);
break;
default:
// Other types of CARingBuffer errors should never occur. This will crash debug builds.
LogSync_Error("VCPlayThrough::%s: Ring buffer error: %s (%d)",
inMethodName,
(error == kCARingBufferError_TooMuch ?
"kCARingBufferError_TooMuch" :
"unknown error"),
error);
break;
};
// Reset it.
ioRingBufferError = kCARingBufferError_OK;
}
// static
void* __nullable VCPlayThroughRTLogger::LoggingThreadEntry(VCPlayThroughRTLogger* inRefCon)
{
DebugMsg("VCPlayThroughRTLogger::IOProcLoggingThreadEntry: "
"Starting the IOProc logging thread");
while(!inRefCon->mLoggingThreadShouldExit)
{
// Log the messages, if there are any to log.
inRefCon->LogMessages();
// Wait until woken up.
kern_return_t error = semaphore_wait(inRefCon->mWakeUpLoggingThreadSemaphore);
VC_Utils::LogIfMachError("VCPlayThroughRTLogger::IOProcLoggingThreadEntry",
"semaphore_wait",
error);
}
DebugMsg("VCPlayThroughRTLogger::IOProcLoggingThreadEntry: IOProc logging thread exiting");
return nullptr;
}
#if VC_UnitTest
#pragma mark Test Helpers
bool VCPlayThroughRTLogger::WaitUntilLoggerThreadIdle()
{
int msWaited = 0;
while(mLogReleasingWaitingThreadsMsg ||
mReleaseWaitingThreadsSignalError != KERN_SUCCESS ||
mDroppedFrames.shouldLogMessage ||
mNoSamplesReady.shouldLogMessage ||
mUnexpectedIOStateAfterStopping.shouldLogMessage ||
mRingBufferUnavailable.shouldLogMessage ||
mExceptionStoppingIOProc.shouldLogMessage ||
mRingBufferStoreError != kCARingBufferError_OK ||
mRingBufferFetchError != kCARingBufferError_OK)
{
// Poll until the logger thread has nothing left to log. (Ideally we'd use a semaphore
// instead of polling, but it isn't worth the effort at this point.)
usleep(10 * 1000);
msWaited += 10;
// Time out after 5 seconds.
if(msWaited > 5000)
{
return false;
}
}
return true;
}
#endif /* VC_UnitTest */
#pragma clang assume_nonnull end

227
app/VCPlayThroughRTLogger.h Normal file
View File

@@ -0,0 +1,227 @@
// 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/>.
//
// VCPlayThroughRTLogger.h
// VCApp
//
// Copyright © 2020 Kyle Neideck
//
// A real-time safe logger for VCPlayThrough. The messages are logged asynchronously by a
// non-realtime thread.
//
// For the sake of simplicity, this class is very closely coupled with VCPlayThrough and its
// methods make assumptions about where they will be called. Also, if the same logging method is
// called multiple times before the logging thread next checks for messages, it will only log the
// message for one of those calls and ignore the others.
//
// This class's methods are real-time safe in that they return in a bounded amount of time and we
// think they're probably fast enough that the callers won't miss their deadlines, but we don't try
// to guarantee it. Some of them should only be called in unusual cases where it's worth increasing
// the risk of a thread missing its deadline.
//
#ifndef VCApp__VCPlayThroughRTLogger
#define VCApp__VCPlayThroughRTLogger
// PublicUtility Includes
#include "CARingBuffer.h"
// STL Includes
#include <thread>
// System Includes
#include <mach/error.h>
#include <mach/semaphore.h>
#pragma clang assume_nonnull begin
class VCPlayThroughRTLogger
{
#pragma mark Construction/Destruction
public:
VCPlayThroughRTLogger();
~VCPlayThroughRTLogger();
VCPlayThroughRTLogger(const VCPlayThroughRTLogger&) = delete;
VCPlayThroughRTLogger& operator=(
const VCPlayThroughRTLogger&) = delete;
private:
static semaphore_t CreateSemaphore();
#pragma mark Log Messages
public:
/*! For VCPlayThrough::ReleaseThreadsWaitingForOutputToStart. */
void LogReleasingWaitingThreads();
/*! For VCPlayThrough::ReleaseThreadsWaitingForOutputToStart. */
void LogIfMachError_ReleaseWaitingThreadsSignal(mach_error_t inError);
/*! For VCPlayThrough::OutputDeviceIOProc. Not thread-safe. */
void LogIfDroppedFrames(Float64 inFirstInputSampleTime,
Float64 inLastInputSampleTime);
/*! For VCPlayThrough::OutputDeviceIOProc. Not thread-safe. */
void LogNoSamplesReady(CARingBuffer::SampleTime inLastInputSampleTime,
CARingBuffer::SampleTime inReadHeadSampleTime,
Float64 inInToOutSampleOffset);
/*! For VCPlayThrough::UpdateIOProcState. Not thread-safe. */
void LogExceptionStoppingIOProc(const char* inCallerName)
{
LogExceptionStoppingIOProc(inCallerName, noErr, false);
}
/*! For VCPlayThrough::UpdateIOProcState. Not thread-safe. */
void LogExceptionStoppingIOProc(const char* inCallerName, OSStatus inError)
{
LogExceptionStoppingIOProc(inCallerName, inError, true);
}
private:
void LogExceptionStoppingIOProc(const char* inCallerName,
OSStatus inError,
bool inErrorKnown);
public:
/*! For VCPlayThrough::UpdateIOProcState. Not thread-safe. */
void LogUnexpectedIOStateAfterStopping(const char* inCallerName,
int inIOState);
/*! For VCPlayThrough::InputDeviceIOProc and VCPlayThrough::OutputDeviceIOProc. */
void LogRingBufferUnavailable(const char* inCallerName, bool inGotLock);
/*! For VCPlayThrough::OutputDeviceIOProc. */
void LogIfRingBufferError_Fetch(CARingBufferError inError)
{
LogIfRingBufferError(inError, mRingBufferFetchError);
}
/*! For VCPlayThrough::InputDeviceIOProc. */
void LogIfRingBufferError_Store(CARingBufferError inError)
{
LogIfRingBufferError(inError, mRingBufferStoreError);
}
private:
void LogIfRingBufferError(CARingBufferError inError,
std::atomic<CARingBufferError>& outError);
template <typename T, typename F>
void LogAsync(T& inMessageData, F&& inStoreMessageData);
// Wrapper methods used to mock out the logging for unit tests.
void LogSync_Warning(const char* inFormat, ...) __printflike(2, 3);
void LogSync_Error(const char* inFormat, ...) __printflike(2, 3);
#pragma mark Logging Thread
private:
void WakeLoggingThread();
void LogMessages();
void LogSync_ReleasingWaitingThreads();
void LogSync_ReleaseWaitingThreadsSignalError();
void LogSync_DroppedFrames();
void LogSync_NoSamplesReady();
void LogSync_ExceptionStoppingIOProc();
void LogSync_UnexpectedIOStateAfterStopping();
void LogSync_RingBufferUnavailable();
void LogSync_RingBufferError(
std::atomic<CARingBufferError>& ioRingBufferError,
const char* inMethodName);
// The entry point of the logging thread (mLoggingThread).
static void* __nullable LoggingThreadEntry(VCPlayThroughRTLogger* inRefCon);
#if VC_UnitTest
#pragma mark Test Helpers
public:
/*!
* @return True if the logger thread finished logging the requested messages. False if it still
* had messages to log after 5 seconds.
*/
bool WaitUntilLoggerThreadIdle();
#endif /* VC_UnitTest */
private:
// For VCPlayThrough::ReleaseThreadsWaitingForOutputToStart
std::atomic<bool> mLogReleasingWaitingThreadsMsg { false };
std::atomic<kern_return_t> mReleaseWaitingThreadsSignalError { KERN_SUCCESS };
// For VCPlayThrough::InputDeviceIOProc and VCPlayThrough::OutputDeviceIOProc
struct {
Float64 firstInputSampleTime;
Float64 lastInputSampleTime;
std::atomic<bool> shouldLogMessage { false };
} mDroppedFrames;
struct {
CARingBuffer::SampleTime lastInputSampleTime;
CARingBuffer::SampleTime readHeadSampleTime;
Float64 inToOutSampleOffset;
std::atomic<bool> shouldLogMessage { false };
} mNoSamplesReady;
struct {
const char* callerName;
bool gotLock;
std::atomic<bool> shouldLogMessage { false };
} mRingBufferUnavailable;
// For VCPlayThrough::UpdateIOProcState
struct {
const char* callerName;
int ioState;
std::atomic<bool> shouldLogMessage { false };
} mUnexpectedIOStateAfterStopping;
struct {
const char* callerName;
OSStatus error;
bool errorKnown; // If false, we didn't get an error code from the exception.
std::atomic<bool> shouldLogMessage { false };
} mExceptionStoppingIOProc;
// For VCPlayThrough::OutputDeviceIOProc
std::atomic<CARingBufferError> mRingBufferStoreError { kCARingBufferError_OK };
// For VCPlayThrough::InputDeviceIOProc.
std::atomic<CARingBufferError> mRingBufferFetchError { kCARingBufferError_OK };
// Signalled to wake up the mLoggingThread when it has messages to log.
semaphore_t mWakeUpLoggingThreadSemaphore;
std::atomic<bool> mLoggingThreadShouldExit { false };
// The thread that actually logs the messages.
std::thread mLoggingThread;
#if VC_UnitTest
public:
// Tests normally crash (abort) if LogError is called. This flag lets us test the code that
// would otherwise call LogError.
bool mContinueOnErrorLogged { false };
int mNumDebugMessagesLogged { 0 };
int mNumWarningMessagesLogged { 0 };
int mNumErrorMessagesLogged { 0 };
#endif /* VC_UnitTest */
};
#pragma clang assume_nonnull end
#endif /* VCApp__VCPlayThroughRTLogger */

33
app/VCTermination.h Normal file
View File

@@ -0,0 +1,33 @@
// 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.
#ifndef VCApp__VCTermination
#define VCApp__VCTermination
#import "VCAudioDeviceManager.h"
#pragma clang assume_nonnull begin
class VCTermination {
public:
// Sets up signal handlers (SIGINT, SIGTERM, SIGQUIT) and std::terminate handler
// to restore the default audio device before exiting.
static void SetUpTerminationCleanUp(VCAudioDeviceManager* inAudioDevices);
private:
static void CleanUpAudioDevices();
static void* __nullable ExitSignalsProc(void* __nullable ignored);
static sigset_t sExitSignals;
static pthread_t sExitSignalsThread;
static std::terminate_handler sOriginalTerminateHandler;
static VCAudioDeviceManager* __nullable sAudioDevices;
};
#pragma clang assume_nonnull end
#endif /* VCApp__VCTermination */

69
app/VCTermination.mm Normal file
View File

@@ -0,0 +1,69 @@
// 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.
#import "VCTermination.h"
#import "VC_Utils.h"
#import <signal.h>
#import <pthread.h>
#import <exception>
#pragma clang assume_nonnull begin
std::terminate_handler VCTermination::sOriginalTerminateHandler = std::get_terminate();
sigset_t VCTermination::sExitSignals;
pthread_t VCTermination::sExitSignalsThread;
VCAudioDeviceManager* __nullable VCTermination::sAudioDevices = nullptr;
void VCTermination::SetUpTerminationCleanUp(VCAudioDeviceManager* inAudioDevices) {
sAudioDevices = inAudioDevices;
// Block SIGINT/SIGTERM/SIGQUIT so our dedicated thread can handle them.
sigemptyset(&sExitSignals);
sigaddset(&sExitSignals, SIGINT);
sigaddset(&sExitSignals, SIGTERM);
sigaddset(&sExitSignals, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &sExitSignals, nullptr);
pthread_create(&sExitSignalsThread, nullptr, ExitSignalsProc, nullptr);
// Wrap std::terminate to clean up before crashing.
sOriginalTerminateHandler = std::get_terminate();
std::set_terminate([] {
CleanUpAudioDevices();
sOriginalTerminateHandler();
});
}
void VCTermination::CleanUpAudioDevices() {
if (sAudioDevices && [sAudioDevices isVirtualDeviceActive]) {
[sAudioDevices unsetVCDeviceAsOSDefault];
}
}
void* __nullable VCTermination::ExitSignalsProc(void* __nullable ignored) {
#pragma unused (ignored)
int signal = -1;
while (signal != SIGINT && signal != SIGTERM && signal != SIGQUIT) {
if (sigwait(&sExitSignals, &signal) != 0) {
return nullptr;
}
}
NSLog(@"VolumeControl: Received signal %d, cleaning up...", signal);
CleanUpAudioDevices();
// Re-raise with default handler to exit.
pthread_sigmask(SIG_UNBLOCK, &sExitSignals, nullptr);
raise(signal);
return nullptr;
}
#pragma clang assume_nonnull end

119
app/VCVirtualDevice.cpp Normal file
View File

@@ -0,0 +1,119 @@
// 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/>.
//
// VCVirtualDevice.cpp
// VCApp
//
// Copyright © 2016-2019 Kyle Neideck
// Copyright © 2017 Andrew Tonner
//
// Self Include
#include "VCVirtualDevice.h"
// Local Includes
#include "VC_Types.h"
#include "VC_Utils.h"
// PublicUtility Includes
#include "CADebugMacros.h"
#include "CAHALAudioSystemObject.h"
#pragma clang assume_nonnull begin
#pragma mark Construction/Destruction
VCVirtualDevice::VCVirtualDevice()
:
VCAudioDevice(CFSTR(kVCDeviceUID))
{
if(GetObjectID() == kAudioObjectUnknown)
{
LogError("VCVirtualDevice::VCVirtualDevice: Error getting VCDevice ID");
Throw(CAException(kAudioHardwareIllegalOperationError));
}
};
VCVirtualDevice::~VCVirtualDevice()
{
}
#pragma mark Systemwide Default Device
void VCVirtualDevice::SetAsOSDefault()
{
DebugMsg("VCVirtualDevice::SetAsOSDefault: Setting the system's default audio device "
"to VCDevice");
CAHALAudioSystemObject audioSystem;
AudioDeviceID defaultDevice = audioSystem.GetDefaultAudioDevice(false, false);
AudioDeviceID systemDefaultDevice = audioSystem.GetDefaultAudioDevice(false, true);
if(systemDefaultDevice == defaultDevice)
{
// The default system device is the same as the default device, so change both of them.
audioSystem.SetDefaultAudioDevice(false, true, GetObjectID());
}
audioSystem.SetDefaultAudioDevice(false, false, GetObjectID());
}
void VCVirtualDevice::UnsetAsOSDefault(AudioDeviceID inOutputDeviceID)
{
CAHALAudioSystemObject audioSystem;
bool vcDeviceIsDefault =
(audioSystem.GetDefaultAudioDevice(false, false) == GetObjectID());
if(vcDeviceIsDefault)
{
DebugMsg("VCVirtualDevice::UnsetAsOSDefault: Setting the system's default output "
"device back to device %d", inOutputDeviceID);
audioSystem.SetDefaultAudioDevice(false, false, inOutputDeviceID);
}
bool vcDeviceIsSystemDefault =
(audioSystem.GetDefaultAudioDevice(false, true) == GetObjectID());
if(vcDeviceIsSystemDefault)
{
DebugMsg("VCVirtualDevice::UnsetAsOSDefault: Setting the system's default system "
"output device back to device %d", inOutputDeviceID);
audioSystem.SetDefaultAudioDevice(false, true, inOutputDeviceID);
}
}
void VCVirtualDevice::SetHidden(bool inHidden)
{
AudioObjectPropertyAddress addr = {
kAudioDeviceCustomPropertyVCHidden,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
CFBooleanRef value = inHidden ? kCFBooleanTrue : kCFBooleanFalse;
OSStatus err = AudioObjectSetPropertyData(GetObjectID(), &addr, 0, nullptr, sizeof(CFBooleanRef), &value);
if (err != kAudioHardwareNoError) {
LogError("VCVirtualDevice::SetHidden: Failed (%d)", err);
}
}
#pragma clang assume_nonnull end

85
app/VCVirtualDevice.h Normal file
View File

@@ -0,0 +1,85 @@
// 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/>.
//
// VCVirtualDevice.h
// VCApp
//
// Copyright © 2017 Kyle Neideck
//
// The interface to VCDevice, the main virtual device published by VCDriver.
//
// VCDevice is the device that appears as "Background Music" in programs that list the output
// devices, e.g. System Preferences. It receives the system's audio, processes it and sends it to
// VCApp by publishing an input stream. VCApp then plays the audio on the user's real output
// device.
//
// See VCDriver/VCDriver/VC_Device.h.
//
#ifndef VCApp__VCVirtualDevice
#define VCApp__VCVirtualDevice
// Superclass Includes
#include "VCAudioDevice.h"
// Local Includes
#include "VC_Types.h"
// PublicUtility Includes
#include "CACFString.h"
#pragma clang assume_nonnull begin
class VCVirtualDevice
:
public VCAudioDevice
{
#pragma mark Construction/Destruction
public:
/*!
@throws CAException If VCDevice is not found or the HAL returns an error when queried for
VCDevice's current Audio Object ID.
*/
VCVirtualDevice();
virtual ~VCVirtualDevice();
#pragma mark Systemwide Default Device
public:
/*!
Set VCDevice as the default audio device for all processes.
@throws CAException If the HAL responds with an error.
*/
void SetAsOSDefault();
/*!
Replace VCDevice as the default device with the output device.
@throws CAException If the HAL responds with an error.
*/
void UnsetAsOSDefault(AudioDeviceID inOutputDeviceID);
// Show or hide the virtual device in system device lists.
void SetHidden(bool inHidden);
};
#pragma clang assume_nonnull end
#endif /* VCApp__VCVirtualDevice */

169
app/main.mm Normal file
View File

@@ -0,0 +1,169 @@
// VolumeControl
//
// Provides software volume control for audio devices that lack hardware volume
// (e.g. HDMI outputs). Also works as a universal volume control for all devices.
#import <Cocoa/Cocoa.h>
#import <AVFoundation/AVCaptureDevice.h>
#import "VCAudioDeviceManager.h"
#import "VCTermination.h"
// Minimal app delegate — handles termination cleanup.
@interface VCAppDelegate : NSObject <NSApplicationDelegate>
@property (nonatomic) VCAudioDeviceManager* audioDevices;
@end
@implementation VCAppDelegate
- (void) applicationWillTerminate:(NSNotification*)note {
#pragma unused (note)
if ([self.audioDevices isVirtualDeviceActive]) {
[self.audioDevices unsetVCDeviceAsOSDefault];
}
}
@end
// Menu bar controller — speaker icon with scroll-to-adjust volume.
@interface VCMenuBar : NSObject
@property (nonatomic) NSStatusItem* statusItem;
@property (nonatomic) VCAudioDeviceManager* audioDevices;
@property (nonatomic) id scrollMonitor;
@end
@implementation VCMenuBar
- (void) setupWithAudioDevices:(VCAudioDeviceManager*)devices {
self.audioDevices = devices;
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[self updateIcon];
NSMenu* menu = [[NSMenu alloc] init];
NSMenuItem* label = [[NSMenuItem alloc] initWithTitle:@"VolumeControl" action:nil keyEquivalent:@""];
[label setEnabled:NO];
[menu addItem:label];
[menu addItem:[NSMenuItem separatorItem]];
[menu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
self.statusItem.menu = menu;
// Scroll on status bar icon to adjust volume.
self.scrollMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskScrollWheel
handler:^NSEvent*(NSEvent* event) {
// Only respond if the mouse is over our status item.
NSRect frame = self.statusItem.button.window.frame;
NSPoint mouse = [NSEvent mouseLocation];
if (!NSPointInRect(mouse, frame)) {
return event;
}
float delta = event.scrollingDeltaY;
if (event.hasPreciseScrollingDeltas) {
delta *= 0.002f; // Trackpad: fine-grained
} else {
delta *= 0.02f; // Mouse wheel: coarser steps
}
float vol = [self.audioDevices volume] + delta;
[self.audioDevices setVolume:vol];
[self updateIcon];
return nil; // Consume the event.
}];
}
- (void) dealloc {
if (self.scrollMonitor) {
[NSEvent removeMonitor:self.scrollMonitor];
}
}
- (void) updateIcon {
float vol = [self.audioDevices volume];
BOOL muted = [self.audioDevices isMuted];
NSString* symbolName;
if (muted || vol < 0.01f) {
symbolName = @"speaker.fill";
} else if (vol < 0.33f) {
symbolName = @"speaker.wave.1.fill";
} else if (vol < 0.66f) {
symbolName = @"speaker.wave.2.fill";
} else {
symbolName = @"speaker.wave.3.fill";
}
NSImage* icon = [NSImage imageWithSystemSymbolName:symbolName
accessibilityDescription:@"VolumeControl"];
[icon setTemplate:YES];
self.statusItem.button.image = icon;
}
@end
int main(int argc, const char* argv[]) {
#pragma unused (argc, argv)
@autoreleasepool {
NSApplication* app = [NSApplication sharedApplication];
[app setActivationPolicy:NSApplicationActivationPolicyAccessory];
NSLog(@"VolumeControl: Starting...");
VCAudioDeviceManager* audioDevices = [VCAudioDeviceManager new];
if (!audioDevices) {
NSLog(@"VolumeControl: Could not find the virtual audio device driver.");
return 1;
}
VCTermination::SetUpTerminationCleanUp(audioDevices);
// App delegate for clean shutdown.
VCAppDelegate* delegate = [[VCAppDelegate alloc] init];
delegate.audioDevices = audioDevices;
[app setDelegate:delegate];
// Menu bar icon with scroll volume control.
VCMenuBar* menuBar = [[VCMenuBar alloc] init];
[menuBar setupWithAudioDevices:audioDevices];
// Update icon when volume changes externally (keyboard keys, system slider).
audioDevices.onVolumeChanged = ^{
[menuBar updateIcon];
};
// Request microphone permission. Block until granted.
if (@available(macOS 10.14, *)) {
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
__block BOOL granted = NO;
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL g) {
granted = g;
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
if (!granted) {
NSLog(@"VolumeControl: Microphone permission denied.");
return 1;
}
}
// Scan devices and activate.
[audioDevices evaluateAndActivate];
[menuBar updateIcon];
NSLog(@"VolumeControl: Running.");
[app run];
}
return 0;
}