/* |
File: NullAudio.c |
Abstract: Part of NullAudio Driver Example |
Version: 1.0.1 |
|
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
|
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
|
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
|
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
|
Copyright (C) 2013 Apple Inc. All Rights Reserved. |
|
*/ |
/*================================================================================================== |
NullAudio.c |
==================================================================================================*/ |
|
//================================================================================================== |
// Includes |
//================================================================================================== |
|
// System Includes |
#include <CoreAudio/AudioServerPlugIn.h> |
#include <dispatch/dispatch.h> |
#include <mach/mach_time.h> |
#include <pthread.h> |
#include <stdint.h> |
#include <sys/syslog.h> |
|
//================================================================================================== |
#pragma mark - |
#pragma mark Macros |
//================================================================================================== |
|
#if TARGET_RT_BIG_ENDIAN |
#define FourCCToCString(the4CC) { ((char*)&the4CC)[0], ((char*)&the4CC)[1], ((char*)&the4CC)[2], ((char*)&the4CC)[3], 0 } |
#else |
#define FourCCToCString(the4CC) { ((char*)&the4CC)[3], ((char*)&the4CC)[2], ((char*)&the4CC)[1], ((char*)&the4CC)[0], 0 } |
#endif |
|
#if DEBUG |
|
#define DebugMsg(inFormat, ...) syslog(LOG_NOTICE, inFormat, ## __VA_ARGS__) |
|
#define FailIf(inCondition, inHandler, inMessage) \ |
if(inCondition) \ |
{ \ |
DebugMsg(inMessage); \ |
goto inHandler; \ |
} |
|
#define FailWithAction(inCondition, inAction, inHandler, inMessage) \ |
if(inCondition) \ |
{ \ |
DebugMsg(inMessage); \ |
{ inAction; } \ |
goto inHandler; \ |
} |
|
#else |
|
#define DebugMsg(inFormat, ...) |
|
#define FailIf(inCondition, inHandler, inMessage) \ |
if(inCondition) \ |
{ \ |
goto inHandler; \ |
} |
|
#define FailWithAction(inCondition, inAction, inHandler, inMessage) \ |
if(inCondition) \ |
{ \ |
{ inAction; } \ |
goto inHandler; \ |
} |
|
#endif |
|
//================================================================================================== |
#pragma mark - |
#pragma mark NullAudio State |
//================================================================================================== |
|
// The purpose of the NullAudio is to provide the barest of bare bones implementations to |
// illustrate the minimal set of things a driver has to do. As such, the driver has the following |
// qualities: |
// - a box |
// - a device |
// - supports 44100 and 48000 sample rates |
// - provides a rate scalar of 1.0 via hard coding |
// - a single input stream |
// - supports 2 channels of 32 bit float LPCM samples |
// - always produces zeros |
// - a single output stream |
// - supports 2 channels of 32 bit float LPCM samples |
// - data written to it is ignored |
// - controls |
// - master input volume |
// - master output volume |
// - master input mute |
// - master output mute |
// - master input data source |
// - master output data source |
// - all are for illustration purposes only and do not actually manipulate data |
|
|
// Declare the internal object ID numbers for all the objects this driver implements. Note that |
// because the driver has fixed set of objects that never grows or shrinks. If this were not the |
// case, the driver would need to have a means to dynamically allocate these IDs. It is important |
// to realize that a lot of the structure of this driver is vastly simpler when the IDs are all |
// known a priori. Comments in the code will try to identify some of these simplifications and |
// point out what a more complicated driver will need to do. |
enum |
{ |
kObjectID_PlugIn = kAudioObjectPlugInObject, |
kObjectID_Box = 2, |
kObjectID_Device = 3, |
kObjectID_Stream_Input = 4, |
kObjectID_Volume_Input_Master = 5, |
kObjectID_Mute_Input_Master = 6, |
kObjectID_DataSource_Input_Master = 7, |
kObjectID_Stream_Output = 8, |
kObjectID_Volume_Output_Master = 9, |
kObjectID_Mute_Output_Master = 10, |
kObjectID_DataSource_Output_Master = 11 |
}; |
|
// Declare the stuff that tracks the state of the plug-in, the device and its sub-objects. |
// Note that we use global variables here because this driver only ever has a single device. If |
// multiple devices were supported, this state would need to be encapsulated in one or more structs |
// so that each object's state can be tracked individually. |
// Note also that we share a single mutex across all objects to be thread safe for the same reason. |
#define kPlugIn_BundleID "com.apple.audio.NullAudio" |
static pthread_mutex_t gPlugIn_StateMutex = PTHREAD_MUTEX_INITIALIZER; |
static UInt32 gPlugIn_RefCount = 0; |
static AudioServerPlugInHostRef gPlugIn_Host = NULL; |
|
#define kBox_UID "NullAudioBox_UID" |
static CFStringRef gBox_Name = NULL; |
static Boolean gBox_Acquired = true; |
|
#define kDevice_UID "NullAudioDevice_UID" |
#define kDevice_ModelUID "NullAudioDevice_ModelUID" |
static pthread_mutex_t gDevice_IOMutex = PTHREAD_MUTEX_INITIALIZER; |
static Float64 gDevice_SampleRate = 44100.0; |
static UInt64 gDevice_IOIsRunning = 0; |
static const UInt32 kDevice_RingBufferSize = 16384; |
static Float64 gDevice_HostTicksPerFrame = 0.0; |
static UInt64 gDevice_NumberTimeStamps = 0; |
static Float64 gDevice_AnchorSampleTime = 0.0; |
static UInt64 gDevice_AnchorHostTime = 0; |
|
static bool gStream_Input_IsActive = true; |
static bool gStream_Output_IsActive = true; |
|
static const Float32 kVolume_MinDB = -96.0; |
static const Float32 kVolume_MaxDB = 6.0; |
static Float32 gVolume_Input_Master_Value = 0.0; |
static Float32 gVolume_Output_Master_Value = 0.0; |
|
static bool gMute_Input_Master_Value = false; |
static bool gMute_Output_Master_Value = false; |
|
static const UInt32 kDataSource_NumberItems = 4; |
#define kDataSource_ItemNamePattern "Data Source Item %d" |
static UInt32 gDataSource_Input_Master_Value = 0; |
static UInt32 gDataSource_Output_Master_Value = 0; |
|
//================================================================================================== |
#pragma mark - |
#pragma mark AudioServerPlugInDriverInterface Implementation |
//================================================================================================== |
|
#pragma mark Prototypes |
|
// Entry points for the COM methods |
void* NullAudio_Create(CFAllocatorRef inAllocator, CFUUIDRef inRequestedTypeUUID); |
static HRESULT NullAudio_QueryInterface(void* inDriver, REFIID inUUID, LPVOID* outInterface); |
static ULONG NullAudio_AddRef(void* inDriver); |
static ULONG NullAudio_Release(void* inDriver); |
static OSStatus NullAudio_Initialize(AudioServerPlugInDriverRef inDriver, AudioServerPlugInHostRef inHost); |
static OSStatus NullAudio_CreateDevice(AudioServerPlugInDriverRef inDriver, CFDictionaryRef inDescription, const AudioServerPlugInClientInfo* inClientInfo, AudioObjectID* outDeviceObjectID); |
static OSStatus NullAudio_DestroyDevice(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID); |
static OSStatus NullAudio_AddDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo); |
static OSStatus NullAudio_RemoveDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo); |
static OSStatus NullAudio_PerformDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo); |
static OSStatus NullAudio_AbortDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo); |
static Boolean NullAudio_HasProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); |
static OSStatus NullAudio_IsPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); |
static OSStatus NullAudio_GetPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); |
static OSStatus NullAudio_GetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); |
static OSStatus NullAudio_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); |
static OSStatus NullAudio_StartIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID); |
static OSStatus NullAudio_StopIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID); |
static OSStatus NullAudio_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, Float64* outSampleTime, UInt64* outHostTime, UInt64* outSeed); |
static OSStatus NullAudio_WillDoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, Boolean* outWillDo, Boolean* outWillDoInPlace); |
static OSStatus NullAudio_BeginIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo); |
static OSStatus NullAudio_DoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, AudioObjectID inStreamObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo, void* ioMainBuffer, void* ioSecondaryBuffer); |
static OSStatus NullAudio_EndIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo); |
|
// Implementation |
static Boolean NullAudio_HasPlugInProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); |
static OSStatus NullAudio_IsPlugInPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); |
static OSStatus NullAudio_GetPlugInPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); |
static OSStatus NullAudio_GetPlugInPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); |
static OSStatus NullAudio_SetPlugInPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData, UInt32* outNumberPropertiesChanged, AudioObjectPropertyAddress outChangedAddresses[2]); |
|
static Boolean NullAudio_HasBoxProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); |
static OSStatus NullAudio_IsBoxPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); |
static OSStatus NullAudio_GetBoxPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); |
static OSStatus NullAudio_GetBoxPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); |
static OSStatus NullAudio_SetBoxPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData, UInt32* outNumberPropertiesChanged, AudioObjectPropertyAddress outChangedAddresses[2]); |
|
static Boolean NullAudio_HasDeviceProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); |
static OSStatus NullAudio_IsDevicePropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); |
static OSStatus NullAudio_GetDevicePropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); |
static OSStatus NullAudio_GetDevicePropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); |
static OSStatus NullAudio_SetDevicePropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData, UInt32* outNumberPropertiesChanged, AudioObjectPropertyAddress outChangedAddresses[2]); |
|
static Boolean NullAudio_HasStreamProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); |
static OSStatus NullAudio_IsStreamPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); |
static OSStatus NullAudio_GetStreamPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); |
static OSStatus NullAudio_GetStreamPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); |
static OSStatus NullAudio_SetStreamPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData, UInt32* outNumberPropertiesChanged, AudioObjectPropertyAddress outChangedAddresses[2]); |
|
static Boolean NullAudio_HasControlProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); |
static OSStatus NullAudio_IsControlPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); |
static OSStatus NullAudio_GetControlPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); |
static OSStatus NullAudio_GetControlPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); |
static OSStatus NullAudio_SetControlPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData, UInt32* outNumberPropertiesChanged, AudioObjectPropertyAddress outChangedAddresses[2]); |
|
#pragma mark The Interface |
|
static AudioServerPlugInDriverInterface gAudioServerPlugInDriverInterface = |
{ |
NULL, |
NullAudio_QueryInterface, |
NullAudio_AddRef, |
NullAudio_Release, |
NullAudio_Initialize, |
NullAudio_CreateDevice, |
NullAudio_DestroyDevice, |
NullAudio_AddDeviceClient, |
NullAudio_RemoveDeviceClient, |
NullAudio_PerformDeviceConfigurationChange, |
NullAudio_AbortDeviceConfigurationChange, |
NullAudio_HasProperty, |
NullAudio_IsPropertySettable, |
NullAudio_GetPropertyDataSize, |
NullAudio_GetPropertyData, |
NullAudio_SetPropertyData, |
NullAudio_StartIO, |
NullAudio_StopIO, |
NullAudio_GetZeroTimeStamp, |
NullAudio_WillDoIOOperation, |
NullAudio_BeginIOOperation, |
NullAudio_DoIOOperation, |
NullAudio_EndIOOperation |
}; |
static AudioServerPlugInDriverInterface* gAudioServerPlugInDriverInterfacePtr = &gAudioServerPlugInDriverInterface; |
static AudioServerPlugInDriverRef gAudioServerPlugInDriverRef = &gAudioServerPlugInDriverInterfacePtr; |
|
#pragma mark Factory |
|
void* NullAudio_Create(CFAllocatorRef inAllocator, CFUUIDRef inRequestedTypeUUID) |
{ |
// This is the CFPlugIn factory function. Its job is to create the implementation for the given |
// type provided that the type is supported. Because this driver is simple and all its |
// initialization is handled via static iniitalization when the bundle is loaded, all that |
// needs to be done is to return the AudioServerPlugInDriverRef that points to the driver's |
// interface. A more complicated driver would create any base line objects it needs to satisfy |
// the IUnknown methods that are used to discover that actual interface to talk to the driver. |
// The majority of the driver's initilization should be handled in the Initialize() method of |
// the driver's AudioServerPlugInDriverInterface. |
|
#pragma unused(inAllocator) |
void* theAnswer = NULL; |
if(CFEqual(inRequestedTypeUUID, kAudioServerPlugInTypeUUID)) |
{ |
theAnswer = gAudioServerPlugInDriverRef; |
} |
return theAnswer; |
} |
|
#pragma mark Inheritence |
|
static HRESULT NullAudio_QueryInterface(void* inDriver, REFIID inUUID, LPVOID* outInterface) |
{ |
// This function is called by the HAL to get the interface to talk to the plug-in through. |
// AudioServerPlugIns are required to support the IUnknown interface and the |
// AudioServerPlugInDriverInterface. As it happens, all interfaces must also provide the |
// IUnknown interface, so we can always just return the single interface we made with |
// gAudioServerPlugInDriverInterfacePtr regardless of which one is asked for. |
|
// declare the local variables |
HRESULT theAnswer = 0; |
CFUUIDRef theRequestedUUID = NULL; |
|
// validate the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_QueryInterface: bad driver reference"); |
FailWithAction(outInterface == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_QueryInterface: no place to store the returned interface"); |
|
// make a CFUUIDRef from inUUID |
theRequestedUUID = CFUUIDCreateFromUUIDBytes(NULL, inUUID); |
FailWithAction(theRequestedUUID == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_QueryInterface: failed to create the CFUUIDRef"); |
|
// AudioServerPlugIns only support two interfaces, IUnknown (which has to be supported by all |
// CFPlugIns and AudioServerPlugInDriverInterface (which is the actual interface the HAL will |
// use). |
if(CFEqual(theRequestedUUID, IUnknownUUID) || CFEqual(theRequestedUUID, kAudioServerPlugInDriverInterfaceUUID)) |
{ |
pthread_mutex_lock(&gPlugIn_StateMutex); |
++gPlugIn_RefCount; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
*outInterface = gAudioServerPlugInDriverRef; |
} |
else |
{ |
theAnswer = E_NOINTERFACE; |
} |
|
// make sure to release the UUID we created |
CFRelease(theRequestedUUID); |
|
Done: |
return theAnswer; |
} |
|
static ULONG NullAudio_AddRef(void* inDriver) |
{ |
// This call returns the resulting reference count after the increment. |
|
// declare the local variables |
ULONG theAnswer = 0; |
|
// check the arguments |
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "NullAudio_AddRef: bad driver reference"); |
|
// decrement the refcount |
pthread_mutex_lock(&gPlugIn_StateMutex); |
if(gPlugIn_RefCount < UINT32_MAX) |
{ |
++gPlugIn_RefCount; |
} |
theAnswer = gPlugIn_RefCount; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
|
Done: |
return theAnswer; |
} |
|
static ULONG NullAudio_Release(void* inDriver) |
{ |
// This call returns the resulting reference count after the decrement. |
|
// declare the local variables |
ULONG theAnswer = 0; |
|
// check the arguments |
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "NullAudio_Release: bad driver reference"); |
|
// increment the refcount |
pthread_mutex_lock(&gPlugIn_StateMutex); |
if(gPlugIn_RefCount > 0) |
{ |
--gPlugIn_RefCount; |
// Note that we don't do anything special if the refcount goes to zero as the HAL |
// will never fully release a plug-in it opens. We keep managing the refcount so that |
// the API semantics are correct though. |
} |
theAnswer = gPlugIn_RefCount; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
|
Done: |
return theAnswer; |
} |
|
#pragma mark Basic Operations |
|
static OSStatus NullAudio_Initialize(AudioServerPlugInDriverRef inDriver, AudioServerPlugInHostRef inHost) |
{ |
// The job of this method is, as the name implies, to get the driver initialized. One specific |
// thing that needs to be done is to store the AudioServerPlugInHostRef so that it can be used |
// later. Note that when this call returns, the HAL will scan the various lists the driver |
// maintains (such as the device list) to get the inital set of objects the driver is |
// publishing. So, there is no need to notifiy the HAL about any objects created as part of the |
// execution of this method. |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_Initialize: bad driver reference"); |
|
// store the AudioServerPlugInHostRef |
gPlugIn_Host = inHost; |
|
// initialize the box acquired property from the settings |
CFPropertyListRef theSettingsData = NULL; |
gPlugIn_Host->CopyFromStorage(gPlugIn_Host, CFSTR("box acquired"), &theSettingsData); |
if(theSettingsData != NULL) |
{ |
if(CFGetTypeID(theSettingsData) == CFBooleanGetTypeID()) |
{ |
gBox_Acquired = CFBooleanGetValue((CFBooleanRef)theSettingsData); |
} |
else if(CFGetTypeID(theSettingsData) == CFNumberGetTypeID()) |
{ |
SInt32 theValue = 0; |
CFNumberGetValue((CFNumberRef)theSettingsData, kCFNumberSInt32Type, &theValue); |
gBox_Acquired = theValue ? 1 : 0; |
} |
CFRelease(theSettingsData); |
} |
|
// initialize the box name from the settings |
gPlugIn_Host->CopyFromStorage(gPlugIn_Host, CFSTR("box acquired"), &theSettingsData); |
if(theSettingsData != NULL) |
{ |
if(CFGetTypeID(theSettingsData) == CFStringGetTypeID()) |
{ |
gBox_Name = (CFStringRef)theSettingsData; |
CFRetain(gBox_Name); |
} |
CFRelease(theSettingsData); |
} |
|
// set the box name directly as a last resort |
if(gBox_Name == NULL) |
{ |
gBox_Name = CFSTR("Null Box"); |
} |
|
// calculate the host ticks per frame |
struct mach_timebase_info theTimeBaseInfo; |
mach_timebase_info(&theTimeBaseInfo); |
Float64 theHostClockFrequency = theTimeBaseInfo.denom / theTimeBaseInfo.numer; |
theHostClockFrequency *= 1000000000.0; |
gDevice_HostTicksPerFrame = theHostClockFrequency / gDevice_SampleRate; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_CreateDevice(AudioServerPlugInDriverRef inDriver, CFDictionaryRef inDescription, const AudioServerPlugInClientInfo* inClientInfo, AudioObjectID* outDeviceObjectID) |
{ |
// This method is used to tell a driver that implements the Transport Manager semantics to |
// create an AudioEndpointDevice from a set of AudioEndpoints. Since this driver is not a |
// Transport Manager, we just check the arguments and return |
// kAudioHardwareUnsupportedOperationError. |
|
#pragma unused(inDescription, inClientInfo, outDeviceObjectID) |
|
// declare the local variables |
OSStatus theAnswer = kAudioHardwareUnsupportedOperationError; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_CreateDevice: bad driver reference"); |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_DestroyDevice(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID) |
{ |
// This method is used to tell a driver that implements the Transport Manager semantics to |
// destroy an AudioEndpointDevice. Since this driver is not a Transport Manager, we just check |
// the arguments and return kAudioHardwareUnsupportedOperationError. |
|
#pragma unused(inDeviceObjectID) |
|
// declare the local variables |
OSStatus theAnswer = kAudioHardwareUnsupportedOperationError; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_DestroyDevice: bad driver reference"); |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_AddDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo) |
{ |
// This method is used to inform the driver about a new client that is using the given device. |
// This allows the device to act differently depending on who the client is. This driver does |
// not need to track the clients using the device, so we just check the arguments and return |
// successfully. |
|
#pragma unused(inClientInfo) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_AddDeviceClient: bad driver reference"); |
FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_AddDeviceClient: bad device ID"); |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_RemoveDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo) |
{ |
// This method is used to inform the driver about a client that is no longer using the given |
// device. This driver does not track clients, so we just check the arguments and return |
// successfully. |
|
#pragma unused(inClientInfo) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_RemoveDeviceClient: bad driver reference"); |
FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_RemoveDeviceClient: bad device ID"); |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_PerformDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo) |
{ |
// This method is called to tell the device that it can perform the configuation change that it |
// had requested via a call to the host method, RequestDeviceConfigurationChange(). The |
// arguments, inChangeAction and inChangeInfo are the same as what was passed to |
// RequestDeviceConfigurationChange(). |
// |
// The HAL guarantees that IO will be stopped while this method is in progress. The HAL will |
// also handle figuring out exactly what changed for the non-control related properties. This |
// means that the only notifications that would need to be sent here would be for either |
// custom properties the HAL doesn't know about or for controls. |
// |
// For the device implemented by this driver, only sample rate changes go through this process |
// as it is the only state that can be changed for the device that isn't a control. For this |
// change, the new sample rate is passed in the inChangeAction argument. |
|
#pragma unused(inChangeInfo) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_PerformDeviceConfigurationChange: bad driver reference"); |
FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_PerformDeviceConfigurationChange: bad device ID"); |
FailWithAction((inChangeAction != 44100) && (inChangeAction != 48000), theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_PerformDeviceConfigurationChange: bad sample rate"); |
|
// lock the state mutex |
pthread_mutex_lock(&gPlugIn_StateMutex); |
|
// change the sample rate |
gDevice_SampleRate = inChangeAction; |
|
// recalculate the state that depends on the sample rate |
struct mach_timebase_info theTimeBaseInfo; |
mach_timebase_info(&theTimeBaseInfo); |
Float64 theHostClockFrequency = theTimeBaseInfo.denom / theTimeBaseInfo.numer; |
theHostClockFrequency *= 1000000000.0; |
gDevice_HostTicksPerFrame = theHostClockFrequency / gDevice_SampleRate; |
|
// unlock the state mutex |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_AbortDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo) |
{ |
// This method is called to tell the driver that a request for a config change has been denied. |
// This provides the driver an opportunity to clean up any state associated with the request. |
// For this driver, an aborted config change requires no action. So we just check the arguments |
// and return |
|
#pragma unused(inChangeAction, inChangeInfo) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_PerformDeviceConfigurationChange: bad driver reference"); |
FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_PerformDeviceConfigurationChange: bad device ID"); |
|
Done: |
return theAnswer; |
} |
|
#pragma mark Property Operations |
|
static Boolean NullAudio_HasProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) |
{ |
// This method returns whether or not the given object has the given property. |
|
// declare the local variables |
Boolean theAnswer = false; |
|
// check the arguments |
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "NullAudio_HasProperty: bad driver reference"); |
FailIf(inAddress == NULL, Done, "NullAudio_HasProperty: no address"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetPropertyData() method. |
switch(inObjectID) |
{ |
case kObjectID_PlugIn: |
theAnswer = NullAudio_HasPlugInProperty(inDriver, inObjectID, inClientProcessID, inAddress); |
break; |
|
case kObjectID_Box: |
theAnswer = NullAudio_HasBoxProperty(inDriver, inObjectID, inClientProcessID, inAddress); |
break; |
|
case kObjectID_Device: |
theAnswer = NullAudio_HasDeviceProperty(inDriver, inObjectID, inClientProcessID, inAddress); |
break; |
|
case kObjectID_Stream_Input: |
case kObjectID_Stream_Output: |
theAnswer = NullAudio_HasStreamProperty(inDriver, inObjectID, inClientProcessID, inAddress); |
break; |
|
case kObjectID_Volume_Input_Master: |
case kObjectID_Volume_Output_Master: |
case kObjectID_Mute_Input_Master: |
case kObjectID_Mute_Output_Master: |
case kObjectID_DataSource_Input_Master: |
case kObjectID_DataSource_Output_Master: |
theAnswer = NullAudio_HasControlProperty(inDriver, inObjectID, inClientProcessID, inAddress); |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_IsPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) |
{ |
// This method returns whether or not the given property on the object can have its value |
// changed. |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_IsPropertySettable: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsPropertySettable: no address"); |
FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsPropertySettable: no place to put the return value"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetPropertyData() method. |
switch(inObjectID) |
{ |
case kObjectID_PlugIn: |
theAnswer = NullAudio_IsPlugInPropertySettable(inDriver, inObjectID, inClientProcessID, inAddress, outIsSettable); |
break; |
|
case kObjectID_Box: |
theAnswer = NullAudio_IsBoxPropertySettable(inDriver, inObjectID, inClientProcessID, inAddress, outIsSettable); |
break; |
|
case kObjectID_Device: |
theAnswer = NullAudio_IsDevicePropertySettable(inDriver, inObjectID, inClientProcessID, inAddress, outIsSettable); |
break; |
|
case kObjectID_Stream_Input: |
case kObjectID_Stream_Output: |
theAnswer = NullAudio_IsStreamPropertySettable(inDriver, inObjectID, inClientProcessID, inAddress, outIsSettable); |
break; |
|
case kObjectID_Volume_Input_Master: |
case kObjectID_Volume_Output_Master: |
case kObjectID_Mute_Input_Master: |
case kObjectID_Mute_Output_Master: |
case kObjectID_DataSource_Input_Master: |
case kObjectID_DataSource_Output_Master: |
theAnswer = NullAudio_IsControlPropertySettable(inDriver, inObjectID, inClientProcessID, inAddress, outIsSettable); |
break; |
|
default: |
theAnswer = kAudioHardwareBadObjectError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) |
{ |
// This method returns the byte size of the property's data. |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetPropertyDataSize: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetPropertyDataSize: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetPropertyDataSize: no place to put the return value"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetPropertyData() method. |
switch(inObjectID) |
{ |
case kObjectID_PlugIn: |
theAnswer = NullAudio_GetPlugInPropertyDataSize(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, outDataSize); |
break; |
|
case kObjectID_Box: |
theAnswer = NullAudio_GetBoxPropertyDataSize(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, outDataSize); |
break; |
|
case kObjectID_Device: |
theAnswer = NullAudio_GetDevicePropertyDataSize(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, outDataSize); |
break; |
|
case kObjectID_Stream_Input: |
case kObjectID_Stream_Output: |
theAnswer = NullAudio_GetStreamPropertyDataSize(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, outDataSize); |
break; |
|
case kObjectID_Volume_Input_Master: |
case kObjectID_Volume_Output_Master: |
case kObjectID_Mute_Input_Master: |
case kObjectID_Mute_Output_Master: |
case kObjectID_DataSource_Input_Master: |
case kObjectID_DataSource_Output_Master: |
theAnswer = NullAudio_GetControlPropertyDataSize(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, outDataSize); |
break; |
|
default: |
theAnswer = kAudioHardwareBadObjectError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) |
{ |
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetPropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetPropertyData: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetPropertyData: no place to put the return value size"); |
FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetPropertyData: no place to put the return value"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. |
// |
// Also, since most of the data that will get returned is static, there are few instances where |
// it is necessary to lock the state mutex. |
switch(inObjectID) |
{ |
case kObjectID_PlugIn: |
theAnswer = NullAudio_GetPlugInPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); |
break; |
|
case kObjectID_Box: |
theAnswer = NullAudio_GetBoxPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); |
break; |
|
case kObjectID_Device: |
theAnswer = NullAudio_GetDevicePropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); |
break; |
|
case kObjectID_Stream_Input: |
case kObjectID_Stream_Output: |
theAnswer = NullAudio_GetStreamPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); |
break; |
|
case kObjectID_Volume_Input_Master: |
case kObjectID_Volume_Output_Master: |
case kObjectID_Mute_Input_Master: |
case kObjectID_Mute_Output_Master: |
case kObjectID_DataSource_Input_Master: |
case kObjectID_DataSource_Output_Master: |
theAnswer = NullAudio_GetControlPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); |
break; |
|
default: |
theAnswer = kAudioHardwareBadObjectError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData) |
{ |
// declare the local variables |
OSStatus theAnswer = 0; |
UInt32 theNumberPropertiesChanged = 0; |
AudioObjectPropertyAddress theChangedAddresses[2]; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_SetPropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetPropertyData: no address"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetPropertyData() method. |
switch(inObjectID) |
{ |
case kObjectID_PlugIn: |
theAnswer = NullAudio_SetPlugInPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData, &theNumberPropertiesChanged, theChangedAddresses); |
break; |
|
case kObjectID_Box: |
theAnswer = NullAudio_SetBoxPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData, &theNumberPropertiesChanged, theChangedAddresses); |
break; |
|
case kObjectID_Device: |
theAnswer = NullAudio_SetDevicePropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData, &theNumberPropertiesChanged, theChangedAddresses); |
break; |
|
case kObjectID_Stream_Input: |
case kObjectID_Stream_Output: |
theAnswer = NullAudio_SetStreamPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData, &theNumberPropertiesChanged, theChangedAddresses); |
break; |
|
case kObjectID_Volume_Input_Master: |
case kObjectID_Volume_Output_Master: |
case kObjectID_Mute_Input_Master: |
case kObjectID_Mute_Output_Master: |
case kObjectID_DataSource_Input_Master: |
case kObjectID_DataSource_Output_Master: |
theAnswer = NullAudio_SetControlPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData, &theNumberPropertiesChanged, theChangedAddresses); |
break; |
|
default: |
theAnswer = kAudioHardwareBadObjectError; |
break; |
}; |
|
// send any notifications |
if(theNumberPropertiesChanged > 0) |
{ |
gPlugIn_Host->PropertiesChanged(gPlugIn_Host, inObjectID, theNumberPropertiesChanged, theChangedAddresses); |
} |
|
Done: |
return theAnswer; |
} |
|
#pragma mark PlugIn Property Operations |
|
static Boolean NullAudio_HasPlugInProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) |
{ |
// This method returns whether or not the plug-in object has the given property. |
|
#pragma unused(inClientProcessID) |
|
// declare the local variables |
Boolean theAnswer = false; |
|
// check the arguments |
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "NullAudio_HasPlugInProperty: bad driver reference"); |
FailIf(inAddress == NULL, Done, "NullAudio_HasPlugInProperty: no address"); |
FailIf(inObjectID != kObjectID_PlugIn, Done, "NullAudio_HasPlugInProperty: not the plug-in object"); |
|
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetPlugInPropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyManufacturer: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioPlugInPropertyBoxList: |
case kAudioPlugInPropertyTranslateUIDToBox: |
case kAudioPlugInPropertyDeviceList: |
case kAudioPlugInPropertyTranslateUIDToDevice: |
case kAudioPlugInPropertyResourceBundle: |
theAnswer = true; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_IsPlugInPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) |
{ |
// This method returns whether or not the given property on the plug-in object can have its |
// value changed. |
|
#pragma unused(inClientProcessID) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_IsPlugInPropertySettable: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsPlugInPropertySettable: no address"); |
FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsPlugInPropertySettable: no place to put the return value"); |
FailWithAction(inObjectID != kObjectID_PlugIn, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_IsPlugInPropertySettable: not the plug-in object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetPlugInPropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyManufacturer: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioPlugInPropertyBoxList: |
case kAudioPlugInPropertyTranslateUIDToBox: |
case kAudioPlugInPropertyDeviceList: |
case kAudioPlugInPropertyTranslateUIDToDevice: |
case kAudioPlugInPropertyResourceBundle: |
*outIsSettable = false; |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetPlugInPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) |
{ |
// This method returns the byte size of the property's data. |
|
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetPlugInPropertyDataSize: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetPlugInPropertyDataSize: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetPlugInPropertyDataSize: no place to put the return value"); |
FailWithAction(inObjectID != kObjectID_PlugIn, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetPlugInPropertyDataSize: not the plug-in object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetPlugInPropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyManufacturer: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
if(gBox_Acquired) |
{ |
*outDataSize = 2 * sizeof(AudioClassID); |
} |
else |
{ |
*outDataSize = sizeof(AudioClassID); |
} |
break; |
|
case kAudioPlugInPropertyBoxList: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioPlugInPropertyTranslateUIDToBox: |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioPlugInPropertyDeviceList: |
if(gBox_Acquired) |
{ |
*outDataSize = sizeof(AudioClassID); |
} |
else |
{ |
*outDataSize = 0; |
} |
break; |
|
case kAudioPlugInPropertyTranslateUIDToDevice: |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioPlugInPropertyResourceBundle: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetPlugInPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) |
{ |
#pragma unused(inClientProcessID) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
UInt32 theNumberItemsToFetch; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetPlugInPropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetPlugInPropertyData: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetPlugInPropertyData: no place to put the return value size"); |
FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetPlugInPropertyData: no place to put the return value"); |
FailWithAction(inObjectID != kObjectID_PlugIn, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetPlugInPropertyData: not the plug-in object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. |
// |
// Also, since most of the data that will get returned is static, there are few instances where |
// it is necessary to lock the state mutex. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
// The base class for kAudioPlugInClassID is kAudioObjectClassID |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetPlugInPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the plug-in"); |
*((AudioClassID*)outData) = kAudioObjectClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
// The class is always kAudioPlugInClassID for regular drivers |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetPlugInPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the plug-in"); |
*((AudioClassID*)outData) = kAudioPlugInClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
// The plug-in doesn't have an owning object |
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetPlugInPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the plug-in"); |
*((AudioObjectID*)outData) = kAudioObjectUnknown; |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyManufacturer: |
// This is the human readable name of the maker of the plug-in. |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetPlugInPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the plug-in"); |
*((CFStringRef*)outData) = CFSTR("Apple Inc."); |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
// Calculate the number of items that have been requested. Note that this |
// number is allowed to be smaller than the actual size of the list. In such |
// case, only that number of items will be returned |
theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); |
|
// Clamp that to the number of boxes this driver implements (which is just 1) |
if(theNumberItemsToFetch > (gBox_Acquired ? 2 : 1)) |
{ |
theNumberItemsToFetch = (gBox_Acquired ? 2 : 1); |
} |
|
// Write the devices' object IDs into the return value |
if(theNumberItemsToFetch > 1) |
{ |
((AudioObjectID*)outData)[0] = kObjectID_Box; |
((AudioObjectID*)outData)[0] = kObjectID_Device; |
} |
else if(theNumberItemsToFetch > 0) |
{ |
((AudioObjectID*)outData)[0] = kObjectID_Box; |
} |
|
// Return how many bytes we wrote to |
*outDataSize = theNumberItemsToFetch * sizeof(AudioClassID); |
break; |
|
case kAudioPlugInPropertyBoxList: |
// Calculate the number of items that have been requested. Note that this |
// number is allowed to be smaller than the actual size of the list. In such |
// case, only that number of items will be returned |
theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); |
|
// Clamp that to the number of boxes this driver implements (which is just 1) |
if(theNumberItemsToFetch > 1) |
{ |
theNumberItemsToFetch = 1; |
} |
|
// Write the devices' object IDs into the return value |
if(theNumberItemsToFetch > 0) |
{ |
((AudioObjectID*)outData)[0] = kObjectID_Box; |
} |
|
// Return how many bytes we wrote to |
*outDataSize = theNumberItemsToFetch * sizeof(AudioClassID); |
break; |
|
case kAudioPlugInPropertyTranslateUIDToBox: |
// This property takes the CFString passed in the qualifier and converts that |
// to the object ID of the box it corresponds to. For this driver, there is |
// just the one box. Note that it is not an error if the string in the |
// qualifier doesn't match any devices. In such case, kAudioObjectUnknown is |
// the object ID to return. |
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetPlugInPropertyData: not enough space for the return value of kAudioPlugInPropertyTranslateUIDToBox"); |
FailWithAction(inQualifierDataSize == sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetPlugInPropertyData: the qualifier is the wrong size for kAudioPlugInPropertyTranslateUIDToBox"); |
FailWithAction(inQualifierData == NULL, theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetPlugInPropertyData: no qualifier for kAudioPlugInPropertyTranslateUIDToBox"); |
if(CFStringCompare(*((CFStringRef*)inQualifierData), CFSTR(kBox_UID), 0) == kCFCompareEqualTo) |
{ |
*((AudioObjectID*)outData) = kObjectID_Box; |
} |
else |
{ |
*((AudioObjectID*)outData) = kAudioObjectUnknown; |
} |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioPlugInPropertyDeviceList: |
// Calculate the number of items that have been requested. Note that this |
// number is allowed to be smaller than the actual size of the list. In such |
// case, only that number of items will be returned |
theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); |
|
// Clamp that to the number of devices this driver implements (which is just 1 if the |
// box has been acquired) |
if(theNumberItemsToFetch > (gBox_Acquired ? 1 : 0)) |
{ |
theNumberItemsToFetch = (gBox_Acquired ? 1 : 0); |
} |
|
// Write the devices' object IDs into the return value |
if(theNumberItemsToFetch > 0) |
{ |
((AudioObjectID*)outData)[0] = kObjectID_Device; |
} |
|
// Return how many bytes we wrote to |
*outDataSize = theNumberItemsToFetch * sizeof(AudioClassID); |
break; |
|
case kAudioPlugInPropertyTranslateUIDToDevice: |
// This property takes the CFString passed in the qualifier and converts that |
// to the object ID of the device it corresponds to. For this driver, there is |
// just the one device. Note that it is not an error if the string in the |
// qualifier doesn't match any devices. In such case, kAudioObjectUnknown is |
// the object ID to return. |
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetPlugInPropertyData: not enough space for the return value of kAudioPlugInPropertyTranslateUIDToDevice"); |
FailWithAction(inQualifierDataSize == sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetPlugInPropertyData: the qualifier is the wrong size for kAudioPlugInPropertyTranslateUIDToDevice"); |
FailWithAction(inQualifierData == NULL, theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetPlugInPropertyData: no qualifier for kAudioPlugInPropertyTranslateUIDToDevice"); |
if(CFStringCompare(*((CFStringRef*)inQualifierData), CFSTR(kDevice_UID), 0) == kCFCompareEqualTo) |
{ |
*((AudioObjectID*)outData) = kObjectID_Device; |
} |
else |
{ |
*((AudioObjectID*)outData) = kAudioObjectUnknown; |
} |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioPlugInPropertyResourceBundle: |
// The resource bundle is a path relative to the path of the plug-in's bundle. |
// To specify that the plug-in bundle itself should be used, we just return the |
// empty string. |
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetPlugInPropertyData: not enough space for the return value of kAudioPlugInPropertyResourceBundle"); |
*((CFStringRef*)outData) = CFSTR(""); |
*outDataSize = sizeof(CFStringRef); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_SetPlugInPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData, UInt32* outNumberPropertiesChanged, AudioObjectPropertyAddress outChangedAddresses[2]) |
{ |
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData, inDataSize, inData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_SetPlugInPropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetPlugInPropertyData: no address"); |
FailWithAction(outNumberPropertiesChanged == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetPlugInPropertyData: no place to return the number of properties that changed"); |
FailWithAction(outChangedAddresses == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetPlugInPropertyData: no place to return the properties that changed"); |
FailWithAction(inObjectID != kObjectID_PlugIn, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_SetPlugInPropertyData: not the plug-in object"); |
|
// initialize the returned number of changed properties |
*outNumberPropertiesChanged = 0; |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetPlugInPropertyData() method. |
switch(inAddress->mSelector) |
{ |
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
#pragma mark Box Property Operations |
|
static Boolean NullAudio_HasBoxProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) |
{ |
// This method returns whether or not the box object has the given property. |
|
#pragma unused(inClientProcessID) |
|
// declare the local variables |
Boolean theAnswer = false; |
|
// check the arguments |
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "NullAudio_HasBoxProperty: bad driver reference"); |
FailIf(inAddress == NULL, Done, "NullAudio_HasBoxProperty: no address"); |
FailIf(inObjectID != kObjectID_Box, Done, "NullAudio_HasBoxProperty: not the box object"); |
|
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetBoxPropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyName: |
case kAudioObjectPropertyModelName: |
case kAudioObjectPropertyManufacturer: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioObjectPropertyIdentify: |
case kAudioObjectPropertySerialNumber: |
case kAudioObjectPropertyFirmwareVersion: |
case kAudioBoxPropertyBoxUID: |
case kAudioBoxPropertyTransportType: |
case kAudioBoxPropertyHasAudio: |
case kAudioBoxPropertyHasVideo: |
case kAudioBoxPropertyHasMIDI: |
case kAudioBoxPropertyIsProtected: |
case kAudioBoxPropertyAcquired: |
case kAudioBoxPropertyAcquisitionFailed: |
case kAudioBoxPropertyDeviceList: |
theAnswer = true; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_IsBoxPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) |
{ |
// This method returns whether or not the given property on the plug-in object can have its |
// value changed. |
|
#pragma unused(inClientProcessID) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_IsBoxPropertySettable: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsBoxPropertySettable: no address"); |
FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsBoxPropertySettable: no place to put the return value"); |
FailWithAction(inObjectID != kObjectID_Box, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_IsBoxPropertySettable: not the plug-in object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetBoxPropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyModelName: |
case kAudioObjectPropertyManufacturer: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioObjectPropertySerialNumber: |
case kAudioObjectPropertyFirmwareVersion: |
case kAudioBoxPropertyBoxUID: |
case kAudioBoxPropertyTransportType: |
case kAudioBoxPropertyHasAudio: |
case kAudioBoxPropertyHasVideo: |
case kAudioBoxPropertyHasMIDI: |
case kAudioBoxPropertyIsProtected: |
case kAudioBoxPropertyAcquisitionFailed: |
case kAudioBoxPropertyDeviceList: |
*outIsSettable = false; |
break; |
|
case kAudioObjectPropertyName: |
case kAudioObjectPropertyIdentify: |
case kAudioBoxPropertyAcquired: |
*outIsSettable = true; |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetBoxPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) |
{ |
// This method returns the byte size of the property's data. |
|
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetBoxPropertyDataSize: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetBoxPropertyDataSize: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetBoxPropertyDataSize: no place to put the return value"); |
FailWithAction(inObjectID != kObjectID_Box, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetBoxPropertyDataSize: not the plug-in object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetBoxPropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyName: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyModelName: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyManufacturer: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
*outDataSize = 0; |
break; |
|
case kAudioObjectPropertyIdentify: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioObjectPropertySerialNumber: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyFirmwareVersion: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioBoxPropertyBoxUID: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioBoxPropertyTransportType: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyHasAudio: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyHasVideo: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyHasMIDI: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyIsProtected: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyAcquired: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyAcquisitionFailed: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyDeviceList: |
{ |
pthread_mutex_lock(&gPlugIn_StateMutex); |
*outDataSize = gBox_Acquired ? sizeof(AudioObjectID) : 0; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
} |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetBoxPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) |
{ |
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetBoxPropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetBoxPropertyData: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetBoxPropertyData: no place to put the return value size"); |
FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetBoxPropertyData: no place to put the return value"); |
FailWithAction(inObjectID != kObjectID_Box, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetBoxPropertyData: not the plug-in object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. |
// |
// Also, since most of the data that will get returned is static, there are few instances where |
// it is necessary to lock the state mutex. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
// The base class for kAudioBoxClassID is kAudioObjectClassID |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the box"); |
*((AudioClassID*)outData) = kAudioObjectClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
// The class is always kAudioBoxClassID for regular drivers |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the box"); |
*((AudioClassID*)outData) = kAudioBoxClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
// The owner is the plug-in object |
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the box"); |
*((AudioObjectID*)outData) = kObjectID_PlugIn; |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyName: |
// This is the human readable name of the maker of the box. |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the box"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
*((CFStringRef*)outData) = gBox_Name; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
if(*((CFStringRef*)outData) != NULL) |
{ |
CFRetain(*((CFStringRef*)outData)); |
} |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyModelName: |
// This is the human readable name of the maker of the box. |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the box"); |
*((CFStringRef*)outData) = CFSTR("Null Model"); |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyManufacturer: |
// This is the human readable name of the maker of the box. |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the box"); |
*((CFStringRef*)outData) = CFSTR("Apple Inc."); |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
// This returns the objects directly owned by the object. Boxes don't own anything. |
*outDataSize = 0; |
break; |
|
case kAudioObjectPropertyIdentify: |
// This is used to highling the device in the UI, but it's value has no meaning |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyIdentify for the box"); |
*((UInt32*)outData) = 0; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioObjectPropertySerialNumber: |
// This is the human readable serial number of the box. |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertySerialNumber for the box"); |
*((CFStringRef*)outData) = CFSTR("00000001"); |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyFirmwareVersion: |
// This is the human readable firmware version of the box. |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyFirmwareVersion for the box"); |
*((CFStringRef*)outData) = CFSTR("1.0"); |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioBoxPropertyBoxUID: |
// Boxes have UIDs the same as devices |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the box"); |
*((CFStringRef*)outData) = CFSTR(kBox_UID); |
break; |
|
case kAudioBoxPropertyTransportType: |
// This value represents how the device is attached to the system. This can be |
// any 32 bit integer, but common values for this property are defined in |
// <CoreAudio/AudioHardwareBase.h> |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioDevicePropertyTransportType for the box"); |
*((UInt32*)outData) = kAudioDeviceTransportTypeVirtual; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyHasAudio: |
// Indicates whether or not the box has audio capabilities |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyHasAudio for the box"); |
*((UInt32*)outData) = 1; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyHasVideo: |
// Indicates whether or not the box has video capabilities |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyHasVideo for the box"); |
*((UInt32*)outData) = 0; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyHasMIDI: |
// Indicates whether or not the box has MIDI capabilities |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyHasMIDI for the box"); |
*((UInt32*)outData) = 0; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyIsProtected: |
// Indicates whether or not the box has requires authentication to use |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyIsProtected for the box"); |
*((UInt32*)outData) = 0; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyAcquired: |
// When set to a non-zero value, the device is acquired for use by the local machine |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyAcquired for the box"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
*((UInt32*)outData) = gBox_Acquired ? 1 : 0; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyAcquisitionFailed: |
// This is used for notifications to say when an attempt to acquire a device has failed. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyAcquisitionFailed for the box"); |
*((UInt32*)outData) = 0; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioBoxPropertyDeviceList: |
// This is used to indicate which devices came from this box |
pthread_mutex_lock(&gPlugIn_StateMutex); |
if(gBox_Acquired) |
{ |
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyDeviceList for the box"); |
*((AudioObjectID*)outData) = kObjectID_Device; |
*outDataSize = sizeof(AudioObjectID); |
} |
else |
{ |
*outDataSize = 0; |
} |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_SetBoxPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData, UInt32* outNumberPropertiesChanged, AudioObjectPropertyAddress outChangedAddresses[2]) |
{ |
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData, inDataSize, inData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_SetBoxPropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetBoxPropertyData: no address"); |
FailWithAction(outNumberPropertiesChanged == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetBoxPropertyData: no place to return the number of properties that changed"); |
FailWithAction(outChangedAddresses == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetBoxPropertyData: no place to return the properties that changed"); |
FailWithAction(inObjectID != kObjectID_Box, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_SetBoxPropertyData: not the box object"); |
|
// initialize the returned number of changed properties |
*outNumberPropertiesChanged = 0; |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetPlugInPropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyName: |
// Boxes should allow their name to be editable |
{ |
FailWithAction(inDataSize != sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_SetBoxPropertyData: wrong size for the data for kAudioObjectPropertyName"); |
CFStringRef* theNewName = (CFStringRef*)inData; |
pthread_mutex_lock(&gPlugIn_StateMutex); |
if((theNewName != NULL) && (*theNewName != NULL)) |
{ |
CFRetain(*theNewName); |
} |
if(gBox_Name != NULL) |
{ |
CFRelease(gBox_Name); |
} |
gBox_Name = *theNewName; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
*outNumberPropertiesChanged = 1; |
outChangedAddresses[0].mSelector = kAudioObjectPropertyName; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
} |
break; |
|
case kAudioObjectPropertyIdentify: |
// since we don't have any actual hardware to flash, we will schedule a notificaiton for |
// this property off into the future as a testing thing. Note that a real implementation |
// of this property should only send the notificaiton if the hardware wants the app to |
// flash it's UI for the device. |
{ |
syslog(LOG_NOTICE, "The identify property has been set on the Box implemented by the NullAudio driver."); |
FailWithAction(inDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_SetBoxPropertyData: wrong size for the data for kAudioObjectPropertyIdentify"); |
dispatch_after(dispatch_time(0, 2ULL * 1000ULL * 1000ULL * 1000ULL), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() |
{ |
AudioObjectPropertyAddress theAddress = { kAudioObjectPropertyIdentify, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; |
gPlugIn_Host->PropertiesChanged(gPlugIn_Host, kObjectID_Box, 1, &theAddress); |
}); |
} |
break; |
|
case kAudioBoxPropertyAcquired: |
// When the box is acquired, it means the contents, namely the device, are available to the system |
{ |
FailWithAction(inDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_SetBoxPropertyData: wrong size for the data for kAudioBoxPropertyAcquired"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
if(gBox_Acquired != (*((UInt32*)inData) != 0)) |
{ |
// the new value is different from the old value, so save it |
gBox_Acquired = *((UInt32*)inData) != 0; |
gPlugIn_Host->WriteToStorage(gPlugIn_Host, CFSTR("box acquired"), gBox_Acquired ? kCFBooleanTrue : kCFBooleanFalse); |
|
// and it means that this property and the device list property have changed |
*outNumberPropertiesChanged = 2; |
outChangedAddresses[0].mSelector = kAudioBoxPropertyAcquired; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
outChangedAddresses[1].mSelector = kAudioBoxPropertyDeviceList; |
outChangedAddresses[1].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[1].mElement = kAudioObjectPropertyElementMaster; |
|
// but it also means that the device list has changed for the plug-in too |
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() |
{ |
AudioObjectPropertyAddress theAddress = { kAudioPlugInPropertyDeviceList, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; |
gPlugIn_Host->PropertiesChanged(gPlugIn_Host, kObjectID_PlugIn, 1, &theAddress); |
}); |
} |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
} |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
#pragma mark Device Property Operations |
|
static Boolean NullAudio_HasDeviceProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) |
{ |
// This method returns whether or not the given object has the given property. |
|
#pragma unused(inClientProcessID) |
|
// declare the local variables |
Boolean theAnswer = false; |
|
// check the arguments |
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "NullAudio_HasDeviceProperty: bad driver reference"); |
FailIf(inAddress == NULL, Done, "NullAudio_HasDeviceProperty: no address"); |
FailIf(inObjectID != kObjectID_Device, Done, "NullAudio_HasDeviceProperty: not the device object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetDevicePropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyName: |
case kAudioObjectPropertyManufacturer: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioDevicePropertyDeviceUID: |
case kAudioDevicePropertyModelUID: |
case kAudioDevicePropertyTransportType: |
case kAudioDevicePropertyRelatedDevices: |
case kAudioDevicePropertyClockDomain: |
case kAudioDevicePropertyDeviceIsAlive: |
case kAudioDevicePropertyDeviceIsRunning: |
case kAudioObjectPropertyControlList: |
case kAudioDevicePropertyNominalSampleRate: |
case kAudioDevicePropertyAvailableNominalSampleRates: |
case kAudioDevicePropertyIsHidden: |
case kAudioDevicePropertyZeroTimeStampPeriod: |
case kAudioDevicePropertyIcon: |
case kAudioDevicePropertyStreams: |
theAnswer = true; |
break; |
|
case kAudioDevicePropertyDeviceCanBeDefaultDevice: |
case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: |
case kAudioDevicePropertyLatency: |
case kAudioDevicePropertySafetyOffset: |
case kAudioDevicePropertyPreferredChannelsForStereo: |
case kAudioDevicePropertyPreferredChannelLayout: |
theAnswer = (inAddress->mScope == kAudioObjectPropertyScopeInput) || (inAddress->mScope == kAudioObjectPropertyScopeOutput); |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_IsDevicePropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) |
{ |
// This method returns whether or not the given property on the object can have its value |
// changed. |
|
#pragma unused(inClientProcessID) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_IsDevicePropertySettable: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsDevicePropertySettable: no address"); |
FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsDevicePropertySettable: no place to put the return value"); |
FailWithAction(inObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_IsDevicePropertySettable: not the device object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetDevicePropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyName: |
case kAudioObjectPropertyManufacturer: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioDevicePropertyDeviceUID: |
case kAudioDevicePropertyModelUID: |
case kAudioDevicePropertyTransportType: |
case kAudioDevicePropertyRelatedDevices: |
case kAudioDevicePropertyClockDomain: |
case kAudioDevicePropertyDeviceIsAlive: |
case kAudioDevicePropertyDeviceIsRunning: |
case kAudioDevicePropertyDeviceCanBeDefaultDevice: |
case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: |
case kAudioDevicePropertyLatency: |
case kAudioDevicePropertyStreams: |
case kAudioObjectPropertyControlList: |
case kAudioDevicePropertySafetyOffset: |
case kAudioDevicePropertyAvailableNominalSampleRates: |
case kAudioDevicePropertyIsHidden: |
case kAudioDevicePropertyPreferredChannelsForStereo: |
case kAudioDevicePropertyPreferredChannelLayout: |
case kAudioDevicePropertyZeroTimeStampPeriod: |
case kAudioDevicePropertyIcon: |
*outIsSettable = false; |
break; |
|
case kAudioDevicePropertyNominalSampleRate: |
*outIsSettable = true; |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetDevicePropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) |
{ |
// This method returns the byte size of the property's data. |
|
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetDevicePropertyDataSize: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetDevicePropertyDataSize: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetDevicePropertyDataSize: no place to put the return value"); |
FailWithAction(inObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetDevicePropertyDataSize: not the device object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetDevicePropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyName: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyManufacturer: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
switch(inAddress->mScope) |
{ |
case kAudioObjectPropertyScopeGlobal: |
*outDataSize = 8 * sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyScopeInput: |
*outDataSize = 4 * sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyScopeOutput: |
*outDataSize = 4 * sizeof(AudioObjectID); |
break; |
}; |
break; |
|
case kAudioDevicePropertyDeviceUID: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioDevicePropertyModelUID: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioDevicePropertyTransportType: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyRelatedDevices: |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioDevicePropertyClockDomain: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyDeviceIsAlive: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioDevicePropertyDeviceIsRunning: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyDeviceCanBeDefaultDevice: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyLatency: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyStreams: |
switch(inAddress->mScope) |
{ |
case kAudioObjectPropertyScopeGlobal: |
*outDataSize = 2 * sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyScopeInput: |
*outDataSize = 1 * sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyScopeOutput: |
*outDataSize = 1 * sizeof(AudioObjectID); |
break; |
}; |
break; |
|
case kAudioObjectPropertyControlList: |
*outDataSize = 6 * sizeof(AudioObjectID); |
break; |
|
case kAudioDevicePropertySafetyOffset: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyNominalSampleRate: |
*outDataSize = sizeof(Float64); |
break; |
|
case kAudioDevicePropertyAvailableNominalSampleRates: |
*outDataSize = 2 * sizeof(AudioValueRange); |
break; |
|
case kAudioDevicePropertyIsHidden: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyPreferredChannelsForStereo: |
*outDataSize = 2 * sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyPreferredChannelLayout: |
*outDataSize = offsetof(AudioChannelLayout, mChannelDescriptions) + (2 * sizeof(AudioChannelDescription)); |
break; |
|
case kAudioDevicePropertyZeroTimeStampPeriod: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyIcon: |
*outDataSize = sizeof(CFURLRef); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetDevicePropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) |
{ |
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
UInt32 theNumberItemsToFetch; |
UInt32 theItemIndex; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetDevicePropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetDevicePropertyData: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetDevicePropertyData: no place to put the return value size"); |
FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetDevicePropertyData: no place to put the return value"); |
FailWithAction(inObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetDevicePropertyData: not the device object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. |
// |
// Also, since most of the data that will get returned is static, there are few instances where |
// it is necessary to lock the state mutex. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
// The base class for kAudioDeviceClassID is kAudioObjectClassID |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the device"); |
*((AudioClassID*)outData) = kAudioObjectClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
// The class is always kAudioDeviceClassID for devices created by drivers |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioObjectPropertyClass for the device"); |
*((AudioClassID*)outData) = kAudioDeviceClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
// The device's owner is the plug-in object |
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the device"); |
*((AudioObjectID*)outData) = kObjectID_PlugIn; |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyName: |
// This is the human readable name of the device. |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the device"); |
*((CFStringRef*)outData) = CFSTR("DeviceName"); |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyManufacturer: |
// This is the human readable name of the maker of the plug-in. |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the device"); |
*((CFStringRef*)outData) = CFSTR("ManufacturerName"); |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
// Calculate the number of items that have been requested. Note that this |
// number is allowed to be smaller than the actual size of the list. In such |
// case, only that number of items will be returned |
theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); |
|
// The device owns its streams and controls. Note that what is returned here |
// depends on the scope requested. |
switch(inAddress->mScope) |
{ |
case kAudioObjectPropertyScopeGlobal: |
// global scope means return all objects |
if(theNumberItemsToFetch > 8) |
{ |
theNumberItemsToFetch = 8; |
} |
|
// fill out the list with as many objects as requested, which is everything |
for(theItemIndex = 0; theItemIndex < theNumberItemsToFetch; ++theItemIndex) |
{ |
((AudioObjectID*)outData)[theItemIndex] = kObjectID_Stream_Input + theItemIndex; |
} |
break; |
|
case kAudioObjectPropertyScopeInput: |
// input scope means just the objects on the input side |
if(theNumberItemsToFetch > 4) |
{ |
theNumberItemsToFetch = 4; |
} |
|
// fill out the list with the right objects |
for(theItemIndex = 0; theItemIndex < theNumberItemsToFetch; ++theItemIndex) |
{ |
((AudioObjectID*)outData)[theItemIndex] = kObjectID_Stream_Input + theItemIndex; |
} |
break; |
|
case kAudioObjectPropertyScopeOutput: |
// output scope means just the objects on the output side |
if(theNumberItemsToFetch > 4) |
{ |
theNumberItemsToFetch = 4; |
} |
|
// fill out the list with the right objects |
for(theItemIndex = 0; theItemIndex < theNumberItemsToFetch; ++theItemIndex) |
{ |
((AudioObjectID*)outData)[theItemIndex] = kObjectID_Stream_Output + theItemIndex; |
} |
break; |
}; |
|
// report how much we wrote |
*outDataSize = theNumberItemsToFetch * sizeof(AudioObjectID); |
break; |
|
case kAudioDevicePropertyDeviceUID: |
// This is a CFString that is a persistent token that can identify the same |
// audio device across boot sessions. Note that two instances of the same |
// device must have different values for this property. |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceUID for the device"); |
*((CFStringRef*)outData) = CFSTR(kDevice_UID); |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioDevicePropertyModelUID: |
// This is a CFString that is a persistent token that can identify audio |
// devices that are the same kind of device. Note that two instances of the |
// save device must have the same value for this property. |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyModelUID for the device"); |
*((CFStringRef*)outData) = CFSTR(kDevice_ModelUID); |
*outDataSize = sizeof(CFStringRef); |
break; |
|
case kAudioDevicePropertyTransportType: |
// This value represents how the device is attached to the system. This can be |
// any 32 bit integer, but common values for this property are defined in |
// <CoreAudio/AudioHardwareBase.h> |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyTransportType for the device"); |
*((UInt32*)outData) = kAudioDeviceTransportTypeVirtual; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyRelatedDevices: |
// The related devices property identifys device objects that are very closely |
// related. Generally, this is for relating devices that are packaged together |
// in the hardware such as when the input side and the output side of a piece |
// of hardware can be clocked separately and therefore need to be represented |
// as separate AudioDevice objects. In such case, both devices would report |
// that they are related to each other. Note that at minimum, a device is |
// related to itself, so this list will always be at least one item long. |
|
// Calculate the number of items that have been requested. Note that this |
// number is allowed to be smaller than the actual size of the list. In such |
// case, only that number of items will be returned |
theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); |
|
// we only have the one device... |
if(theNumberItemsToFetch > 1) |
{ |
theNumberItemsToFetch = 1; |
} |
|
// Write the devices' object IDs into the return value |
if(theNumberItemsToFetch > 0) |
{ |
((AudioObjectID*)outData)[0] = kObjectID_Device; |
} |
|
// report how much we wrote |
*outDataSize = theNumberItemsToFetch * sizeof(AudioObjectID); |
break; |
|
case kAudioDevicePropertyClockDomain: |
// This property allows the device to declare what other devices it is |
// synchronized with in hardware. The way it works is that if two devices have |
// the same value for this property and the value is not zero, then the two |
// devices are synchronized in hardware. Note that a device that either can't |
// be synchronized with others or doesn't know should return 0 for this |
// property. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyClockDomain for the device"); |
*((UInt32*)outData) = 0; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyDeviceIsAlive: |
// This property returns whether or not the device is alive. Note that it is |
// note uncommon for a device to be dead but still momentarily availble in the |
// device list. In the case of this device, it will always be alive. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceIsAlive for the device"); |
*((UInt32*)outData) = 1; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyDeviceIsRunning: |
// This property returns whether or not IO is running for the device. Note that |
// we need to take both the state lock to check this value for thread safety. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceIsRunning for the device"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
*((UInt32*)outData) = ((gDevice_IOIsRunning > 0) > 0) ? 1 : 0; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyDeviceCanBeDefaultDevice: |
// This property returns whether or not the device wants to be able to be the |
// default device for content. This is the device that iTunes and QuickTime |
// will use to play their content on and FaceTime will use as it's microhphone. |
// Nearly all devices should allow for this. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceCanBeDefaultDevice for the device"); |
*((UInt32*)outData) = 1; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: |
// This property returns whether or not the device wants to be the system |
// default device. This is the device that is used to play interface sounds and |
// other incidental or UI-related sounds on. Most devices should allow this |
// although devices with lots of latency may not want to. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceCanBeDefaultSystemDevice for the device"); |
*((UInt32*)outData) = 1; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyLatency: |
// This property returns the presentation latency of the device. For this, |
// device, the value is 0 due to the fact that it always vends silence. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyLatency for the device"); |
*((UInt32*)outData) = 0; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyStreams: |
// Calculate the number of items that have been requested. Note that this |
// number is allowed to be smaller than the actual size of the list. In such |
// case, only that number of items will be returned |
theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); |
|
// Note that what is returned here depends on the scope requested. |
switch(inAddress->mScope) |
{ |
case kAudioObjectPropertyScopeGlobal: |
// global scope means return all streams |
if(theNumberItemsToFetch > 2) |
{ |
theNumberItemsToFetch = 2; |
} |
|
// fill out the list with as many objects as requested |
if(theNumberItemsToFetch > 0) |
{ |
((AudioObjectID*)outData)[0] = kObjectID_Stream_Input; |
} |
if(theNumberItemsToFetch > 1) |
{ |
((AudioObjectID*)outData)[1] = kObjectID_Stream_Output; |
} |
break; |
|
case kAudioObjectPropertyScopeInput: |
// input scope means just the objects on the input side |
if(theNumberItemsToFetch > 1) |
{ |
theNumberItemsToFetch = 1; |
} |
|
// fill out the list with as many objects as requested |
if(theNumberItemsToFetch > 0) |
{ |
((AudioObjectID*)outData)[0] = kObjectID_Stream_Input; |
} |
break; |
|
case kAudioObjectPropertyScopeOutput: |
// output scope means just the objects on the output side |
if(theNumberItemsToFetch > 1) |
{ |
theNumberItemsToFetch = 1; |
} |
|
// fill out the list with as many objects as requested |
if(theNumberItemsToFetch > 0) |
{ |
((AudioObjectID*)outData)[0] = kObjectID_Stream_Output; |
} |
break; |
}; |
|
// report how much we wrote |
*outDataSize = theNumberItemsToFetch * sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyControlList: |
// Calculate the number of items that have been requested. Note that this |
// number is allowed to be smaller than the actual size of the list. In such |
// case, only that number of items will be returned |
theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); |
if(theNumberItemsToFetch > 6) |
{ |
theNumberItemsToFetch = 6; |
} |
|
// fill out the list with as many objects as requested, which is everything |
for(theItemIndex = 0; theItemIndex < theNumberItemsToFetch; ++theItemIndex) |
{ |
if(theItemIndex < 3) |
{ |
((AudioObjectID*)outData)[theItemIndex] = kObjectID_Volume_Input_Master + theItemIndex; |
} |
else |
{ |
((AudioObjectID*)outData)[theItemIndex] = kObjectID_Volume_Output_Master + (theItemIndex - 3); |
} |
} |
|
// report how much we wrote |
*outDataSize = theNumberItemsToFetch * sizeof(AudioObjectID); |
break; |
|
case kAudioDevicePropertySafetyOffset: |
// This property returns the how close to now the HAL can read and write. For |
// this, device, the value is 0 due to the fact that it always vends silence. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertySafetyOffset for the device"); |
*((UInt32*)outData) = 0; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyNominalSampleRate: |
// This property returns the nominal sample rate of the device. Note that we |
// only need to take the state lock to get this value. |
FailWithAction(inDataSize < sizeof(Float64), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyNominalSampleRate for the device"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
*((Float64*)outData) = gDevice_SampleRate; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
*outDataSize = sizeof(Float64); |
break; |
|
case kAudioDevicePropertyAvailableNominalSampleRates: |
// This returns all nominal sample rates the device supports as an array of |
// AudioValueRangeStructs. Note that for discrete sampler rates, the range |
// will have the minimum value equal to the maximum value. |
|
// Calculate the number of items that have been requested. Note that this |
// number is allowed to be smaller than the actual size of the list. In such |
// case, only that number of items will be returned |
theNumberItemsToFetch = inDataSize / sizeof(AudioValueRange); |
|
// clamp it to the number of items we have |
if(theNumberItemsToFetch > 2) |
{ |
theNumberItemsToFetch = 2; |
} |
|
// fill out the return array |
if(theNumberItemsToFetch > 0) |
{ |
((AudioValueRange*)outData)[0].mMinimum = 44100.0; |
((AudioValueRange*)outData)[0].mMaximum = 44100.0; |
} |
if(theNumberItemsToFetch > 1) |
{ |
((AudioValueRange*)outData)[1].mMinimum = 48000.0; |
((AudioValueRange*)outData)[1].mMaximum = 48000.0; |
} |
|
// report how much we wrote |
*outDataSize = theNumberItemsToFetch * sizeof(AudioValueRange); |
break; |
|
case kAudioDevicePropertyIsHidden: |
// This returns whether or not the device is visible to clients. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyIsHidden for the device"); |
*((UInt32*)outData) = 0; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyPreferredChannelsForStereo: |
// This property returns which two channesl to use as left/right for stereo |
// data by default. Note that the channel numbers are 1-based.xz |
FailWithAction(inDataSize < (2 * sizeof(UInt32)), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyPreferredChannelsForStereo for the device"); |
((UInt32*)outData)[0] = 1; |
((UInt32*)outData)[1] = 2; |
*outDataSize = 2 * sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyPreferredChannelLayout: |
// This property returns the default AudioChannelLayout to use for the device |
// by default. For this device, we return a stereo ACL. |
{ |
// calcualte how big the |
UInt32 theACLSize = offsetof(AudioChannelLayout, mChannelDescriptions) + (2 * sizeof(AudioChannelDescription)); |
FailWithAction(inDataSize < theACLSize, theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyPreferredChannelLayout for the device"); |
((AudioChannelLayout*)outData)->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions; |
((AudioChannelLayout*)outData)->mChannelBitmap = 0; |
((AudioChannelLayout*)outData)->mNumberChannelDescriptions = 2; |
for(theItemIndex = 0; theItemIndex < 2; ++theItemIndex) |
{ |
((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mChannelLabel = kAudioChannelLabel_Left + theItemIndex; |
((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mChannelFlags = 0; |
((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mCoordinates[0] = 0; |
((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mCoordinates[1] = 0; |
((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mCoordinates[2] = 0; |
} |
*outDataSize = theACLSize; |
} |
break; |
|
case kAudioDevicePropertyZeroTimeStampPeriod: |
// This property returns how many frames the HAL should expect to see between |
// successive sample times in the zero time stamps this device provides. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyZeroTimeStampPeriod for the device"); |
*((UInt32*)outData) = kDevice_RingBufferSize; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioDevicePropertyIcon: |
{ |
// This is a CFURL that points to the device's Icon in the plug-in's resource bundle. |
FailWithAction(inDataSize < sizeof(CFURLRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceUID for the device"); |
CFBundleRef theBundle = CFBundleGetBundleWithIdentifier(CFSTR(kPlugIn_BundleID)); |
FailWithAction(theBundle == NULL, theAnswer = kAudioHardwareUnspecifiedError, Done, "NullAudio_GetDevicePropertyData: could not get the plug-in bundle for kAudioDevicePropertyIcon"); |
CFURLRef theURL = CFBundleCopyResourceURL(theBundle, CFSTR("DeviceIcon.icns"), NULL, NULL); |
FailWithAction(theURL == NULL, theAnswer = kAudioHardwareUnspecifiedError, Done, "NullAudio_GetDevicePropertyData: could not get the URL for kAudioDevicePropertyIcon"); |
*((CFURLRef*)outData) = theURL; |
*outDataSize = sizeof(CFURLRef); |
} |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_SetDevicePropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData, UInt32* outNumberPropertiesChanged, AudioObjectPropertyAddress outChangedAddresses[2]) |
{ |
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
Float64 theOldSampleRate; |
UInt64 theNewSampleRate; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_SetDevicePropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetDevicePropertyData: no address"); |
FailWithAction(outNumberPropertiesChanged == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetDevicePropertyData: no place to return the number of properties that changed"); |
FailWithAction(outChangedAddresses == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetDevicePropertyData: no place to return the properties that changed"); |
FailWithAction(inObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_SetDevicePropertyData: not the device object"); |
|
// initialize the returned number of changed properties |
*outNumberPropertiesChanged = 0; |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetDevicePropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioDevicePropertyNominalSampleRate: |
// Changing the sample rate needs to be handled via the |
// RequestConfigChange/PerformConfigChange machinery. |
|
// check the arguments |
FailWithAction(inDataSize != sizeof(Float64), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_SetDevicePropertyData: wrong size for the data for kAudioDevicePropertyNominalSampleRate"); |
FailWithAction((*((const Float64*)inData) != 44100.0) && (*((const Float64*)inData) != 48000.0), theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetDevicePropertyData: unsupported value for kAudioDevicePropertyNominalSampleRate"); |
|
// make sure that the new value is different than the old value |
pthread_mutex_lock(&gPlugIn_StateMutex); |
theOldSampleRate = gDevice_SampleRate; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
if(*((const Float64*)inData) != theOldSampleRate) |
{ |
// we dispatch this so that the change can happen asynchronously |
theOldSampleRate = *((const Float64*)inData); |
theNewSampleRate = (UInt64)theOldSampleRate; |
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ gPlugIn_Host->RequestDeviceConfigurationChange(gPlugIn_Host, kObjectID_Device, theNewSampleRate, NULL); }); |
} |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
#pragma mark Stream Property Operations |
|
static Boolean NullAudio_HasStreamProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) |
{ |
// This method returns whether or not the given object has the given property. |
|
#pragma unused(inClientProcessID) |
|
// declare the local variables |
Boolean theAnswer = false; |
|
// check the arguments |
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "NullAudio_HasStreamProperty: bad driver reference"); |
FailIf(inAddress == NULL, Done, "NullAudio_HasStreamProperty: no address"); |
FailIf((inObjectID != kObjectID_Stream_Input) && (inObjectID != kObjectID_Stream_Output), Done, "NullAudio_HasStreamProperty: not a stream object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetStreamPropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioStreamPropertyIsActive: |
case kAudioStreamPropertyDirection: |
case kAudioStreamPropertyTerminalType: |
case kAudioStreamPropertyStartingChannel: |
case kAudioStreamPropertyLatency: |
case kAudioStreamPropertyVirtualFormat: |
case kAudioStreamPropertyPhysicalFormat: |
case kAudioStreamPropertyAvailableVirtualFormats: |
case kAudioStreamPropertyAvailablePhysicalFormats: |
theAnswer = true; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_IsStreamPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) |
{ |
// This method returns whether or not the given property on the object can have its value |
// changed. |
|
#pragma unused(inClientProcessID) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_IsStreamPropertySettable: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsStreamPropertySettable: no address"); |
FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsStreamPropertySettable: no place to put the return value"); |
FailWithAction((inObjectID != kObjectID_Stream_Input) && (inObjectID != kObjectID_Stream_Output), theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_IsStreamPropertySettable: not a stream object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetStreamPropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioStreamPropertyDirection: |
case kAudioStreamPropertyTerminalType: |
case kAudioStreamPropertyStartingChannel: |
case kAudioStreamPropertyLatency: |
case kAudioStreamPropertyAvailableVirtualFormats: |
case kAudioStreamPropertyAvailablePhysicalFormats: |
*outIsSettable = false; |
break; |
|
case kAudioStreamPropertyIsActive: |
case kAudioStreamPropertyVirtualFormat: |
case kAudioStreamPropertyPhysicalFormat: |
*outIsSettable = true; |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetStreamPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) |
{ |
// This method returns the byte size of the property's data. |
|
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetStreamPropertyDataSize: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetStreamPropertyDataSize: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetStreamPropertyDataSize: no place to put the return value"); |
FailWithAction((inObjectID != kObjectID_Stream_Input) && (inObjectID != kObjectID_Stream_Output), theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetStreamPropertyDataSize: not a stream object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetStreamPropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
*outDataSize = 0 * sizeof(AudioObjectID); |
break; |
|
case kAudioStreamPropertyIsActive: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioStreamPropertyDirection: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioStreamPropertyTerminalType: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioStreamPropertyStartingChannel: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioStreamPropertyLatency: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioStreamPropertyVirtualFormat: |
case kAudioStreamPropertyPhysicalFormat: |
*outDataSize = sizeof(AudioStreamBasicDescription); |
break; |
|
case kAudioStreamPropertyAvailableVirtualFormats: |
case kAudioStreamPropertyAvailablePhysicalFormats: |
*outDataSize = 2 * sizeof(AudioStreamRangedDescription); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetStreamPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) |
{ |
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
UInt32 theNumberItemsToFetch; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetStreamPropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetStreamPropertyData: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetStreamPropertyData: no place to put the return value size"); |
FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetStreamPropertyData: no place to put the return value"); |
FailWithAction((inObjectID != kObjectID_Stream_Input) && (inObjectID != kObjectID_Stream_Output), theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetStreamPropertyData: not a stream object"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. |
// |
// Also, since most of the data that will get returned is static, there are few instances where |
// it is necessary to lock the state mutex. |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
// The base class for kAudioStreamClassID is kAudioObjectClassID |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetStreamPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the stream"); |
*((AudioClassID*)outData) = kAudioObjectClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
// The class is always kAudioStreamClassID for streams created by drivers |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetStreamPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the stream"); |
*((AudioClassID*)outData) = kAudioStreamClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
// The stream's owner is the device object |
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetStreamPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the stream"); |
*((AudioObjectID*)outData) = kObjectID_Device; |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
// Streams do not own any objects |
*outDataSize = 0 * sizeof(AudioObjectID); |
break; |
|
case kAudioStreamPropertyIsActive: |
// This property tells the device whether or not the given stream is going to |
// be used for IO. Note that we need to take the state lock to examine this |
// value. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyIsActive for the stream"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
*((UInt32*)outData) = (inObjectID == kObjectID_Stream_Input) ? gStream_Input_IsActive : gStream_Output_IsActive; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioStreamPropertyDirection: |
// This returns whether the stream is an input stream or an output stream. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyDirection for the stream"); |
*((UInt32*)outData) = (inObjectID == kObjectID_Stream_Input) ? 1 : 0; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioStreamPropertyTerminalType: |
// This returns a value that indicates what is at the other end of the stream |
// such as a speaker or headphones, or a microphone. Values for this property |
// are defined in <CoreAudio/AudioHardwareBase.h> |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyTerminalType for the stream"); |
*((UInt32*)outData) = (inObjectID == kObjectID_Stream_Input) ? kAudioStreamTerminalTypeMicrophone : kAudioStreamTerminalTypeSpeaker; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioStreamPropertyStartingChannel: |
// This property returns the absolute channel number for the first channel in |
// the stream. For exmaple, if a device has two output streams with two |
// channels each, then the starting channel number for the first stream is 1 |
// and ths starting channel number fo the second stream is 3. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyStartingChannel for the stream"); |
*((UInt32*)outData) = 1; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioStreamPropertyLatency: |
// This property returns any additonal presentation latency the stream has. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyStartingChannel for the stream"); |
*((UInt32*)outData) = 0; |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioStreamPropertyVirtualFormat: |
case kAudioStreamPropertyPhysicalFormat: |
// This returns the current format of the stream in an |
// AudioStreamBasicDescription. Note that we need to hold the state lock to get |
// this value. |
// Note that for devices that don't override the mix operation, the virtual |
// format has to be the same as the physical format. |
FailWithAction(inDataSize < sizeof(AudioStreamBasicDescription), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyVirtualFormat for the stream"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
((AudioStreamBasicDescription*)outData)->mSampleRate = gDevice_SampleRate; |
((AudioStreamBasicDescription*)outData)->mFormatID = kAudioFormatLinearPCM; |
((AudioStreamBasicDescription*)outData)->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; |
((AudioStreamBasicDescription*)outData)->mBytesPerPacket = 8; |
((AudioStreamBasicDescription*)outData)->mFramesPerPacket = 1; |
((AudioStreamBasicDescription*)outData)->mBytesPerFrame = 8; |
((AudioStreamBasicDescription*)outData)->mChannelsPerFrame = 2; |
((AudioStreamBasicDescription*)outData)->mBitsPerChannel = 32; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
*outDataSize = sizeof(AudioStreamBasicDescription); |
break; |
|
case kAudioStreamPropertyAvailableVirtualFormats: |
case kAudioStreamPropertyAvailablePhysicalFormats: |
// This returns an array of AudioStreamRangedDescriptions that describe what |
// formats are supported. |
|
// Calculate the number of items that have been requested. Note that this |
// number is allowed to be smaller than the actual size of the list. In such |
// case, only that number of items will be returned |
theNumberItemsToFetch = inDataSize / sizeof(AudioStreamRangedDescription); |
|
// clamp it to the number of items we have |
if(theNumberItemsToFetch > 2) |
{ |
theNumberItemsToFetch = 2; |
} |
|
// fill out the return array |
if(theNumberItemsToFetch > 0) |
{ |
((AudioStreamRangedDescription*)outData)[0].mFormat.mSampleRate = 44100.0; |
((AudioStreamRangedDescription*)outData)[0].mFormat.mFormatID = kAudioFormatLinearPCM; |
((AudioStreamRangedDescription*)outData)[0].mFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; |
((AudioStreamRangedDescription*)outData)[0].mFormat.mBytesPerPacket = 8; |
((AudioStreamRangedDescription*)outData)[0].mFormat.mFramesPerPacket = 1; |
((AudioStreamRangedDescription*)outData)[0].mFormat.mBytesPerFrame = 8; |
((AudioStreamRangedDescription*)outData)[0].mFormat.mChannelsPerFrame = 2; |
((AudioStreamRangedDescription*)outData)[0].mFormat.mBitsPerChannel = 32; |
((AudioStreamRangedDescription*)outData)[0].mSampleRateRange.mMinimum = 44100.0; |
((AudioStreamRangedDescription*)outData)[0].mSampleRateRange.mMaximum = 44100.0; |
} |
if(theNumberItemsToFetch > 1) |
{ |
((AudioStreamRangedDescription*)outData)[1].mFormat.mSampleRate = 48000.0; |
((AudioStreamRangedDescription*)outData)[1].mFormat.mFormatID = kAudioFormatLinearPCM; |
((AudioStreamRangedDescription*)outData)[1].mFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; |
((AudioStreamRangedDescription*)outData)[1].mFormat.mBytesPerPacket = 8; |
((AudioStreamRangedDescription*)outData)[1].mFormat.mFramesPerPacket = 1; |
((AudioStreamRangedDescription*)outData)[1].mFormat.mBytesPerFrame = 8; |
((AudioStreamRangedDescription*)outData)[1].mFormat.mChannelsPerFrame = 2; |
((AudioStreamRangedDescription*)outData)[1].mFormat.mBitsPerChannel = 32; |
((AudioStreamRangedDescription*)outData)[1].mSampleRateRange.mMinimum = 48000.0; |
((AudioStreamRangedDescription*)outData)[1].mSampleRateRange.mMaximum = 48000.0; |
} |
|
// report how much we wrote |
*outDataSize = theNumberItemsToFetch * sizeof(AudioStreamRangedDescription); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_SetStreamPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData, UInt32* outNumberPropertiesChanged, AudioObjectPropertyAddress outChangedAddresses[2]) |
{ |
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
Float64 theOldSampleRate; |
UInt64 theNewSampleRate; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_SetStreamPropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetStreamPropertyData: no address"); |
FailWithAction(outNumberPropertiesChanged == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetStreamPropertyData: no place to return the number of properties that changed"); |
FailWithAction(outChangedAddresses == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetStreamPropertyData: no place to return the properties that changed"); |
FailWithAction((inObjectID != kObjectID_Stream_Input) && (inObjectID != kObjectID_Stream_Output), theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_SetStreamPropertyData: not a stream object"); |
|
// initialize the returned number of changed properties |
*outNumberPropertiesChanged = 0; |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetStreamPropertyData() method. |
switch(inAddress->mSelector) |
{ |
case kAudioStreamPropertyIsActive: |
// Changing the active state of a stream doesn't affect IO or change the structure |
// so we can just save the state and send the notification. |
FailWithAction(inDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_SetStreamPropertyData: wrong size for the data for kAudioDevicePropertyNominalSampleRate"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
if(inObjectID == kObjectID_Stream_Input) |
{ |
if(gStream_Input_IsActive != (*((const UInt32*)inData) != 0)) |
{ |
gStream_Input_IsActive = *((const UInt32*)inData) != 0; |
*outNumberPropertiesChanged = 1; |
outChangedAddresses[0].mSelector = kAudioStreamPropertyIsActive; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
} |
} |
else |
{ |
if(gStream_Output_IsActive != (*((const UInt32*)inData) != 0)) |
{ |
gStream_Output_IsActive = *((const UInt32*)inData) != 0; |
*outNumberPropertiesChanged = 1; |
outChangedAddresses[0].mSelector = kAudioStreamPropertyIsActive; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
} |
} |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
break; |
|
case kAudioStreamPropertyVirtualFormat: |
case kAudioStreamPropertyPhysicalFormat: |
// Changing the stream format needs to be handled via the |
// RequestConfigChange/PerformConfigChange machinery. Note that because this |
// device only supports 2 channel 32 bit float data, the only thing that can |
// change is the sample rate. |
FailWithAction(inDataSize != sizeof(AudioStreamBasicDescription), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_SetStreamPropertyData: wrong size for the data for kAudioStreamPropertyPhysicalFormat"); |
FailWithAction(((const AudioStreamBasicDescription*)inData)->mFormatID != kAudioFormatLinearPCM, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "NullAudio_SetStreamPropertyData: unsupported format ID for kAudioStreamPropertyPhysicalFormat"); |
FailWithAction(((const AudioStreamBasicDescription*)inData)->mFormatFlags != (kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked), theAnswer = kAudioDeviceUnsupportedFormatError, Done, "NullAudio_SetStreamPropertyData: unsupported format flags for kAudioStreamPropertyPhysicalFormat"); |
FailWithAction(((const AudioStreamBasicDescription*)inData)->mBytesPerPacket != 8, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "NullAudio_SetStreamPropertyData: unsupported bytes per packet for kAudioStreamPropertyPhysicalFormat"); |
FailWithAction(((const AudioStreamBasicDescription*)inData)->mFramesPerPacket != 1, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "NullAudio_SetStreamPropertyData: unsupported frames per packet for kAudioStreamPropertyPhysicalFormat"); |
FailWithAction(((const AudioStreamBasicDescription*)inData)->mBytesPerFrame != 8, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "NullAudio_SetStreamPropertyData: unsupported bytes per frame for kAudioStreamPropertyPhysicalFormat"); |
FailWithAction(((const AudioStreamBasicDescription*)inData)->mChannelsPerFrame != 2, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "NullAudio_SetStreamPropertyData: unsupported channels per frame for kAudioStreamPropertyPhysicalFormat"); |
FailWithAction(((const AudioStreamBasicDescription*)inData)->mBitsPerChannel != 32, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "NullAudio_SetStreamPropertyData: unsupported bits per channel for kAudioStreamPropertyPhysicalFormat"); |
FailWithAction((((const AudioStreamBasicDescription*)inData)->mSampleRate != 44100.0) && (((const AudioStreamBasicDescription*)inData)->mSampleRate != 48000.0), theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetStreamPropertyData: unsupported sample rate for kAudioStreamPropertyPhysicalFormat"); |
|
// If we made it this far, the requested format is something we support, so make sure the sample rate is actually different |
pthread_mutex_lock(&gPlugIn_StateMutex); |
theOldSampleRate = gDevice_SampleRate; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
if(((const AudioStreamBasicDescription*)inData)->mSampleRate != theOldSampleRate) |
{ |
// we dispatch this so that the change can happen asynchronously |
theOldSampleRate = ((const AudioStreamBasicDescription*)inData)->mSampleRate; |
theNewSampleRate = (UInt64)theOldSampleRate; |
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ gPlugIn_Host->RequestDeviceConfigurationChange(gPlugIn_Host, kObjectID_Device, theNewSampleRate, NULL); }); |
} |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
#pragma mark Control Property Operations |
|
static Boolean NullAudio_HasControlProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) |
{ |
// This method returns whether or not the given object has the given property. |
|
#pragma unused(inClientProcessID) |
|
// declare the local variables |
Boolean theAnswer = false; |
|
// check the arguments |
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "NullAudio_HasControlProperty: bad driver reference"); |
FailIf(inAddress == NULL, Done, "NullAudio_HasControlProperty: no address"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetControlPropertyData() method. |
switch(inObjectID) |
{ |
case kObjectID_Volume_Input_Master: |
case kObjectID_Volume_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioControlPropertyScope: |
case kAudioControlPropertyElement: |
case kAudioLevelControlPropertyScalarValue: |
case kAudioLevelControlPropertyDecibelValue: |
case kAudioLevelControlPropertyDecibelRange: |
case kAudioLevelControlPropertyConvertScalarToDecibels: |
case kAudioLevelControlPropertyConvertDecibelsToScalar: |
theAnswer = true; |
break; |
}; |
break; |
|
case kObjectID_Mute_Input_Master: |
case kObjectID_Mute_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioControlPropertyScope: |
case kAudioControlPropertyElement: |
case kAudioBooleanControlPropertyValue: |
theAnswer = true; |
break; |
}; |
break; |
|
case kObjectID_DataSource_Input_Master: |
case kObjectID_DataSource_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioControlPropertyScope: |
case kAudioControlPropertyElement: |
case kAudioSelectorControlPropertyCurrentItem: |
case kAudioSelectorControlPropertyAvailableItems: |
case kAudioSelectorControlPropertyItemName: |
theAnswer = true; |
break; |
}; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_IsControlPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) |
{ |
// This method returns whether or not the given property on the object can have its value |
// changed. |
|
#pragma unused(inClientProcessID) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_IsControlPropertySettable: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsControlPropertySettable: no address"); |
FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_IsControlPropertySettable: no place to put the return value"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetControlPropertyData() method. |
switch(inObjectID) |
{ |
case kObjectID_Volume_Input_Master: |
case kObjectID_Volume_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioControlPropertyScope: |
case kAudioControlPropertyElement: |
case kAudioLevelControlPropertyDecibelRange: |
case kAudioLevelControlPropertyConvertScalarToDecibels: |
case kAudioLevelControlPropertyConvertDecibelsToScalar: |
*outIsSettable = false; |
break; |
|
case kAudioLevelControlPropertyScalarValue: |
case kAudioLevelControlPropertyDecibelValue: |
*outIsSettable = true; |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
case kObjectID_Mute_Input_Master: |
case kObjectID_Mute_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioControlPropertyScope: |
case kAudioControlPropertyElement: |
*outIsSettable = false; |
break; |
|
case kAudioBooleanControlPropertyValue: |
*outIsSettable = true; |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
case kObjectID_DataSource_Input_Master: |
case kObjectID_DataSource_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
case kAudioObjectPropertyClass: |
case kAudioObjectPropertyOwner: |
case kAudioObjectPropertyOwnedObjects: |
case kAudioControlPropertyScope: |
case kAudioControlPropertyElement: |
case kAudioSelectorControlPropertyAvailableItems: |
case kAudioSelectorControlPropertyItemName: |
*outIsSettable = false; |
break; |
|
case kAudioSelectorControlPropertyCurrentItem: |
*outIsSettable = true; |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
default: |
theAnswer = kAudioHardwareBadObjectError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetControlPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) |
{ |
// This method returns the byte size of the property's data. |
|
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetControlPropertyDataSize: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetControlPropertyDataSize: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetControlPropertyDataSize: no place to put the return value"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetControlPropertyData() method. |
switch(inObjectID) |
{ |
case kObjectID_Volume_Input_Master: |
case kObjectID_Volume_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
*outDataSize = 0 * sizeof(AudioObjectID); |
break; |
|
case kAudioControlPropertyScope: |
*outDataSize = sizeof(AudioObjectPropertyScope); |
break; |
|
case kAudioControlPropertyElement: |
*outDataSize = sizeof(AudioObjectPropertyElement); |
break; |
|
case kAudioLevelControlPropertyScalarValue: |
*outDataSize = sizeof(Float32); |
break; |
|
case kAudioLevelControlPropertyDecibelValue: |
*outDataSize = sizeof(Float32); |
break; |
|
case kAudioLevelControlPropertyDecibelRange: |
*outDataSize = sizeof(AudioValueRange); |
break; |
|
case kAudioLevelControlPropertyConvertScalarToDecibels: |
*outDataSize = sizeof(Float32); |
break; |
|
case kAudioLevelControlPropertyConvertDecibelsToScalar: |
*outDataSize = sizeof(Float32); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
case kObjectID_Mute_Input_Master: |
case kObjectID_Mute_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
*outDataSize = 0 * sizeof(AudioObjectID); |
break; |
|
case kAudioControlPropertyScope: |
*outDataSize = sizeof(AudioObjectPropertyScope); |
break; |
|
case kAudioControlPropertyElement: |
*outDataSize = sizeof(AudioObjectPropertyElement); |
break; |
|
case kAudioBooleanControlPropertyValue: |
*outDataSize = sizeof(UInt32); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
case kObjectID_DataSource_Input_Master: |
case kObjectID_DataSource_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
*outDataSize = 0 * sizeof(AudioObjectID); |
break; |
|
case kAudioControlPropertyScope: |
*outDataSize = sizeof(AudioObjectPropertyScope); |
break; |
|
case kAudioControlPropertyElement: |
*outDataSize = sizeof(AudioObjectPropertyElement); |
break; |
|
case kAudioSelectorControlPropertyCurrentItem: |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioSelectorControlPropertyAvailableItems: |
*outDataSize = kDataSource_NumberItems * sizeof(UInt32); |
break; |
|
case kAudioSelectorControlPropertyItemName: |
*outDataSize = sizeof(CFStringRef); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
default: |
theAnswer = kAudioHardwareBadObjectError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetControlPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) |
{ |
#pragma unused(inClientProcessID) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
UInt32 theNumberItemsToFetch; |
UInt32 theItemIndex; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetControlPropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetControlPropertyData: no address"); |
FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetControlPropertyData: no place to put the return value size"); |
FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetControlPropertyData: no place to put the return value"); |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. |
// |
// Also, since most of the data that will get returned is static, there are few instances where |
// it is necessary to lock the state mutex. |
switch(inObjectID) |
{ |
case kObjectID_Volume_Input_Master: |
case kObjectID_Volume_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
// The base class for kAudioVolumeControlClassID is kAudioLevelControlClassID |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the volume control"); |
*((AudioClassID*)outData) = kAudioLevelControlClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
// Volume controls are of the class, kAudioVolumeControlClassID |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the volume control"); |
*((AudioClassID*)outData) = kAudioVolumeControlClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
// The control's owner is the device object |
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the volume control"); |
*((AudioObjectID*)outData) = kObjectID_Device; |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
// Controls do not own any objects |
*outDataSize = 0 * sizeof(AudioObjectID); |
break; |
|
case kAudioControlPropertyScope: |
// This property returns the scope that the control is attached to. |
FailWithAction(inDataSize < sizeof(AudioObjectPropertyScope), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyScope for the volume control"); |
*((AudioObjectPropertyScope*)outData) = (inObjectID == kObjectID_Volume_Input_Master) ? kAudioObjectPropertyScopeInput : kAudioObjectPropertyScopeOutput; |
*outDataSize = sizeof(AudioObjectPropertyScope); |
break; |
|
case kAudioControlPropertyElement: |
// This property returns the element that the control is attached to. |
FailWithAction(inDataSize < sizeof(AudioObjectPropertyElement), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyElement for the volume control"); |
*((AudioObjectPropertyElement*)outData) = kAudioObjectPropertyElementMaster; |
*outDataSize = sizeof(AudioObjectPropertyElement); |
break; |
|
case kAudioLevelControlPropertyScalarValue: |
// This returns the value of the control in the normalized range of 0 to 1. |
// Note that we need to take the state lock to examine the value. |
FailWithAction(inDataSize < sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioLevelControlPropertyScalarValue for the volume control"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
*((Float32*)outData) = (inObjectID == kObjectID_Volume_Input_Master) ? gVolume_Input_Master_Value : gVolume_Output_Master_Value; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
*outDataSize = sizeof(Float32); |
break; |
|
case kAudioLevelControlPropertyDecibelValue: |
// This returns the dB value of the control. |
// Note that we need to take the state lock to examine the value. |
FailWithAction(inDataSize < sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioLevelControlPropertyDecibelValue for the volume control"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
*((Float32*)outData) = (inObjectID == kObjectID_Volume_Input_Master) ? gVolume_Input_Master_Value : gVolume_Output_Master_Value; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
|
// Note that we square the scalar value before converting to dB so as to |
// provide a better curve for the slider |
*((Float32*)outData) *= *((Float32*)outData); |
*((Float32*)outData) = kVolume_MinDB + (*((Float32*)outData) * (kVolume_MaxDB - kVolume_MinDB)); |
|
// report how much we wrote |
*outDataSize = sizeof(Float32); |
break; |
|
case kAudioLevelControlPropertyDecibelRange: |
// This returns the dB range of the control. |
FailWithAction(inDataSize < sizeof(AudioValueRange), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioLevelControlPropertyDecibelRange for the volume control"); |
((AudioValueRange*)outData)->mMinimum = kVolume_MinDB; |
((AudioValueRange*)outData)->mMaximum = kVolume_MaxDB; |
*outDataSize = sizeof(AudioValueRange); |
break; |
|
case kAudioLevelControlPropertyConvertScalarToDecibels: |
// This takes the scalar value in outData and converts it to dB. |
FailWithAction(inDataSize < sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioLevelControlPropertyDecibelValue for the volume control"); |
|
// clamp the value to be between 0 and 1 |
if(*((Float32*)outData) < 0.0) |
{ |
*((Float32*)outData) = 0; |
} |
if(*((Float32*)outData) > 1.0) |
{ |
*((Float32*)outData) = 1.0; |
} |
|
// Note that we square the scalar value before converting to dB so as to |
// provide a better curve for the slider |
*((Float32*)outData) *= *((Float32*)outData); |
*((Float32*)outData) = kVolume_MinDB + (*((Float32*)outData) * (kVolume_MaxDB - kVolume_MinDB)); |
|
// report how much we wrote |
*outDataSize = sizeof(Float32); |
break; |
|
case kAudioLevelControlPropertyConvertDecibelsToScalar: |
// This takes the dB value in outData and converts it to scalar. |
FailWithAction(inDataSize < sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioLevelControlPropertyDecibelValue for the volume control"); |
|
// clamp the value to be between kVolume_MinDB and kVolume_MaxDB |
if(*((Float32*)outData) < kVolume_MinDB) |
{ |
*((Float32*)outData) = kVolume_MinDB; |
} |
if(*((Float32*)outData) > kVolume_MaxDB) |
{ |
*((Float32*)outData) = kVolume_MaxDB; |
} |
|
// Note that we square the scalar value before converting to dB so as to |
// provide a better curve for the slider. We undo that here. |
*((Float32*)outData) = *((Float32*)outData) - kVolume_MinDB; |
*((Float32*)outData) /= kVolume_MaxDB - kVolume_MinDB; |
*((Float32*)outData) = sqrtf(*((Float32*)outData)); |
|
// report how much we wrote |
*outDataSize = sizeof(Float32); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
case kObjectID_Mute_Input_Master: |
case kObjectID_Mute_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
// The base class for kAudioMuteControlClassID is kAudioBooleanControlClassID |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the mute control"); |
*((AudioClassID*)outData) = kAudioBooleanControlClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
// Mute controls are of the class, kAudioMuteControlClassID |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the mute control"); |
*((AudioClassID*)outData) = kAudioMuteControlClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
// The control's owner is the device object |
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the mute control"); |
*((AudioObjectID*)outData) = kObjectID_Device; |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
// Controls do not own any objects |
*outDataSize = 0 * sizeof(AudioObjectID); |
break; |
|
case kAudioControlPropertyScope: |
// This property returns the scope that the control is attached to. |
FailWithAction(inDataSize < sizeof(AudioObjectPropertyScope), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyScope for the mute control"); |
*((AudioObjectPropertyScope*)outData) = (inObjectID == kObjectID_Mute_Input_Master) ? kAudioObjectPropertyScopeInput : kAudioObjectPropertyScopeOutput; |
*outDataSize = sizeof(AudioObjectPropertyScope); |
break; |
|
case kAudioControlPropertyElement: |
// This property returns the element that the control is attached to. |
FailWithAction(inDataSize < sizeof(AudioObjectPropertyElement), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyElement for the mute control"); |
*((AudioObjectPropertyElement*)outData) = kAudioObjectPropertyElementMaster; |
*outDataSize = sizeof(AudioObjectPropertyElement); |
break; |
|
case kAudioBooleanControlPropertyValue: |
// This returns the value of the mute control where 0 means that mute is off |
// and audio can be heard and 1 means that mute is on and audio cannot be heard. |
// Note that we need to take the state lock to examine this value. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioBooleanControlPropertyValue for the mute control"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
*((UInt32*)outData) = (inObjectID == kObjectID_Mute_Input_Master) ? (gMute_Input_Master_Value ? 1 : 0) : (gMute_Output_Master_Value ? 1 : 0); |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
*outDataSize = sizeof(UInt32); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
case kObjectID_DataSource_Input_Master: |
case kObjectID_DataSource_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioObjectPropertyBaseClass: |
// The base class for kAudioDataSourceControlClassID is kAudioSelectorControlClassID |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the data source control"); |
*((AudioClassID*)outData) = kAudioSelectorControlClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyClass: |
// Data Source controls are of the class, kAudioDataSourceControlClassID |
FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the data source control"); |
*((AudioClassID*)outData) = kAudioDataSourceControlClassID; |
*outDataSize = sizeof(AudioClassID); |
break; |
|
case kAudioObjectPropertyOwner: |
// The control's owner is the device object |
FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the data source control"); |
*((AudioObjectID*)outData) = kObjectID_Device; |
*outDataSize = sizeof(AudioObjectID); |
break; |
|
case kAudioObjectPropertyOwnedObjects: |
// Controls do not own any objects |
*outDataSize = 0 * sizeof(AudioObjectID); |
break; |
|
case kAudioControlPropertyScope: |
// This property returns the scope that the control is attached to. |
FailWithAction(inDataSize < sizeof(AudioObjectPropertyScope), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyScope for the data source control"); |
*((AudioObjectPropertyScope*)outData) = (inObjectID == kObjectID_DataSource_Input_Master) ? kAudioObjectPropertyScopeInput : kAudioObjectPropertyScopeOutput; |
*outDataSize = sizeof(AudioObjectPropertyScope); |
break; |
|
case kAudioControlPropertyElement: |
// This property returns the element that the control is attached to. |
FailWithAction(inDataSize < sizeof(AudioObjectPropertyElement), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyElement for the data source control"); |
*((AudioObjectPropertyElement*)outData) = kAudioObjectPropertyElementMaster; |
*outDataSize = sizeof(AudioObjectPropertyElement); |
break; |
|
case kAudioSelectorControlPropertyCurrentItem: |
// This returns the value of the data source selector. |
// Note that we need to take the state lock to examine this value. |
FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioSelectorControlPropertyCurrentItem for the data source control"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
*((UInt32*)outData) = (inObjectID == kObjectID_DataSource_Input_Master) ? gDataSource_Input_Master_Value : gDataSource_Output_Master_Value; |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
*outDataSize = sizeof(UInt32); |
break; |
|
case kAudioSelectorControlPropertyAvailableItems: |
// This returns the IDs for all the items the data source control supports. |
|
// Calculate the number of items that have been requested. Note that this |
// number is allowed to be smaller than the actual size of the list. In such |
// case, only that number of items will be returned |
theNumberItemsToFetch = inDataSize / sizeof(UInt32); |
|
// clamp it to the number of items we have |
if(theNumberItemsToFetch > kDataSource_NumberItems) |
{ |
theNumberItemsToFetch = kDataSource_NumberItems; |
} |
|
// fill out the return array |
for(theItemIndex = 0; theItemIndex < theNumberItemsToFetch; ++theItemIndex) |
{ |
((UInt32*)outData)[theItemIndex] = theItemIndex; |
} |
|
// report how much we wrote |
*outDataSize = theNumberItemsToFetch * sizeof(UInt32); |
break; |
|
case kAudioSelectorControlPropertyItemName: |
// This returns the user-readable name for the selector item in the qualifier |
FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: not enough space for the return value of kAudioSelectorControlPropertyItemName for the data source control"); |
FailWithAction(inQualifierDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_GetControlPropertyData: wrong size for the qualifier of kAudioSelectorControlPropertyItemName for the data source control"); |
FailWithAction(*((const UInt32*)inQualifierData) >= kDataSource_NumberItems, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_GetControlPropertyData: the item in the qualifier is not valid for kAudioSelectorControlPropertyItemName for the data source control"); |
*((CFStringRef*)outData) = CFStringCreateWithFormat(NULL, NULL, CFSTR(kDataSource_ItemNamePattern), *((const UInt32*)inQualifierData)); |
*outDataSize = sizeof(CFStringRef); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
default: |
theAnswer = kAudioHardwareBadObjectError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_SetControlPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData, UInt32* outNumberPropertiesChanged, AudioObjectPropertyAddress outChangedAddresses[2]) |
{ |
#pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
Float32 theNewVolume; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_SetControlPropertyData: bad driver reference"); |
FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetControlPropertyData: no address"); |
FailWithAction(outNumberPropertiesChanged == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetControlPropertyData: no place to return the number of properties that changed"); |
FailWithAction(outChangedAddresses == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetControlPropertyData: no place to return the properties that changed"); |
|
// initialize the returned number of changed properties |
*outNumberPropertiesChanged = 0; |
|
// Note that for each object, this driver implements all the required properties plus a few |
// extras that are useful but not required. There is more detailed commentary about each |
// property in the NullAudio_GetControlPropertyData() method. |
switch(inObjectID) |
{ |
case kObjectID_Volume_Input_Master: |
case kObjectID_Volume_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioLevelControlPropertyScalarValue: |
// For the scalar volume, we clamp the new value to [0, 1]. Note that if this |
// value changes, it implies that the dB value changed too. |
FailWithAction(inDataSize != sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_SetControlPropertyData: wrong size for the data for kAudioLevelControlPropertyScalarValue"); |
theNewVolume = *((const Float32*)inData); |
if(theNewVolume < 0.0) |
{ |
theNewVolume = 0.0; |
} |
else if(theNewVolume > 1.0) |
{ |
theNewVolume = 1.0; |
} |
pthread_mutex_lock(&gPlugIn_StateMutex); |
if(inObjectID == kObjectID_Volume_Input_Master) |
{ |
if(gVolume_Input_Master_Value != theNewVolume) |
{ |
gVolume_Input_Master_Value = theNewVolume; |
*outNumberPropertiesChanged = 2; |
outChangedAddresses[0].mSelector = kAudioLevelControlPropertyScalarValue; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
outChangedAddresses[1].mSelector = kAudioLevelControlPropertyDecibelValue; |
outChangedAddresses[1].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[1].mElement = kAudioObjectPropertyElementMaster; |
} |
} |
else |
{ |
if(gVolume_Output_Master_Value != theNewVolume) |
{ |
gVolume_Output_Master_Value = theNewVolume; |
*outNumberPropertiesChanged = 2; |
outChangedAddresses[0].mSelector = kAudioLevelControlPropertyScalarValue; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
outChangedAddresses[1].mSelector = kAudioLevelControlPropertyDecibelValue; |
outChangedAddresses[1].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[1].mElement = kAudioObjectPropertyElementMaster; |
} |
} |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
break; |
|
case kAudioLevelControlPropertyDecibelValue: |
// For the dB value, we first convert it to a scalar value since that is how |
// the value is tracked. Note that if this value changes, it implies that the |
// scalar value changes as well. |
FailWithAction(inDataSize != sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_SetControlPropertyData: wrong size for the data for kAudioLevelControlPropertyScalarValue"); |
theNewVolume = *((const Float32*)inData); |
if(theNewVolume < kVolume_MinDB) |
{ |
theNewVolume = kVolume_MinDB; |
} |
else if(theNewVolume > kVolume_MaxDB) |
{ |
theNewVolume = kVolume_MaxDB; |
} |
// Note that we square the scalar value before converting to dB so as to |
// provide a better curve for the slider. We undo that here. |
theNewVolume = theNewVolume - kVolume_MinDB; |
theNewVolume /= kVolume_MaxDB - kVolume_MinDB; |
theNewVolume = sqrtf(theNewVolume); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
if(inObjectID == kObjectID_Volume_Input_Master) |
{ |
if(gVolume_Input_Master_Value != theNewVolume) |
{ |
gVolume_Input_Master_Value = theNewVolume; |
*outNumberPropertiesChanged = 2; |
outChangedAddresses[0].mSelector = kAudioLevelControlPropertyScalarValue; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
outChangedAddresses[1].mSelector = kAudioLevelControlPropertyDecibelValue; |
outChangedAddresses[1].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[1].mElement = kAudioObjectPropertyElementMaster; |
} |
} |
else |
{ |
if(gVolume_Output_Master_Value != theNewVolume) |
{ |
gVolume_Output_Master_Value = theNewVolume; |
*outNumberPropertiesChanged = 2; |
outChangedAddresses[0].mSelector = kAudioLevelControlPropertyScalarValue; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
outChangedAddresses[1].mSelector = kAudioLevelControlPropertyDecibelValue; |
outChangedAddresses[1].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[1].mElement = kAudioObjectPropertyElementMaster; |
} |
} |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
case kObjectID_Mute_Input_Master: |
case kObjectID_Mute_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioBooleanControlPropertyValue: |
FailWithAction(inDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_SetControlPropertyData: wrong size for the data for kAudioBooleanControlPropertyValue"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
if(inObjectID == kObjectID_Mute_Input_Master) |
{ |
if(gMute_Input_Master_Value != (*((const UInt32*)inData) != 0)) |
{ |
gMute_Input_Master_Value = *((const UInt32*)inData) != 0; |
*outNumberPropertiesChanged = 1; |
outChangedAddresses[0].mSelector = kAudioBooleanControlPropertyValue; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
} |
} |
else |
{ |
if(gMute_Output_Master_Value != (*((const UInt32*)inData) != 0)) |
{ |
gMute_Output_Master_Value = *((const UInt32*)inData) != 0; |
*outNumberPropertiesChanged = 1; |
outChangedAddresses[0].mSelector = kAudioBooleanControlPropertyValue; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
} |
} |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
case kObjectID_DataSource_Input_Master: |
case kObjectID_DataSource_Output_Master: |
switch(inAddress->mSelector) |
{ |
case kAudioSelectorControlPropertyCurrentItem: |
// For selector controls, we check to make sure the requested value is in the |
// available items list and just store the value. |
FailWithAction(inDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "NullAudio_SetControlPropertyData: wrong size for the data for kAudioSelectorControlPropertyCurrentItem"); |
FailWithAction(*((const UInt32*)inData) >= kDataSource_NumberItems, theAnswer = kAudioHardwareIllegalOperationError, Done, "NullAudio_SetControlPropertyData: requested item not in available items list for kAudioSelectorControlPropertyCurrentItem"); |
pthread_mutex_lock(&gPlugIn_StateMutex); |
if(inObjectID == kObjectID_DataSource_Input_Master) |
{ |
if(gDataSource_Input_Master_Value != *((const UInt32*)inData)) |
{ |
gDataSource_Input_Master_Value = *((const UInt32*)inData); |
*outNumberPropertiesChanged = 1; |
outChangedAddresses[0].mSelector = kAudioSelectorControlPropertyCurrentItem; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
} |
} |
else |
{ |
if(gDataSource_Output_Master_Value != *((const UInt32*)inData)) |
{ |
gDataSource_Output_Master_Value = *((const UInt32*)inData); |
*outNumberPropertiesChanged = 1; |
outChangedAddresses[0].mSelector = kAudioSelectorControlPropertyCurrentItem; |
outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; |
outChangedAddresses[0].mElement = kAudioObjectPropertyElementMaster; |
} |
} |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
break; |
|
default: |
theAnswer = kAudioHardwareUnknownPropertyError; |
break; |
}; |
break; |
|
default: |
theAnswer = kAudioHardwareBadObjectError; |
break; |
}; |
|
Done: |
return theAnswer; |
} |
|
#pragma mark IO Operations |
|
static OSStatus NullAudio_StartIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID) |
{ |
// This call tells the device that IO is starting for the given client. When this routine |
// returns, the device's clock is running and it is ready to have data read/written. It is |
// important to note that multiple clients can have IO running on the device at the same time. |
// So, work only needs to be done when the first client starts. All subsequent starts simply |
// increment the counter. |
|
#pragma unused(inClientID) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_StartIO: bad driver reference"); |
FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_StartIO: bad device ID"); |
|
// we need to hold the state lock |
pthread_mutex_lock(&gPlugIn_StateMutex); |
|
// figure out what we need to do |
if(gDevice_IOIsRunning == UINT64_MAX) |
{ |
// overflowing is an error |
theAnswer = kAudioHardwareIllegalOperationError; |
} |
else if(gDevice_IOIsRunning == 0) |
{ |
// We need to start the hardware, which in this case is just anchoring the time line. |
gDevice_IOIsRunning = 1; |
gDevice_NumberTimeStamps = 0; |
gDevice_AnchorSampleTime = 0; |
gDevice_AnchorHostTime = mach_absolute_time(); |
} |
else |
{ |
// IO is already running, so just bump the counter |
++gDevice_IOIsRunning; |
} |
|
// unlock the state lock |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_StopIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID) |
{ |
// This call tells the device that the client has stopped IO. The driver can stop the hardware |
// once all clients have stopped. |
|
#pragma unused(inClientID) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_StopIO: bad driver reference"); |
FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_StopIO: bad device ID"); |
|
// we need to hold the state lock |
pthread_mutex_lock(&gPlugIn_StateMutex); |
|
// figure out what we need to do |
if(gDevice_IOIsRunning == 0) |
{ |
// underflowing is an error |
theAnswer = kAudioHardwareIllegalOperationError; |
} |
else if(gDevice_IOIsRunning == 1) |
{ |
// We need to stop the hardware, which in this case means that there's nothing to do. |
gDevice_IOIsRunning = 0; |
} |
else |
{ |
// IO is still running, so just bump the counter |
--gDevice_IOIsRunning; |
} |
|
// unlock the state lock |
pthread_mutex_unlock(&gPlugIn_StateMutex); |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, Float64* outSampleTime, UInt64* outHostTime, UInt64* outSeed) |
{ |
// This method returns the current zero time stamp for the device. The HAL models the timing of |
// a device as a series of time stamps that relate the sample time to a host time. The zero |
// time stamps are spaced such that the sample times are the value of |
// kAudioDevicePropertyZeroTimeStampPeriod apart. This is often modeled using a ring buffer |
// where the zero time stamp is updated when wrapping around the ring buffer. |
// |
// For this device, the zero time stamps' sample time increments every kDevice_RingBufferSize |
// frames and the host time increments by kDevice_RingBufferSize * gDevice_HostTicksPerFrame. |
|
#pragma unused(inClientID) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
UInt64 theCurrentHostTime; |
Float64 theHostTicksPerRingBuffer; |
Float64 theHostTickOffset; |
UInt64 theNextHostTime; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetZeroTimeStamp: bad driver reference"); |
FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_GetZeroTimeStamp: bad device ID"); |
|
// we need to hold the locks |
pthread_mutex_lock(&gDevice_IOMutex); |
|
// get the current host time |
theCurrentHostTime = mach_absolute_time(); |
|
// calculate the next host time |
theHostTicksPerRingBuffer = gDevice_HostTicksPerFrame * ((Float64)kDevice_RingBufferSize); |
theHostTickOffset = ((Float64)(gDevice_NumberTimeStamps + 1)) * theHostTicksPerRingBuffer; |
theNextHostTime = gDevice_AnchorHostTime + ((UInt64)theHostTickOffset); |
|
// go to the next time if the next host time is less than the current time |
if(theNextHostTime <= theCurrentHostTime) |
{ |
++gDevice_NumberTimeStamps; |
} |
|
// set the return values |
*outSampleTime = gDevice_NumberTimeStamps * kDevice_RingBufferSize; |
*outHostTime = gDevice_AnchorHostTime + (((Float64)gDevice_NumberTimeStamps) * theHostTicksPerRingBuffer); |
*outSeed = 1; |
|
// unlock the state lock |
pthread_mutex_unlock(&gDevice_IOMutex); |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_WillDoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, Boolean* outWillDo, Boolean* outWillDoInPlace) |
{ |
// This method returns whether or not the device will do a given IO operation. For this device, |
// we only support reading input data and writing output data. |
|
#pragma unused(inClientID) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_WillDoIOOperation: bad driver reference"); |
FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_WillDoIOOperation: bad device ID"); |
|
// figure out if we support the operation |
bool willDo = false; |
bool willDoInPlace = true; |
switch(inOperationID) |
{ |
case kAudioServerPlugInIOOperationReadInput: |
willDo = true; |
willDoInPlace = true; |
break; |
|
case kAudioServerPlugInIOOperationWriteMix: |
willDo = true; |
willDoInPlace = true; |
break; |
|
}; |
|
// fill out the return values |
if(outWillDo != NULL) |
{ |
*outWillDo = willDo; |
} |
if(outWillDoInPlace != NULL) |
{ |
*outWillDoInPlace = willDoInPlace; |
} |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_BeginIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo) |
{ |
// This is called at the beginning of an IO operation. This device doesn't do anything, so just |
// check the arguments and return. |
|
#pragma unused(inClientID, inOperationID, inIOBufferFrameSize, inIOCycleInfo) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_BeginIOOperation: bad driver reference"); |
FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_BeginIOOperation: bad device ID"); |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_DoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, AudioObjectID inStreamObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo, void* ioMainBuffer, void* ioSecondaryBuffer) |
{ |
// This is called to actuall perform a given operation. For this device, all we need to do is |
// clear the buffer for the ReadInput operation. |
|
#pragma unused(inClientID, inIOCycleInfo, ioSecondaryBuffer) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_DoIOOperation: bad driver reference"); |
FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_DoIOOperation: bad device ID"); |
FailWithAction((inStreamObjectID != kObjectID_Stream_Input) && (inStreamObjectID != kObjectID_Stream_Output), theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_DoIOOperation: bad stream ID"); |
|
// clear the buffer if this iskAudioServerPlugInIOOperationReadInput |
if(inOperationID == kAudioServerPlugInIOOperationReadInput) |
{ |
// we are always dealing with a 2 channel 32 bit float buffer |
memset(ioMainBuffer, 0, inIOBufferFrameSize * 8); |
} |
|
Done: |
return theAnswer; |
} |
|
static OSStatus NullAudio_EndIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo) |
{ |
// This is called at the end of an IO operation. This device doesn't do anything, so just check |
// the arguments and return. |
|
#pragma unused(inClientID, inOperationID, inIOBufferFrameSize, inIOCycleInfo) |
|
// declare the local variables |
OSStatus theAnswer = 0; |
|
// check the arguments |
FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_EndIOOperation: bad driver reference"); |
FailWithAction(inDeviceObjectID != kObjectID_Device, theAnswer = kAudioHardwareBadObjectError, Done, "NullAudio_EndIOOperation: bad device ID"); |
|
Done: |
return theAnswer; |
} |