mirror of
https://github.com/morgan9e/VolumeControl
synced 2026-04-14 00:04:05 +09:00
Initial commit
This commit is contained in:
946
driver/VC_PlugInInterface.cpp
Normal file
946
driver/VC_PlugInInterface.cpp
Normal file
@@ -0,0 +1,946 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// VC_PlugInInterface.cpp
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016, 2017 Kyle Neideck
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_PlugIn.cpp from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioServerPlugIn.h>
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
|
||||
// Local Includes
|
||||
#include "VC_Types.h"
|
||||
#include "VC_Object.h"
|
||||
#include "VC_PlugIn.h"
|
||||
#include "VC_Device.h"
|
||||
|
||||
|
||||
#pragma mark COM Prototypes
|
||||
|
||||
// Entry points for the COM methods
|
||||
extern "C" void* VC_Create(CFAllocatorRef inAllocator, CFUUIDRef inRequestedTypeUUID);
|
||||
static HRESULT VC_QueryInterface(void* inDriver, REFIID inUUID, LPVOID* outInterface);
|
||||
static ULONG VC_AddRef(void* inDriver);
|
||||
static ULONG VC_Release(void* inDriver);
|
||||
static OSStatus VC_Initialize(AudioServerPlugInDriverRef inDriver, AudioServerPlugInHostRef inHost);
|
||||
static OSStatus VC_CreateDevice(AudioServerPlugInDriverRef inDriver, CFDictionaryRef inDescription, const AudioServerPlugInClientInfo* inClientInfo, AudioObjectID* outDeviceObjectID);
|
||||
static OSStatus VC_DestroyDevice(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID);
|
||||
static OSStatus VC_AddDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo);
|
||||
static OSStatus VC_RemoveDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo);
|
||||
static OSStatus VC_PerformDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo);
|
||||
static OSStatus VC_AbortDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo);
|
||||
static Boolean VC_HasProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress);
|
||||
static OSStatus VC_IsPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable);
|
||||
static OSStatus VC_GetPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
|
||||
static OSStatus VC_GetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData);
|
||||
static OSStatus VC_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
|
||||
static OSStatus VC_StartIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID);
|
||||
static OSStatus VC_StopIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID);
|
||||
static OSStatus VC_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, Float64* outSampleTime, UInt64* outHostTime, UInt64* outSeed);
|
||||
static OSStatus VC_WillDoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, Boolean* outWillDo, Boolean* outWillDoInPlace);
|
||||
static OSStatus VC_BeginIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo);
|
||||
static OSStatus VC_DoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, AudioObjectID inStreamObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo, void* ioMainBuffer, void* ioSecondaryBuffer);
|
||||
static OSStatus VC_EndIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo);
|
||||
|
||||
#pragma mark The COM Interface
|
||||
|
||||
static AudioServerPlugInDriverInterface gAudioServerPlugInDriverInterface =
|
||||
{
|
||||
NULL,
|
||||
VC_QueryInterface,
|
||||
VC_AddRef,
|
||||
VC_Release,
|
||||
VC_Initialize,
|
||||
VC_CreateDevice,
|
||||
VC_DestroyDevice,
|
||||
VC_AddDeviceClient,
|
||||
VC_RemoveDeviceClient,
|
||||
VC_PerformDeviceConfigurationChange,
|
||||
VC_AbortDeviceConfigurationChange,
|
||||
VC_HasProperty,
|
||||
VC_IsPropertySettable,
|
||||
VC_GetPropertyDataSize,
|
||||
VC_GetPropertyData,
|
||||
VC_SetPropertyData,
|
||||
VC_StartIO,
|
||||
VC_StopIO,
|
||||
VC_GetZeroTimeStamp,
|
||||
VC_WillDoIOOperation,
|
||||
VC_BeginIOOperation,
|
||||
VC_DoIOOperation,
|
||||
VC_EndIOOperation
|
||||
};
|
||||
static AudioServerPlugInDriverInterface* gAudioServerPlugInDriverInterfacePtr = &gAudioServerPlugInDriverInterface;
|
||||
static AudioServerPlugInDriverRef gAudioServerPlugInDriverRef = &gAudioServerPlugInDriverInterfacePtr;
|
||||
static UInt32 gAudioServerPlugInDriverRefCount = 1;
|
||||
|
||||
// TODO: This name is a bit misleading because the devices are actually owned by the plug-in.
|
||||
static VC_Object& VC_LookUpOwnerObject(AudioObjectID inObjectID)
|
||||
{
|
||||
switch(inObjectID)
|
||||
{
|
||||
case kObjectID_PlugIn:
|
||||
return VC_PlugIn::GetInstance();
|
||||
|
||||
case kObjectID_Device:
|
||||
case kObjectID_Stream_Input:
|
||||
case kObjectID_Stream_Output:
|
||||
case kObjectID_Volume_Output_Master:
|
||||
case kObjectID_Mute_Output_Master:
|
||||
return VC_Device::GetInstance();
|
||||
}
|
||||
|
||||
DebugMsg("VC_LookUpOwnerObject: unknown object");
|
||||
Throw(CAException(kAudioHardwareBadObjectError));
|
||||
}
|
||||
|
||||
static VC_AbstractDevice& VC_LookUpDevice(AudioObjectID inObjectID)
|
||||
{
|
||||
switch(inObjectID)
|
||||
{
|
||||
case kObjectID_Device:
|
||||
return VC_Device::GetInstance();
|
||||
}
|
||||
|
||||
DebugMsg("VC_LookUpDevice: unknown device");
|
||||
Throw(CAException(kAudioHardwareBadDeviceError));
|
||||
}
|
||||
|
||||
#pragma mark Factory
|
||||
|
||||
extern "C"
|
||||
void* VC_Create(CFAllocatorRef inAllocator, CFUUIDRef inRequestedTypeUUID)
|
||||
{
|
||||
// This is the CFPlugIn factory function. Its job is to create the implementation for the given
|
||||
// type provided that the type is supported. Because this driver is simple and all its
|
||||
// initialization is handled via static initialization when the bundle is loaded, all that
|
||||
// needs to be done is to return the AudioServerPlugInDriverRef that points to the driver's
|
||||
// interface. A more complicated driver would create any base line objects it needs to satisfy
|
||||
// the IUnknown methods that are used to discover that actual interface to talk to the driver.
|
||||
// The majority of the driver's initialization should be handled in the Initialize() method of
|
||||
// the driver's AudioServerPlugInDriverInterface.
|
||||
|
||||
#pragma unused(inAllocator)
|
||||
|
||||
void* theAnswer = NULL;
|
||||
if(CFEqual(inRequestedTypeUUID, kAudioServerPlugInTypeUUID))
|
||||
{
|
||||
theAnswer = gAudioServerPlugInDriverRef;
|
||||
|
||||
VC_PlugIn::GetInstance();
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark Inheritence
|
||||
|
||||
static HRESULT VC_QueryInterface(void* inDriver, REFIID inUUID, LPVOID* outInterface)
|
||||
{
|
||||
// This function is called by the HAL to get the interface to talk to the plug-in through.
|
||||
// AudioServerPlugIns are required to support the IUnknown interface and the
|
||||
// AudioServerPlugInDriverInterface. As it happens, all interfaces must also provide the
|
||||
// IUnknown interface, so we can always just return the single interface we made with
|
||||
// gAudioServerPlugInDriverInterfacePtr regardless of which one is asked for.
|
||||
|
||||
HRESULT theAnswer = 0;
|
||||
CFUUIDRef theRequestedUUID = NULL;
|
||||
|
||||
try
|
||||
{
|
||||
// validate the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_QueryInterface: bad driver reference");
|
||||
ThrowIfNULL(outInterface, CAException(kAudioHardwareIllegalOperationError), "VC_QueryInterface: no place to store the returned interface");
|
||||
|
||||
// make a CFUUIDRef from inUUID
|
||||
theRequestedUUID = CFUUIDCreateFromUUIDBytes(NULL, inUUID);
|
||||
ThrowIf(theRequestedUUID == NULL, CAException(kAudioHardwareIllegalOperationError), "VC_QueryInterface: failed to create the CFUUIDRef");
|
||||
|
||||
// AudioServerPlugIns only support two interfaces, IUnknown (which has to be supported by all
|
||||
// CFPlugIns) and AudioServerPlugInDriverInterface (which is the actual interface the HAL will
|
||||
// use).
|
||||
ThrowIf(!CFEqual(theRequestedUUID, IUnknownUUID) && !CFEqual(theRequestedUUID, kAudioServerPlugInDriverInterfaceUUID), CAException(E_NOINTERFACE), "VC_QueryInterface: requested interface is unsupported");
|
||||
ThrowIf(gAudioServerPlugInDriverRefCount == UINT32_MAX, CAException(E_NOINTERFACE), "VC_QueryInterface: the ref count is maxxed out");
|
||||
|
||||
// do the work
|
||||
++gAudioServerPlugInDriverRefCount;
|
||||
*outInterface = gAudioServerPlugInDriverRef;
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
if(theRequestedUUID != NULL)
|
||||
{
|
||||
CFRelease(theRequestedUUID);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static ULONG VC_AddRef(void* inDriver)
|
||||
{
|
||||
// This call returns the resulting reference count after the increment.
|
||||
|
||||
ULONG theAnswer = 0;
|
||||
|
||||
// check the arguments
|
||||
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "VC_AddRef: bad driver reference");
|
||||
FailIf(gAudioServerPlugInDriverRefCount == UINT32_MAX, Done, "VC_AddRef: out of references");
|
||||
|
||||
// increment the refcount
|
||||
++gAudioServerPlugInDriverRefCount;
|
||||
theAnswer = gAudioServerPlugInDriverRefCount;
|
||||
|
||||
Done:
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static ULONG VC_Release(void* inDriver)
|
||||
{
|
||||
// This call returns the resulting reference count after the decrement.
|
||||
|
||||
ULONG theAnswer = 0;
|
||||
|
||||
// check the arguments
|
||||
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "VC_Release: bad driver reference");
|
||||
FailIf(gAudioServerPlugInDriverRefCount == UINT32_MAX, Done, "VC_Release: out of references");
|
||||
|
||||
// decrement the refcount
|
||||
// Note that we don't do anything special if the refcount goes to zero as the HAL
|
||||
// will never fully release a plug-in it opens. We keep managing the refcount so that
|
||||
// the API semantics are correct though.
|
||||
--gAudioServerPlugInDriverRefCount;
|
||||
theAnswer = gAudioServerPlugInDriverRefCount;
|
||||
|
||||
Done:
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark Basic Operations
|
||||
|
||||
static OSStatus VC_Initialize(AudioServerPlugInDriverRef inDriver, AudioServerPlugInHostRef inHost)
|
||||
{
|
||||
// The job of this method is, as the name implies, to get the driver initialized. One specific
|
||||
// thing that needs to be done is to store the AudioServerPlugInHostRef so that it can be used
|
||||
// later. Note that when this call returns, the HAL will scan the various lists the driver
|
||||
// maintains (such as the device list) to get the initial set of objects the driver is
|
||||
// publishing. So, there is no need to notifiy the HAL about any objects created as part of the
|
||||
// execution of this method.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Check the arguments.
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_Initialize: bad driver reference");
|
||||
|
||||
// Store the AudioServerPlugInHostRef.
|
||||
VC_PlugIn::GetInstance().SetHost(inHost);
|
||||
|
||||
// Init/activate the device.
|
||||
VC_Device::GetInstance();
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_CreateDevice(AudioServerPlugInDriverRef inDriver, CFDictionaryRef inDescription, const AudioServerPlugInClientInfo* inClientInfo, AudioObjectID* outDeviceObjectID)
|
||||
{
|
||||
// This method is used to tell a driver that implements the Transport Manager semantics to
|
||||
// create an AudioEndpointDevice from a set of AudioEndpoints. Since this driver is not a
|
||||
// Transport Manager, we just return kAudioHardwareUnsupportedOperationError.
|
||||
|
||||
#pragma unused(inDriver, inDescription, inClientInfo, outDeviceObjectID)
|
||||
|
||||
return kAudioHardwareUnsupportedOperationError;
|
||||
}
|
||||
|
||||
static OSStatus VC_DestroyDevice(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID)
|
||||
{
|
||||
// This method is used to tell a driver that implements the Transport Manager semantics to
|
||||
// destroy an AudioEndpointDevice. Since this driver is not a Transport Manager, we just check
|
||||
// the arguments and return kAudioHardwareUnsupportedOperationError.
|
||||
|
||||
#pragma unused(inDriver, inDeviceObjectID)
|
||||
|
||||
return kAudioHardwareUnsupportedOperationError;
|
||||
}
|
||||
|
||||
static OSStatus VC_AddDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo)
|
||||
{
|
||||
// This method is used to inform the driver about a new client that is using the given device.
|
||||
// This allows the device to act differently depending on who the client is.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Check the arguments.
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_AddDeviceClient: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_AddDeviceClient: unknown device");
|
||||
|
||||
// Inform the device.
|
||||
VC_LookUpDevice(inDeviceObjectID).AddClient(inClientInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_RemoveDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo)
|
||||
{
|
||||
// This method is used to inform the driver about a client that is no longer using the given
|
||||
// device.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Check the arguments.
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_RemoveDeviceClient: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_RemoveDeviceClient: unknown device");
|
||||
|
||||
// Inform the device.
|
||||
VC_LookUpDevice(inDeviceObjectID).RemoveClient(inClientInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_PerformDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo)
|
||||
{
|
||||
// This method is called to tell the device that it can perform the configuration change that it
|
||||
// had requested via a call to the host method, RequestDeviceConfigurationChange(). The
|
||||
// arguments, inChangeAction and inChangeInfo are the same as what was passed to
|
||||
// RequestDeviceConfigurationChange().
|
||||
//
|
||||
// The HAL guarantees that IO will be stopped while this method is in progress. The HAL will
|
||||
// also handle figuring out exactly what changed for the non-control related properties. This
|
||||
// means that the only notifications that would need to be sent here would be for either
|
||||
// custom properties the HAL doesn't know about or for controls.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_PerformDeviceConfigurationChange: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_PerformDeviceConfigurationChange: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).PerformConfigChange(inChangeAction, inChangeInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_AbortDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo)
|
||||
{
|
||||
// This method is called to tell the driver that a request for a config change has been denied.
|
||||
// This provides the driver an opportunity to clean up any state associated with the request.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_PerformDeviceConfigurationChange: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_PerformDeviceConfigurationChange: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).AbortConfigChange(inChangeAction, inChangeInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
static Boolean VC_HasProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress)
|
||||
{
|
||||
// This method returns whether or not the given object has the given property.
|
||||
|
||||
Boolean theAnswer = false;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_HasProperty: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "VC_HasProperty: no address");
|
||||
|
||||
theAnswer = VC_LookUpOwnerObject(inObjectID).HasProperty(inObjectID, inClientProcessID, *inAddress);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LogError("VC_PlugInInterface::VC_HasProperty: unknown exception. (object: %u, address: %u)",
|
||||
inObjectID,
|
||||
inAddress ? inAddress->mSelector : 0);
|
||||
theAnswer = false;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_IsPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable)
|
||||
{
|
||||
// This method returns whether or not the given property on the object can have its value
|
||||
// changed.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_IsPropertySettable: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "VC_IsPropertySettable: no address");
|
||||
ThrowIfNULL(outIsSettable, CAException(kAudioHardwareIllegalOperationError), "VC_IsPropertySettable: no place to put the return value");
|
||||
|
||||
VC_Object& theAudioObject = VC_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
*outIsSettable = theAudioObject.IsPropertySettable(inObjectID, inClientProcessID, *inAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LogError("VC_PlugInInterface::VC_IsPropertySettable: unknown exception. (object: %u, address: %u)",
|
||||
inObjectID,
|
||||
inAddress ? inAddress->mSelector : 0);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_GetPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize)
|
||||
{
|
||||
// This method returns the byte size of the property's data.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_GetPropertyDataSize: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "VC_GetPropertyDataSize: no address");
|
||||
ThrowIfNULL(outDataSize, CAException(kAudioHardwareIllegalOperationError), "VC_GetPropertyDataSize: no place to put the return value");
|
||||
|
||||
VC_Object& theAudioObject = VC_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
*outDataSize = theAudioObject.GetPropertyDataSize(inObjectID, inClientProcessID, *inAddress, inQualifierDataSize, inQualifierData);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LogError("VC_PlugInInterface::VC_GetPropertyDataSize: unknown exception. (object: %u, address: %u)",
|
||||
inObjectID,
|
||||
inAddress ? inAddress->mSelector : 0);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_GetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData)
|
||||
{
|
||||
// This method fetches the data for a given property
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_GetPropertyData: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "VC_GetPropertyData: no address");
|
||||
ThrowIfNULL(outDataSize, CAException(kAudioHardwareIllegalOperationError), "VC_GetPropertyData: no place to put the return value size");
|
||||
ThrowIfNULL(outData, CAException(kAudioHardwareIllegalOperationError), "VC_GetPropertyData: no place to put the return value");
|
||||
|
||||
VC_Object& theAudioObject = VC_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
theAudioObject.GetPropertyData(inObjectID, inClientProcessID, *inAddress, inQualifierDataSize, inQualifierData, inDataSize, *outDataSize, outData);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LogError("VC_PlugInInterface::VC_GetPropertyData: unknown exception. (object: %u, address: %u)",
|
||||
inObjectID,
|
||||
inAddress ? inAddress->mSelector : 0);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
|
||||
{
|
||||
// This method changes the value of the given property
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "VC_SetPropertyData: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "VC_SetPropertyData: no address");
|
||||
ThrowIfNULL(inData, CAException(kAudioHardwareIllegalOperationError), "VC_SetPropertyData: no data");
|
||||
|
||||
VC_Object& theAudioObject = VC_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
if(theAudioObject.IsPropertySettable(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
theAudioObject.SetPropertyData(inObjectID, inClientProcessID, *inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnsupportedOperationError;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LogError("VC_PlugInInterface::VC_SetPropertyData: unknown exception. (object: %u, address: %u)",
|
||||
inObjectID,
|
||||
inAddress ? inAddress->mSelector : 0);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark IO Operations
|
||||
|
||||
static OSStatus VC_StartIO(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID)
|
||||
{
|
||||
// This call tells the device that IO is starting for the given client. When this routine
|
||||
// returns, the device's clock is running and it is ready to have data read/written. It is
|
||||
// important to note that multiple clients can have IO running on the device at the same time.
|
||||
// So, work only needs to be done when the first client starts. All subsequent starts simply
|
||||
// increment the counter.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_StartIO: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_StartIO: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).StartIO(inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_StopIO(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID)
|
||||
{
|
||||
// This call tells the device that the client has stopped IO. The driver can stop the hardware
|
||||
// once all clients have stopped.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_StopIO: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_StopIO: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).StopIO(inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID,
|
||||
Float64* outSampleTime,
|
||||
UInt64* outHostTime,
|
||||
UInt64* outSeed)
|
||||
{
|
||||
#pragma unused(inClientID)
|
||||
// This method returns the current zero time stamp for the device. The HAL models the timing of
|
||||
// a device as a series of time stamps that relate the sample time to a host time. The zero
|
||||
// time stamps are spaced such that the sample times are the value of
|
||||
// kAudioDevicePropertyZeroTimeStampPeriod apart. This is often modeled using a ring buffer
|
||||
// where the zero time stamp is updated when wrapping around the ring buffer.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_GetZeroTimeStamp: bad driver reference");
|
||||
ThrowIfNULL(outSampleTime,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_GetZeroTimeStamp: no place to put the sample time");
|
||||
ThrowIfNULL(outHostTime,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_GetZeroTimeStamp: no place to put the host time");
|
||||
ThrowIfNULL(outSeed,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_GetZeroTimeStamp: no place to put the seed");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_GetZeroTimeStamp: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).GetZeroTimeStamp(*outSampleTime, *outHostTime, *outSeed);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_WillDoIOOperation(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID,
|
||||
UInt32 inOperationID,
|
||||
Boolean* outWillDo,
|
||||
Boolean* outWillDoInPlace)
|
||||
{
|
||||
#pragma unused(inClientID)
|
||||
// This method returns whether or not the device will do a given IO operation.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_WillDoIOOperation: bad driver reference");
|
||||
ThrowIfNULL(outWillDo,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_WillDoIOOperation: no place to put the will-do return value");
|
||||
ThrowIfNULL(outWillDoInPlace,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_WillDoIOOperation: no place to put the in-place return value");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_WillDoIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
bool willDo = false;
|
||||
bool willDoInPlace = false;
|
||||
VC_LookUpDevice(inDeviceObjectID).WillDoIOOperation(inOperationID, willDo, willDoInPlace);
|
||||
|
||||
// set the return values
|
||||
*outWillDo = willDo;
|
||||
*outWillDoInPlace = willDoInPlace;
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_BeginIOOperation(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID,
|
||||
UInt32 inOperationID,
|
||||
UInt32 inIOBufferFrameSize,
|
||||
const AudioServerPlugInIOCycleInfo* inIOCycleInfo)
|
||||
{
|
||||
// This is called at the beginning of an IO operation.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_BeginIOOperation: bad driver reference");
|
||||
ThrowIfNULL(inIOCycleInfo,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_BeginIOOperation: no cycle info");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_BeginIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).BeginIOOperation(inOperationID,
|
||||
inIOBufferFrameSize,
|
||||
*inIOCycleInfo,
|
||||
inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
DebugMsg("VC_PlugInInterface::VC_BeginIOOperation: unknown exception. (device: %s, operation: %u)",
|
||||
(inDeviceObjectID == kObjectID_Device ? "VCDevice" : "other"),
|
||||
inOperationID);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_DoIOOperation(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
AudioObjectID inStreamObjectID,
|
||||
UInt32 inClientID,
|
||||
UInt32 inOperationID,
|
||||
UInt32 inIOBufferFrameSize,
|
||||
const AudioServerPlugInIOCycleInfo* inIOCycleInfo,
|
||||
void* ioMainBuffer,
|
||||
void* ioSecondaryBuffer)
|
||||
{
|
||||
// This is called to actually perform a given IO operation.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_EndIOOperation: bad driver reference");
|
||||
ThrowIfNULL(inIOCycleInfo,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_EndIOOperation: no cycle info");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_EndIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).DoIOOperation(inStreamObjectID,
|
||||
inClientID,
|
||||
inOperationID,
|
||||
inIOBufferFrameSize,
|
||||
*inIOCycleInfo,
|
||||
ioMainBuffer,
|
||||
ioSecondaryBuffer);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
DebugMsg("VC_PlugInInterface::VC_DoIOOperation: unknown exception. (device: %s, operation: %u)",
|
||||
(inDeviceObjectID == kObjectID_Device ? "VCDevice" : "other"),
|
||||
inOperationID);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus VC_EndIOOperation(AudioServerPlugInDriverRef inDriver,
|
||||
AudioObjectID inDeviceObjectID,
|
||||
UInt32 inClientID,
|
||||
UInt32 inOperationID,
|
||||
UInt32 inIOBufferFrameSize,
|
||||
const AudioServerPlugInIOCycleInfo* inIOCycleInfo)
|
||||
{
|
||||
// This is called at the end of an IO operation.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef,
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"VC_EndIOOperation: bad driver reference");
|
||||
ThrowIfNULL(inIOCycleInfo,
|
||||
CAException(kAudioHardwareIllegalOperationError),
|
||||
"VC_EndIOOperation: no cycle info");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device,
|
||||
CAException(kAudioHardwareBadDeviceError),
|
||||
"VC_EndIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
VC_LookUpDevice(inDeviceObjectID).EndIOOperation(inOperationID,
|
||||
inIOBufferFrameSize,
|
||||
*inIOCycleInfo,
|
||||
inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
DebugMsg("VC_PlugInInterface::VC_EndIOOperation: unknown exception. (device: %s, operation: %u)",
|
||||
(inDeviceObjectID == kObjectID_Device ? "VCDevice" : "other"),
|
||||
inOperationID);
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user