BlackHole.c (216363B)
1 /* 2 File: BlackHole.c 3 4 Copyright (C) 2019 Existential Audio Inc. 5 6 */ 7 /*================================================================================================== 8 BlackHole.c 9 ==================================================================================================*/ 10 11 //================================================================================================== 12 // Includes 13 //================================================================================================== 14 15 #include <CoreAudio/AudioServerPlugIn.h> 16 #include <dispatch/dispatch.h> 17 #include <mach/mach_time.h> 18 #include <pthread.h> 19 #include <stdint.h> 20 #include <sys/syslog.h> 21 #include <Accelerate/Accelerate.h> 22 #include <Availability.h> 23 24 //================================================================================================== 25 #pragma mark - 26 #pragma mark Macros 27 //================================================================================================== 28 29 #if TARGET_RT_BIG_ENDIAN 30 #define FourCCToCString(the4CC) { ((char*)&the4CC)[0], ((char*)&the4CC)[1], ((char*)&the4CC)[2], ((char*)&the4CC)[3], 0 } 31 #else 32 #define FourCCToCString(the4CC) { ((char*)&the4CC)[3], ((char*)&the4CC)[2], ((char*)&the4CC)[1], ((char*)&the4CC)[0], 0 } 33 #endif 34 35 #ifndef __MAC_12_0 36 #define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster 37 #endif 38 39 #if DEBUG 40 41 #define DebugMsg(inFormat, ...) syslog(LOG_NOTICE, inFormat, ## __VA_ARGS__) 42 43 #define FailIf(inCondition, inHandler, inMessage) \ 44 if(inCondition) \ 45 { \ 46 DebugMsg(inMessage); \ 47 goto inHandler; \ 48 } 49 50 #define FailWithAction(inCondition, inAction, inHandler, inMessage) \ 51 if(inCondition) \ 52 { \ 53 DebugMsg(inMessage); \ 54 { inAction; } \ 55 goto inHandler; \ 56 } 57 58 #else 59 60 #define DebugMsg(inFormat, ...) 61 62 #define FailIf(inCondition, inHandler, inMessage) \ 63 if(inCondition) \ 64 { \ 65 goto inHandler; \ 66 } 67 68 #define FailWithAction(inCondition, inAction, inHandler, inMessage) \ 69 if(inCondition) \ 70 { \ 71 { inAction; } \ 72 goto inHandler; \ 73 } 74 75 #endif 76 77 78 //================================================================================================== 79 #pragma mark - 80 #pragma mark BlackHole State 81 //================================================================================================== 82 83 // The driver has the following 84 // qualities: 85 // - a box 86 // - a device 87 // - supports 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, 768000, 8000, 16000 sample rates 88 89 90 // - provides a rate scalar of 1.0 via hard coding 91 // - a single output stream 92 // - supports 16 channels of 32 bit float LPCM samples 93 // - writes to ring buffer 94 // - a single input stream 95 // - supports 16 channels of 32 bit float LPCM samples 96 // - reads from ring buffer 97 // - controls 98 // - master input volume 99 // - master output volume 100 // - master input mute 101 // - master output mute 102 103 104 // Declare the internal object ID numbers for all the objects this driver implements. Note that 105 // because the driver has fixed set of objects that never grows or shrinks. If this were not the 106 // case, the driver would need to have a means to dynamically allocate these IDs. It is important 107 // to realize that a lot of the structure of this driver is vastly simpler when the IDs are all 108 // known a priori. Comments in the code will try to identify some of these simplifications and 109 // point out what a more complicated driver will need to do. 110 enum 111 { 112 kObjectID_PlugIn = kAudioObjectPlugInObject, 113 kObjectID_Box = 2, 114 kObjectID_Device = 3, 115 kObjectID_Stream_Input = 4, 116 kObjectID_Volume_Input_Master = 5, 117 kObjectID_Mute_Input_Master = 6, 118 kObjectID_Stream_Output = 7, 119 kObjectID_Volume_Output_Master = 8, 120 kObjectID_Mute_Output_Master = 9, 121 kObjectID_Pitch_Adjust = 10, 122 kObjectID_ClockSource = 11, 123 kObjectID_Device2 = 12, 124 }; 125 126 enum 127 { 128 ChangeAction_SetSampleRate = 1, 129 ChangeAction_EnablePitchControl = 2, 130 ChangeAction_DisablePitchControl = 3, 131 }; 132 133 enum ObjectType 134 { 135 kObjectType_Stream, 136 kObjectType_Control 137 }; 138 139 struct ObjectInfo { 140 AudioObjectID id; 141 enum ObjectType type; 142 AudioObjectPropertyScope scope; 143 }; 144 145 // Declare the stuff that tracks the state of the plug-in, the device and its sub-objects. 146 // Note that we use global variables here because this driver only ever has a single device. If 147 // multiple devices were supported, this state would need to be encapsulated in one or more structs 148 // so that each object's state can be tracked individually. 149 // Note also that we share a single mutex across all objects to be thread safe for the same reason. 150 151 152 #ifndef kDriver_Name 153 #define kDriver_Name "BlackHole" 154 #endif 155 156 #ifndef kPlugIn_BundleID 157 #define kPlugIn_BundleID "audio.existential.BlackHole2ch" 158 #endif 159 160 #ifndef kPlugIn_Icon 161 #define kPlugIn_Icon "BlackHole.icns" 162 #endif 163 164 #ifndef kHas_Driver_Name_Format 165 #define kHas_Driver_Name_Format true 166 #endif 167 168 #if kHas_Driver_Name_Format 169 #define kDriver_Name_Format "%ich" 170 #define kBox_UID kDriver_Name kDriver_Name_Format "_UID" 171 #define kDevice_UID kDriver_Name kDriver_Name_Format "_UID" 172 #define kDevice2_UID kDriver_Name kDriver_Name_Format "_2_UID" 173 #define kDevice_ModelUID kDriver_Name kDriver_Name_Format "_ModelUID" 174 175 176 #ifndef kDevice_Name 177 #define kDevice_Name kDriver_Name " %ich" 178 #endif 179 180 #ifndef kDevice2_Name 181 #define kDevice2_Name kDriver_Name " %ich 2" 182 #endif 183 184 185 #else 186 #define kBox_UID kDriver_Name "_UID" 187 #define kDevice_UID kDriver_Name "_UID" 188 #define kDevice2_UID kDriver_Name "_2_UID" 189 #define kDevice_ModelUID kDriver_Name "_ModelUID" 190 191 192 #ifndef kDevice_Name 193 #define kDevice_Name kDriver_Name " " 194 #endif 195 196 #ifndef kDevice2_Name 197 #define kDevice2_Name kDriver_Name " Mirror" 198 #endif 199 200 #endif 201 202 #ifndef kDevice_IsHidden 203 #define kDevice_IsHidden false 204 #endif 205 206 #ifndef kDevice2_IsHidden 207 #define kDevice2_IsHidden true 208 #endif 209 210 211 212 #ifndef kDevice_HasInput 213 #define kDevice_HasInput true 214 #endif 215 216 #ifndef kDevice_HasOutput 217 #define kDevice_HasOutput true 218 #endif 219 220 #ifndef kDevice2_HasInput 221 #define kDevice2_HasInput true 222 #endif 223 224 #ifndef kDevice2_HasOutput 225 #define kDevice2_HasOutput true 226 #endif 227 228 229 230 #ifndef kManufacturer_Name 231 #define kManufacturer_Name "Existential Audio Inc." 232 #endif 233 234 #define kLatency_Frame_Size 0 235 236 #ifndef kNumber_Of_Channels 237 #define kNumber_Of_Channels 2 238 #endif 239 240 #ifndef kEnableVolumeControl 241 #define kEnableVolumeControl true 242 #endif 243 244 #ifndef kCanBeDefaultDevice 245 #define kCanBeDefaultDevice true 246 #endif 247 248 #ifndef kCanBeDefaultSystemDevice 249 #define kCanBeDefaultSystemDevice true 250 #endif 251 252 static pthread_mutex_t gPlugIn_StateMutex = PTHREAD_MUTEX_INITIALIZER; 253 static UInt32 gPlugIn_RefCount = 0; 254 static AudioServerPlugInHostRef gPlugIn_Host = NULL; 255 256 257 static CFStringRef gBox_Name = NULL; 258 259 #ifndef kBox_Aquired 260 #define kBox_Aquired true 261 #endif 262 static Boolean gBox_Acquired = kBox_Aquired; 263 264 265 static pthread_mutex_t gDevice_IOMutex = PTHREAD_MUTEX_INITIALIZER; 266 // Hydra: startup sample rate is overridable at build time (-DkDefault_SampleRate=44100) so 267 // the virtual device can come up matching the host's rate. Defaults to 48000 = upstream. 268 #ifndef kDefault_SampleRate 269 #define kDefault_SampleRate 48000.0 270 #endif 271 static Float64 gDevice_SampleRate = kDefault_SampleRate; 272 static Float64 gDevice_RequestedSampleRate = 0.0; 273 static UInt64 gDevice_IOIsRunning = 0; 274 static UInt64 gDevice2_IOIsRunning = 0; 275 static const UInt32 kDevice_RingBufferSize = 16384; 276 static Float64 gDevice_HostTicksPerFrame = 0.0; 277 static Float64 gDevice_AdjustedTicksPerFrame = 0.0; 278 static Float64 gDevice_PreviousTicks = 0.0; 279 static UInt64 gDevice_NumberTimeStamps = 0; 280 static Float64 gDevice_AnchorSampleTime = 0.0; 281 static UInt64 gDevice_AnchorHostTime = 0; 282 283 static bool gStream_Input_IsActive = true; 284 static bool gStream_Output_IsActive = true; 285 286 static const Float32 kVolume_MinDB = -64.0; 287 static const Float32 kVolume_MaxDB = 0.0; 288 static Float32 gVolume_Master_Value = 1.0; 289 static Float32 gPitch_Adjust = 0.5; 290 static bool gMute_Master_Value = false; 291 static UInt32 kClockSource_NumberItems = 2; 292 #define kClockSource_InternalFixed "Internal Fixed" 293 #define kClockSource_InternalAdjustable "Internal Adjustable" 294 static UInt32 gClockSource_Value = 0; 295 static bool gPitch_Adjust_Enabled = false; 296 297 static struct ObjectInfo kDevice_ObjectList[] = { 298 #if kDevice_HasInput 299 { kObjectID_Stream_Input, kObjectType_Stream, kAudioObjectPropertyScopeInput }, 300 { kObjectID_Volume_Input_Master, kObjectType_Control, kAudioObjectPropertyScopeInput }, 301 { kObjectID_Mute_Input_Master, kObjectType_Control, kAudioObjectPropertyScopeInput }, 302 #endif 303 #if kDevice_HasOutput 304 { kObjectID_Stream_Output, kObjectType_Stream, kAudioObjectPropertyScopeOutput }, 305 { kObjectID_Volume_Output_Master, kObjectType_Control, kAudioObjectPropertyScopeOutput }, 306 { kObjectID_Mute_Output_Master, kObjectType_Control, kAudioObjectPropertyScopeOutput }, 307 { kObjectID_Pitch_Adjust, kObjectType_Control, kAudioObjectPropertyScopeOutput }, 308 #endif 309 { kObjectID_ClockSource, kObjectType_Control, kAudioObjectPropertyScopeGlobal } 310 }; 311 312 static struct ObjectInfo kDevice2_ObjectList[] = { 313 #if kDevice2_HasInput 314 { kObjectID_Stream_Input, kObjectType_Stream, kAudioObjectPropertyScopeInput }, 315 { kObjectID_Volume_Input_Master, kObjectType_Control, kAudioObjectPropertyScopeInput }, 316 { kObjectID_Mute_Input_Master, kObjectType_Control, kAudioObjectPropertyScopeInput }, 317 #endif 318 #if kDevice2_HasOutput 319 { kObjectID_Stream_Output, kObjectType_Stream, kAudioObjectPropertyScopeOutput }, 320 { kObjectID_Volume_Output_Master, kObjectType_Control, kAudioObjectPropertyScopeOutput }, 321 { kObjectID_Mute_Output_Master, kObjectType_Control, kAudioObjectPropertyScopeOutput }, 322 #endif 323 }; 324 325 static const UInt32 kDevice_ObjectListSize = sizeof(kDevice_ObjectList) / sizeof(struct ObjectInfo); 326 static const UInt32 kDevice2_ObjectListSize = sizeof(kDevice2_ObjectList) / sizeof(struct ObjectInfo); 327 328 #ifndef kSampleRates 329 #define kSampleRates 8000, 16000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, 768000 330 #endif 331 332 static Float64 kDevice_SampleRates[] = { kSampleRates }; 333 334 static const UInt32 kDevice_SampleRatesSize = sizeof(kDevice_SampleRates) / sizeof(Float64); 335 336 337 338 #define kBits_Per_Channel 32 339 #define kBytes_Per_Channel (kBits_Per_Channel/ 8) 340 #define kBytes_Per_Frame (kNumber_Of_Channels * kBytes_Per_Channel) 341 #define kRing_Buffer_Frame_Size ((65536 + kLatency_Frame_Size)) 342 static Float32* gRingBuffer = NULL; 343 344 345 //================================================================================================== 346 #pragma mark - 347 #pragma mark AudioServerPlugInDriverInterface Implementation 348 //================================================================================================== 349 350 #pragma mark Prototypes 351 352 // Entry points for the COM methods 353 void* BlackHole_Create(CFAllocatorRef inAllocator, CFUUIDRef inRequestedTypeUUID); 354 static HRESULT BlackHole_QueryInterface(void* inDriver, REFIID inUUID, LPVOID* outInterface); 355 static ULONG BlackHole_AddRef(void* inDriver); 356 static ULONG BlackHole_Release(void* inDriver); 357 static OSStatus BlackHole_Initialize(AudioServerPlugInDriverRef inDriver, AudioServerPlugInHostRef inHost); 358 static OSStatus BlackHole_CreateDevice(AudioServerPlugInDriverRef inDriver, CFDictionaryRef inDescription, const AudioServerPlugInClientInfo* inClientInfo, AudioObjectID* outDeviceObjectID); 359 static OSStatus BlackHole_DestroyDevice(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID); 360 static OSStatus BlackHole_AddDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo); 361 static OSStatus BlackHole_RemoveDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo); 362 static OSStatus BlackHole_PerformDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo); 363 static OSStatus BlackHole_AbortDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo); 364 static Boolean BlackHole_HasProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); 365 static OSStatus BlackHole_IsPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); 366 static OSStatus BlackHole_GetPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); 367 static OSStatus BlackHole_GetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); 368 static OSStatus BlackHole_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); 369 static OSStatus BlackHole_StartIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID); 370 static OSStatus BlackHole_StopIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID); 371 static OSStatus BlackHole_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, Float64* outSampleTime, UInt64* outHostTime, UInt64* outSeed); 372 static OSStatus BlackHole_WillDoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, Boolean* outWillDo, Boolean* outWillDoInPlace); 373 static OSStatus BlackHole_BeginIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo); 374 static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, AudioObjectID inStreamObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo, void* ioMainBuffer, void* ioSecondaryBuffer); 375 static OSStatus BlackHole_EndIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo); 376 377 // Implementation 378 static Boolean BlackHole_HasPlugInProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); 379 static OSStatus BlackHole_IsPlugInPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); 380 static OSStatus BlackHole_GetPlugInPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); 381 static OSStatus BlackHole_GetPlugInPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); 382 static OSStatus BlackHole_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]); 383 384 static Boolean BlackHole_HasBoxProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); 385 static OSStatus BlackHole_IsBoxPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); 386 static OSStatus BlackHole_GetBoxPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); 387 static OSStatus BlackHole_GetBoxPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); 388 static OSStatus BlackHole_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]); 389 390 static Boolean BlackHole_HasDeviceProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); 391 static OSStatus BlackHole_IsDevicePropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); 392 static OSStatus BlackHole_GetDevicePropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); 393 static OSStatus BlackHole_GetDevicePropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); 394 static OSStatus BlackHole_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]); 395 396 static Boolean BlackHole_HasStreamProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); 397 static OSStatus BlackHole_IsStreamPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); 398 static OSStatus BlackHole_GetStreamPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); 399 static OSStatus BlackHole_GetStreamPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); 400 static OSStatus BlackHole_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]); 401 402 static Boolean BlackHole_HasControlProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress); 403 static OSStatus BlackHole_IsControlPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable); 404 static OSStatus BlackHole_GetControlPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); 405 static OSStatus BlackHole_GetControlPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData); 406 static OSStatus BlackHole_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]); 407 408 #pragma mark The Interface 409 410 static AudioServerPlugInDriverInterface gAudioServerPlugInDriverInterface = 411 { 412 NULL, 413 BlackHole_QueryInterface, 414 BlackHole_AddRef, 415 BlackHole_Release, 416 BlackHole_Initialize, 417 BlackHole_CreateDevice, 418 BlackHole_DestroyDevice, 419 BlackHole_AddDeviceClient, 420 BlackHole_RemoveDeviceClient, 421 BlackHole_PerformDeviceConfigurationChange, 422 BlackHole_AbortDeviceConfigurationChange, 423 BlackHole_HasProperty, 424 BlackHole_IsPropertySettable, 425 BlackHole_GetPropertyDataSize, 426 BlackHole_GetPropertyData, 427 BlackHole_SetPropertyData, 428 BlackHole_StartIO, 429 BlackHole_StopIO, 430 BlackHole_GetZeroTimeStamp, 431 BlackHole_WillDoIOOperation, 432 BlackHole_BeginIOOperation, 433 BlackHole_DoIOOperation, 434 BlackHole_EndIOOperation 435 }; 436 static AudioServerPlugInDriverInterface* gAudioServerPlugInDriverInterfacePtr = &gAudioServerPlugInDriverInterface; 437 static AudioServerPlugInDriverRef gAudioServerPlugInDriverRef = &gAudioServerPlugInDriverInterfacePtr; 438 439 440 #define RETURN_FORMATTED_STRING(_string_fmt) \ 441 if(kHas_Driver_Name_Format) \ 442 { \ 443 return CFStringCreateWithFormat(NULL, NULL, CFSTR(_string_fmt), kNumber_Of_Channels); \ 444 } \ 445 else \ 446 { \ 447 return CFStringCreateWithCString(NULL, _string_fmt, kCFStringEncodingUTF8); \ 448 } 449 450 static CFStringRef get_box_uid(void) { RETURN_FORMATTED_STRING(kBox_UID) } 451 static CFStringRef get_device_uid(void) { RETURN_FORMATTED_STRING(kDevice_UID) } 452 453 // Hydra: the device's display name comes from the manifest (set via the Hydra TUI) when 454 // present, so it can be renamed without rebuilding the driver. Read once and cached; on any 455 // failure we fall through to the compile-time default, so a missing/bad manifest is safe. 456 #include "../../hydra_cfg.h" 457 static CFStringRef get_device_name(void) { 458 static char nameBuf[256]; 459 static int loaded = 0; 460 if (!loaded) { 461 loaded = 1; 462 if (!hydra_cfg_device_name(nameBuf, (int)sizeof(nameBuf))) { 463 nameBuf[0] = '\0'; 464 } 465 } 466 if (nameBuf[0] != '\0') { 467 return CFStringCreateWithCString(NULL, nameBuf, kCFStringEncodingUTF8); 468 } 469 RETURN_FORMATTED_STRING(kDevice_Name) 470 } 471 static CFStringRef get_device2_uid(void) { RETURN_FORMATTED_STRING(kDevice2_UID) } 472 static CFStringRef get_device2_name(void) { RETURN_FORMATTED_STRING(kDevice2_Name) } 473 static CFStringRef get_device_model_uid(void) { RETURN_FORMATTED_STRING(kDevice_ModelUID) } 474 475 // Volume conversions 476 477 static Float32 volume_to_decibel(Float32 volume) 478 { 479 if (volume <= powf(10.0f, kVolume_MinDB / 20.0f)) 480 return kVolume_MinDB; 481 else 482 return 20.0f * log10f(volume); 483 } 484 485 static Float32 volume_from_decibel(Float32 decibel) 486 { 487 if (decibel <= kVolume_MinDB) 488 return 0.0f; 489 else 490 return powf(10.0f, decibel / 20.0f); 491 } 492 493 static Float32 volume_to_scalar(Float32 volume) 494 { 495 Float32 decibel = volume_to_decibel(volume); 496 return (decibel - kVolume_MinDB) / (kVolume_MaxDB - kVolume_MinDB); 497 } 498 499 static Float32 volume_from_scalar(Float32 scalar) 500 { 501 Float32 decibel = scalar * (kVolume_MaxDB - kVolume_MinDB) + kVolume_MinDB; 502 return volume_from_decibel(decibel); 503 } 504 505 static UInt32 device_object_list_size(AudioObjectPropertyScope scope, AudioObjectID objectID) { 506 507 switch (objectID) { 508 case kObjectID_Device: 509 { 510 if (scope == kAudioObjectPropertyScopeGlobal) 511 { 512 return kDevice_ObjectListSize; 513 } 514 515 UInt32 count = 0; 516 for (UInt32 i = 0; i < kDevice_ObjectListSize; i++) 517 { 518 count += (kDevice_ObjectList[i].scope == scope); 519 } 520 521 return count; 522 } 523 break; 524 525 case kObjectID_Device2: 526 { 527 if (scope == kAudioObjectPropertyScopeGlobal) 528 { 529 return kDevice2_ObjectListSize; 530 } 531 532 UInt32 count = 0; 533 for (UInt32 i = 0; i < kDevice2_ObjectListSize; i++) 534 { 535 count += (kDevice2_ObjectList[i].scope == scope); 536 } 537 538 return count; 539 } 540 break; 541 542 default: 543 return 0; 544 break; 545 } 546 } 547 548 static UInt32 device_stream_list_size(AudioObjectPropertyScope scope, AudioObjectID objectID) { 549 550 switch (objectID) { 551 case kObjectID_Device: 552 { 553 UInt32 count = 0; 554 for (UInt32 i = 0; i < kDevice_ObjectListSize; i++) 555 { 556 count += (kDevice_ObjectList[i].type == kObjectType_Stream && (kDevice_ObjectList[i].scope == scope || scope == kAudioObjectPropertyScopeGlobal)); 557 } 558 559 return count; 560 } 561 break; 562 563 case kObjectID_Device2: 564 { 565 UInt32 count = 0; 566 for (UInt32 i = 0; i < kDevice2_ObjectListSize; i++) 567 { 568 count += (kDevice2_ObjectList[i].type == kObjectType_Stream && (kDevice2_ObjectList[i].scope == scope || scope == kAudioObjectPropertyScopeGlobal)); 569 } 570 571 return count; 572 } 573 break; 574 575 default: 576 return 0; 577 break; 578 } 579 580 581 } 582 583 static UInt32 device_control_list_size(AudioObjectPropertyScope scope, AudioObjectID objectID) { 584 585 switch (objectID) { 586 case kObjectID_Device: 587 { 588 589 UInt32 count = 0; 590 for (UInt32 i = 0; i < kDevice_ObjectListSize; i++) 591 { 592 count += (kDevice_ObjectList[i].type == kObjectType_Control && (kDevice_ObjectList[i].scope == scope || scope == kAudioObjectPropertyScopeGlobal)); 593 } 594 595 return count; 596 } 597 break; 598 case kObjectID_Device2: 599 { 600 601 UInt32 count = 0; 602 for (UInt32 i = 0; i < kDevice2_ObjectListSize; i++) 603 { 604 count += (kDevice2_ObjectList[i].type == kObjectType_Control && (kDevice2_ObjectList[i].scope == scope || scope == kAudioObjectPropertyScopeGlobal)); 605 } 606 607 return count; 608 } 609 break; 610 611 default: 612 return 0; 613 break; 614 } 615 616 } 617 618 static UInt32 minimum(UInt32 a, UInt32 b) { 619 return a < b ? a : b; 620 } 621 622 static bool is_valid_sample_rate(Float64 sample_rate) 623 { 624 for(UInt32 i = 0; i < kDevice_SampleRatesSize; i++) 625 { 626 if (sample_rate == kDevice_SampleRates[i]) 627 { 628 return true; 629 } 630 } 631 632 return false; 633 } 634 635 #pragma mark Factory 636 637 void* BlackHole_Create(CFAllocatorRef inAllocator, CFUUIDRef inRequestedTypeUUID) 638 { 639 // This is the CFPlugIn factory function. Its job is to create the implementation for the given 640 // type provided that the type is supported. Because this driver is simple and all its 641 // initialization is handled via static initialization when the bundle is loaded, all that 642 // needs to be done is to return the AudioServerPlugInDriverRef that points to the driver's 643 // interface. A more complicated driver would create any base line objects it needs to satisfy 644 // the IUnknown methods that are used to discover that actual interface to talk to the driver. 645 // The majority of the driver's initialization should be handled in the Initialize() method of 646 // the driver's AudioServerPlugInDriverInterface. 647 648 #pragma unused(inAllocator) 649 void* theAnswer = NULL; 650 if(CFEqual(inRequestedTypeUUID, kAudioServerPlugInTypeUUID)) 651 { 652 theAnswer = gAudioServerPlugInDriverRef; 653 } 654 return theAnswer; 655 } 656 657 #pragma mark Inheritance 658 659 static HRESULT BlackHole_QueryInterface(void* inDriver, REFIID inUUID, LPVOID* outInterface) 660 { 661 // This function is called by the HAL to get the interface to talk to the plug-in through. 662 // AudioServerPlugIns are required to support the IUnknown interface and the 663 // AudioServerPlugInDriverInterface. As it happens, all interfaces must also provide the 664 // IUnknown interface, so we can always just return the single interface we made with 665 // gAudioServerPlugInDriverInterfacePtr regardless of which one is asked for. 666 667 // declare the local variables 668 HRESULT theAnswer = 0; 669 CFUUIDRef theRequestedUUID = NULL; 670 671 // validate the arguments 672 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_QueryInterface: bad driver reference"); 673 FailWithAction(outInterface == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_QueryInterface: no place to store the returned interface"); 674 675 // make a CFUUIDRef from inUUID 676 theRequestedUUID = CFUUIDCreateFromUUIDBytes(NULL, inUUID); 677 FailWithAction(theRequestedUUID == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_QueryInterface: failed to create the CFUUIDRef"); 678 679 // AudioServerPlugIns only support two interfaces, IUnknown (which has to be supported by all 680 // CFPlugIns and AudioServerPlugInDriverInterface (which is the actual interface the HAL will 681 // use). 682 if(CFEqual(theRequestedUUID, IUnknownUUID) || CFEqual(theRequestedUUID, kAudioServerPlugInDriverInterfaceUUID)) 683 { 684 pthread_mutex_lock(&gPlugIn_StateMutex); 685 ++gPlugIn_RefCount; 686 pthread_mutex_unlock(&gPlugIn_StateMutex); 687 *outInterface = gAudioServerPlugInDriverRef; 688 } 689 else 690 { 691 theAnswer = E_NOINTERFACE; 692 } 693 694 // make sure to release the UUID we created 695 CFRelease(theRequestedUUID); 696 697 Done: 698 return theAnswer; 699 } 700 701 static ULONG BlackHole_AddRef(void* inDriver) 702 { 703 // This call returns the resulting reference count after the increment. 704 705 // declare the local variables 706 ULONG theAnswer = 0; 707 708 // check the arguments 709 FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "BlackHole_AddRef: bad driver reference"); 710 711 // increment the refcount 712 pthread_mutex_lock(&gPlugIn_StateMutex); 713 if(gPlugIn_RefCount < UINT32_MAX) 714 { 715 ++gPlugIn_RefCount; 716 } 717 theAnswer = gPlugIn_RefCount; 718 pthread_mutex_unlock(&gPlugIn_StateMutex); 719 720 Done: 721 return theAnswer; 722 } 723 724 static ULONG BlackHole_Release(void* inDriver) 725 { 726 // This call returns the resulting reference count after the decrement. 727 728 // declare the local variables 729 ULONG theAnswer = 0; 730 731 // check the arguments 732 FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "BlackHole_Release: bad driver reference"); 733 734 // decrement the refcount 735 pthread_mutex_lock(&gPlugIn_StateMutex); 736 if(gPlugIn_RefCount > 0) 737 { 738 --gPlugIn_RefCount; 739 // Note that we don't do anything special if the refcount goes to zero as the HAL 740 // will never fully release a plug-in it opens. We keep managing the refcount so that 741 // the API semantics are correct though. 742 } 743 theAnswer = gPlugIn_RefCount; 744 pthread_mutex_unlock(&gPlugIn_StateMutex); 745 746 Done: 747 return theAnswer; 748 } 749 750 #pragma mark Basic Operations 751 752 static OSStatus BlackHole_Initialize(AudioServerPlugInDriverRef inDriver, AudioServerPlugInHostRef inHost) 753 { 754 // The job of this method is, as the name implies, to get the driver initialized. One specific 755 // thing that needs to be done is to store the AudioServerPlugInHostRef so that it can be used 756 // later. Note that when this call returns, the HAL will scan the various lists the driver 757 // maintains (such as the device list) to get the initial set of objects the driver is 758 // publishing. So, there is no need to notify the HAL about any objects created as part of the 759 // execution of this method. 760 761 // declare the local variables 762 OSStatus theAnswer = 0; 763 764 // check the arguments 765 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_Initialize: bad driver reference"); 766 767 // store the AudioServerPlugInHostRef 768 gPlugIn_Host = inHost; 769 770 // initialize the box acquired property from the settings 771 CFPropertyListRef theSettingsData = NULL; 772 gPlugIn_Host->CopyFromStorage(gPlugIn_Host, CFSTR("box acquired"), &theSettingsData); 773 if(theSettingsData != NULL) 774 { 775 if(CFGetTypeID(theSettingsData) == CFBooleanGetTypeID()) 776 { 777 gBox_Acquired = CFBooleanGetValue((CFBooleanRef)theSettingsData); 778 } 779 else if(CFGetTypeID(theSettingsData) == CFNumberGetTypeID()) 780 { 781 SInt32 theValue = 0; 782 CFNumberGetValue((CFNumberRef)theSettingsData, kCFNumberSInt32Type, &theValue); 783 gBox_Acquired = theValue ? 1 : 0; 784 } 785 CFRelease(theSettingsData); 786 } 787 788 // initialize the box name from the settings 789 gPlugIn_Host->CopyFromStorage(gPlugIn_Host, CFSTR("box acquired"), &theSettingsData); 790 if(theSettingsData != NULL) 791 { 792 if(CFGetTypeID(theSettingsData) == CFStringGetTypeID()) 793 { 794 gBox_Name = (CFStringRef)theSettingsData; 795 CFRetain(gBox_Name); 796 } 797 CFRelease(theSettingsData); 798 } 799 800 // set the box name directly as a last resort 801 if(gBox_Name == NULL) 802 { 803 gBox_Name = CFSTR("BlackHole Box"); 804 } 805 806 // calculate the host ticks per frame 807 struct mach_timebase_info theTimeBaseInfo; 808 mach_timebase_info(&theTimeBaseInfo); 809 Float64 theHostClockFrequency = (Float64)theTimeBaseInfo.denom / (Float64)theTimeBaseInfo.numer; 810 theHostClockFrequency *= 1000000000.0; 811 gDevice_HostTicksPerFrame = theHostClockFrequency / gDevice_SampleRate; 812 gDevice_AdjustedTicksPerFrame = gDevice_HostTicksPerFrame - gDevice_HostTicksPerFrame/100.0 * 2.0*(gPitch_Adjust - 0.5); 813 814 // DebugMsg("BlackHole theTimeBaseInfo.numer: %u \t theTimeBaseInfo.denom: %u", theTimeBaseInfo.numer, theTimeBaseInfo.denom); 815 816 Done: 817 return theAnswer; 818 } 819 820 static OSStatus BlackHole_CreateDevice(AudioServerPlugInDriverRef inDriver, CFDictionaryRef inDescription, const AudioServerPlugInClientInfo* inClientInfo, AudioObjectID* outDeviceObjectID) 821 { 822 // This method is used to tell a driver that implements the Transport Manager semantics to 823 // create an AudioEndpointDevice from a set of AudioEndpoints. Since this driver is not a 824 // Transport Manager, we just check the arguments and return 825 // kAudioHardwareUnsupportedOperationError. 826 827 #pragma unused(inDescription, inClientInfo, outDeviceObjectID) 828 829 // declare the local variables 830 OSStatus theAnswer = kAudioHardwareUnsupportedOperationError; 831 832 // check the arguments 833 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_CreateDevice: bad driver reference"); 834 835 Done: 836 return theAnswer; 837 } 838 839 static OSStatus BlackHole_DestroyDevice(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID) 840 { 841 // This method is used to tell a driver that implements the Transport Manager semantics to 842 // destroy an AudioEndpointDevice. Since this driver is not a Transport Manager, we just check 843 // the arguments and return kAudioHardwareUnsupportedOperationError. 844 845 #pragma unused(inDeviceObjectID) 846 847 // declare the local variables 848 OSStatus theAnswer = kAudioHardwareUnsupportedOperationError; 849 850 // check the arguments 851 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_DestroyDevice: bad driver reference"); 852 853 Done: 854 return theAnswer; 855 } 856 857 static OSStatus BlackHole_AddDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo) 858 { 859 // This method is used to inform the driver about a new client that is using the given device. 860 // This allows the device to act differently depending on who the client is. This driver does 861 // not need to track the clients using the device, so we just check the arguments and return 862 // successfully. 863 864 #pragma unused(inClientInfo) 865 866 // declare the local variables 867 OSStatus theAnswer = 0; 868 869 // check the arguments 870 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_AddDeviceClient: bad driver reference"); 871 FailWithAction(inDeviceObjectID != kObjectID_Device && inDeviceObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_AddDeviceClient: bad device ID"); 872 873 Done: 874 return theAnswer; 875 } 876 877 static OSStatus BlackHole_RemoveDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo) 878 { 879 // This method is used to inform the driver about a client that is no longer using the given 880 // device. This driver does not track clients, so we just check the arguments and return 881 // successfully. 882 883 #pragma unused(inClientInfo) 884 885 // declare the local variables 886 OSStatus theAnswer = 0; 887 888 // check the arguments 889 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_RemoveDeviceClient: bad driver reference"); 890 FailWithAction(inDeviceObjectID != kObjectID_Device && inDeviceObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_RemoveDeviceClient: bad device ID"); 891 892 Done: 893 return theAnswer; 894 } 895 896 static OSStatus BlackHole_PerformDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo) 897 { 898 // This method is called to tell the device that it can perform the configuration change that it 899 // had requested via a call to the host method, RequestDeviceConfigurationChange(). The 900 // arguments, inChangeAction and inChangeInfo are the same as what was passed to 901 // RequestDeviceConfigurationChange(). 902 // 903 // The HAL guarantees that IO will be stopped while this method is in progress. The HAL will 904 // also handle figuring out exactly what changed for the non-control related properties. This 905 // means that the only notifications that would need to be sent here would be for either 906 // custom properties the HAL doesn't know about or for controls. 907 // 908 // For the device implemented by this driver, sample rate changes and enabling/disabling 909 // the pitch adjust go through this process. 910 // These are the only states that can be changed for the device that aren't controls. 911 // Which change is requested is passed in the inChangeAction argument. 912 913 #pragma unused(inChangeInfo) 914 915 // declare the local variables 916 OSStatus theAnswer = 0; 917 Float64 newSampleRate = 0.0; 918 919 // check the arguments 920 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_PerformDeviceConfigurationChange: bad driver reference"); 921 FailWithAction(inDeviceObjectID != kObjectID_Device && inDeviceObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_PerformDeviceConfigurationChange: bad device ID"); 922 switch(inChangeAction) 923 { 924 case ChangeAction_EnablePitchControl: 925 pthread_mutex_lock(&gPlugIn_StateMutex); 926 gPitch_Adjust_Enabled = true; 927 pthread_mutex_unlock(&gPlugIn_StateMutex); 928 break; 929 case ChangeAction_DisablePitchControl: 930 pthread_mutex_lock(&gPlugIn_StateMutex); 931 gPitch_Adjust_Enabled = false; 932 pthread_mutex_unlock(&gPlugIn_StateMutex); 933 break; 934 case ChangeAction_SetSampleRate: 935 pthread_mutex_lock(&gPlugIn_StateMutex); 936 newSampleRate = gDevice_RequestedSampleRate; 937 pthread_mutex_unlock(&gPlugIn_StateMutex); 938 FailWithAction(!is_valid_sample_rate(newSampleRate), theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_PerformDeviceConfigurationChange: bad sample rate"); 939 940 // lock the state mutex 941 pthread_mutex_lock(&gPlugIn_StateMutex); 942 943 // change the sample rate 944 gDevice_SampleRate = newSampleRate; 945 946 // recalculate the state that depends on the sample rate 947 struct mach_timebase_info theTimeBaseInfo; 948 mach_timebase_info(&theTimeBaseInfo); 949 Float64 theHostClockFrequency = (Float64)theTimeBaseInfo.denom / (Float64)theTimeBaseInfo.numer; 950 theHostClockFrequency *= 1000000000.0; 951 gDevice_HostTicksPerFrame = theHostClockFrequency / gDevice_SampleRate; 952 gDevice_AdjustedTicksPerFrame = gDevice_HostTicksPerFrame - gDevice_HostTicksPerFrame/100.0 * 2.0*(gPitch_Adjust - 0.5); 953 954 // unlock the state mutex 955 pthread_mutex_unlock(&gPlugIn_StateMutex); 956 957 // DebugMsg("BlackHole theTimeBaseInfo.numer: %u \t theTimeBaseInfo.denom: %u", theTimeBaseInfo.numer, theTimeBaseInfo.denom); 958 break; 959 }; 960 961 Done: 962 return theAnswer; 963 } 964 965 static OSStatus BlackHole_AbortDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo) 966 { 967 // This method is called to tell the driver that a request for a config change has been denied. 968 // This provides the driver an opportunity to clean up any state associated with the request. 969 // For this driver, an aborted config change requires no action. So we just check the arguments 970 // and return 971 972 #pragma unused(inChangeAction, inChangeInfo) 973 974 // declare the local variables 975 OSStatus theAnswer = 0; 976 977 // check the arguments 978 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_PerformDeviceConfigurationChange: bad driver reference"); 979 FailWithAction(inDeviceObjectID != kObjectID_Device && inDeviceObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_PerformDeviceConfigurationChange: bad device ID"); 980 981 Done: 982 return theAnswer; 983 } 984 985 #pragma mark Property Operations 986 987 static Boolean BlackHole_HasProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) 988 { 989 // This method returns whether or not the given object has the given property. 990 991 // declare the local variables 992 Boolean theAnswer = false; 993 994 // check the arguments 995 FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "BlackHole_HasProperty: bad driver reference"); 996 FailIf(inAddress == NULL, Done, "BlackHole_HasProperty: no address"); 997 998 // Note that for each object, this driver implements all the required properties plus a few 999 // extras that are useful but not required. There is more detailed commentary about each 1000 // property in the BlackHole_GetPropertyData() method. 1001 switch(inObjectID) 1002 { 1003 case kObjectID_PlugIn: 1004 theAnswer = BlackHole_HasPlugInProperty(inDriver, inObjectID, inClientProcessID, inAddress); 1005 break; 1006 1007 case kObjectID_Box: 1008 theAnswer = BlackHole_HasBoxProperty(inDriver, inObjectID, inClientProcessID, inAddress); 1009 break; 1010 1011 case kObjectID_Device: 1012 case kObjectID_Device2: 1013 theAnswer = BlackHole_HasDeviceProperty(inDriver, inObjectID, inClientProcessID, inAddress); 1014 break; 1015 1016 case kObjectID_Stream_Input: 1017 case kObjectID_Stream_Output: 1018 theAnswer = BlackHole_HasStreamProperty(inDriver, inObjectID, inClientProcessID, inAddress); 1019 break; 1020 1021 case kObjectID_Volume_Output_Master: 1022 case kObjectID_Mute_Output_Master: 1023 case kObjectID_Volume_Input_Master: 1024 case kObjectID_Mute_Input_Master: 1025 case kObjectID_Pitch_Adjust: 1026 case kObjectID_ClockSource: 1027 theAnswer = BlackHole_HasControlProperty(inDriver, inObjectID, inClientProcessID, inAddress); 1028 break; 1029 }; 1030 1031 Done: 1032 return theAnswer; 1033 } 1034 1035 static OSStatus BlackHole_IsPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) 1036 { 1037 // This method returns whether or not the given property on the object can have its value 1038 // changed. 1039 1040 // declare the local variables 1041 OSStatus theAnswer = 0; 1042 1043 // check the arguments 1044 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_IsPropertySettable: bad driver reference"); 1045 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsPropertySettable: no address"); 1046 FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsPropertySettable: no place to put the return value"); 1047 1048 // Note that for each object, this driver implements all the required properties plus a few 1049 // extras that are useful but not required. There is more detailed commentary about each 1050 // property in the BlackHole_GetPropertyData() method. 1051 switch(inObjectID) 1052 { 1053 case kObjectID_PlugIn: 1054 theAnswer = BlackHole_IsPlugInPropertySettable(inDriver, inObjectID, inClientProcessID, inAddress, outIsSettable); 1055 break; 1056 1057 case kObjectID_Box: 1058 theAnswer = BlackHole_IsBoxPropertySettable(inDriver, inObjectID, inClientProcessID, inAddress, outIsSettable); 1059 break; 1060 1061 case kObjectID_Device: 1062 case kObjectID_Device2: 1063 theAnswer = BlackHole_IsDevicePropertySettable(inDriver, inObjectID, inClientProcessID, inAddress, outIsSettable); 1064 break; 1065 1066 case kObjectID_Stream_Input: 1067 case kObjectID_Stream_Output: 1068 theAnswer = BlackHole_IsStreamPropertySettable(inDriver, inObjectID, inClientProcessID, inAddress, outIsSettable); 1069 break; 1070 1071 case kObjectID_Volume_Output_Master: 1072 case kObjectID_Mute_Output_Master: 1073 case kObjectID_Volume_Input_Master: 1074 case kObjectID_Mute_Input_Master: 1075 case kObjectID_Pitch_Adjust: 1076 case kObjectID_ClockSource: 1077 theAnswer = BlackHole_IsControlPropertySettable(inDriver, inObjectID, inClientProcessID, inAddress, outIsSettable); 1078 break; 1079 1080 default: 1081 theAnswer = kAudioHardwareBadObjectError; 1082 break; 1083 }; 1084 1085 Done: 1086 return theAnswer; 1087 } 1088 1089 static OSStatus BlackHole_GetPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) 1090 { 1091 // This method returns the byte size of the property's data. 1092 1093 // declare the local variables 1094 OSStatus theAnswer = 0; 1095 1096 // check the arguments 1097 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetPropertyDataSize: bad driver reference"); 1098 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetPropertyDataSize: no address"); 1099 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetPropertyDataSize: no place to put the return value"); 1100 1101 // Note that for each object, this driver implements all the required properties plus a few 1102 // extras that are useful but not required. There is more detailed commentary about each 1103 // property in the BlackHole_GetPropertyData() method. 1104 switch(inObjectID) 1105 { 1106 case kObjectID_PlugIn: 1107 theAnswer = BlackHole_GetPlugInPropertyDataSize(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, outDataSize); 1108 break; 1109 1110 case kObjectID_Box: 1111 theAnswer = BlackHole_GetBoxPropertyDataSize(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, outDataSize); 1112 break; 1113 1114 case kObjectID_Device: 1115 case kObjectID_Device2: 1116 theAnswer = BlackHole_GetDevicePropertyDataSize(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, outDataSize); 1117 break; 1118 1119 case kObjectID_Stream_Input: 1120 case kObjectID_Stream_Output: 1121 theAnswer = BlackHole_GetStreamPropertyDataSize(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, outDataSize); 1122 break; 1123 1124 case kObjectID_Volume_Output_Master: 1125 case kObjectID_Mute_Output_Master: 1126 case kObjectID_Volume_Input_Master: 1127 case kObjectID_Mute_Input_Master: 1128 case kObjectID_Pitch_Adjust: 1129 case kObjectID_ClockSource: 1130 theAnswer = BlackHole_GetControlPropertyDataSize(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, outDataSize); 1131 break; 1132 1133 default: 1134 theAnswer = kAudioHardwareBadObjectError; 1135 break; 1136 }; 1137 1138 Done: 1139 return theAnswer; 1140 } 1141 1142 static OSStatus BlackHole_GetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) 1143 { 1144 // declare the local variables 1145 OSStatus theAnswer = 0; 1146 1147 // check the arguments 1148 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetPropertyData: bad driver reference"); 1149 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetPropertyData: no address"); 1150 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetPropertyData: no place to put the return value size"); 1151 FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetPropertyData: no place to put the return value"); 1152 1153 // Note that for each object, this driver implements all the required properties plus a few 1154 // extras that are useful but not required. 1155 // 1156 // Also, since most of the data that will get returned is static, there are few instances where 1157 // it is necessary to lock the state mutex. 1158 switch(inObjectID) 1159 { 1160 case kObjectID_PlugIn: 1161 theAnswer = BlackHole_GetPlugInPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); 1162 break; 1163 1164 case kObjectID_Box: 1165 theAnswer = BlackHole_GetBoxPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); 1166 break; 1167 1168 case kObjectID_Device: 1169 case kObjectID_Device2: 1170 theAnswer = BlackHole_GetDevicePropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); 1171 break; 1172 1173 case kObjectID_Stream_Input: 1174 case kObjectID_Stream_Output: 1175 theAnswer = BlackHole_GetStreamPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); 1176 break; 1177 1178 case kObjectID_Volume_Output_Master: 1179 case kObjectID_Mute_Output_Master: 1180 case kObjectID_Volume_Input_Master: 1181 case kObjectID_Mute_Input_Master: 1182 case kObjectID_Pitch_Adjust: 1183 case kObjectID_ClockSource: 1184 theAnswer = BlackHole_GetControlPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); 1185 break; 1186 1187 default: 1188 theAnswer = kAudioHardwareBadObjectError; 1189 break; 1190 }; 1191 1192 Done: 1193 return theAnswer; 1194 } 1195 1196 static OSStatus BlackHole_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData) 1197 { 1198 // declare the local variables 1199 OSStatus theAnswer = 0; 1200 UInt32 theNumberPropertiesChanged = 0; 1201 AudioObjectPropertyAddress theChangedAddresses[2]; 1202 1203 // check the arguments 1204 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_SetPropertyData: bad driver reference"); 1205 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetPropertyData: no address"); 1206 1207 // Note that for each object, this driver implements all the required properties plus a few 1208 // extras that are useful but not required. There is more detailed commentary about each 1209 // property in the BlackHole_GetPropertyData() method. 1210 switch(inObjectID) 1211 { 1212 case kObjectID_PlugIn: 1213 theAnswer = BlackHole_SetPlugInPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData, &theNumberPropertiesChanged, theChangedAddresses); 1214 break; 1215 1216 case kObjectID_Box: 1217 theAnswer = BlackHole_SetBoxPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData, &theNumberPropertiesChanged, theChangedAddresses); 1218 break; 1219 1220 case kObjectID_Device: 1221 case kObjectID_Device2: 1222 theAnswer = BlackHole_SetDevicePropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData, &theNumberPropertiesChanged, theChangedAddresses); 1223 break; 1224 1225 case kObjectID_Stream_Input: 1226 case kObjectID_Stream_Output: 1227 theAnswer = BlackHole_SetStreamPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData, &theNumberPropertiesChanged, theChangedAddresses); 1228 break; 1229 1230 case kObjectID_Volume_Output_Master: 1231 case kObjectID_Mute_Output_Master: 1232 case kObjectID_Volume_Input_Master: 1233 case kObjectID_Mute_Input_Master: 1234 case kObjectID_Pitch_Adjust: 1235 case kObjectID_ClockSource: 1236 theAnswer = BlackHole_SetControlPropertyData(inDriver, inObjectID, inClientProcessID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData, &theNumberPropertiesChanged, theChangedAddresses); 1237 break; 1238 1239 default: 1240 theAnswer = kAudioHardwareBadObjectError; 1241 break; 1242 }; 1243 1244 // send any notifications 1245 if(theNumberPropertiesChanged > 0) 1246 { 1247 gPlugIn_Host->PropertiesChanged(gPlugIn_Host, inObjectID, theNumberPropertiesChanged, theChangedAddresses); 1248 } 1249 1250 Done: 1251 return theAnswer; 1252 } 1253 1254 #pragma mark PlugIn Property Operations 1255 1256 static Boolean BlackHole_HasPlugInProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) 1257 { 1258 // This method returns whether or not the plug-in object has the given property. 1259 1260 #pragma unused(inClientProcessID) 1261 1262 // declare the local variables 1263 Boolean theAnswer = false; 1264 1265 // check the arguments 1266 FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "BlackHole_HasPlugInProperty: bad driver reference"); 1267 FailIf(inAddress == NULL, Done, "BlackHole_HasPlugInProperty: no address"); 1268 FailIf(inObjectID != kObjectID_PlugIn, Done, "BlackHole_HasPlugInProperty: not the plug-in object"); 1269 1270 1271 // Note that for each object, this driver implements all the required properties plus a few 1272 // extras that are useful but not required. There is more detailed commentary about each 1273 // property in the BlackHole_GetPlugInPropertyData() method. 1274 switch(inAddress->mSelector) 1275 { 1276 case kAudioObjectPropertyBaseClass: 1277 case kAudioObjectPropertyClass: 1278 case kAudioObjectPropertyOwner: 1279 case kAudioObjectPropertyManufacturer: 1280 case kAudioObjectPropertyOwnedObjects: 1281 case kAudioPlugInPropertyBoxList: 1282 case kAudioPlugInPropertyTranslateUIDToBox: 1283 case kAudioPlugInPropertyDeviceList: 1284 case kAudioPlugInPropertyTranslateUIDToDevice: 1285 case kAudioPlugInPropertyResourceBundle: 1286 theAnswer = true; 1287 break; 1288 }; 1289 1290 Done: 1291 return theAnswer; 1292 } 1293 1294 static OSStatus BlackHole_IsPlugInPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) 1295 { 1296 // This method returns whether or not the given property on the plug-in object can have its 1297 // value changed. 1298 1299 #pragma unused(inClientProcessID) 1300 1301 // declare the local variables 1302 OSStatus theAnswer = 0; 1303 1304 // check the arguments 1305 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_IsPlugInPropertySettable: bad driver reference"); 1306 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsPlugInPropertySettable: no address"); 1307 FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsPlugInPropertySettable: no place to put the return value"); 1308 FailWithAction(inObjectID != kObjectID_PlugIn, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_IsPlugInPropertySettable: not the plug-in object"); 1309 1310 // Note that for each object, this driver implements all the required properties plus a few 1311 // extras that are useful but not required. There is more detailed commentary about each 1312 // property in the BlackHole_GetPlugInPropertyData() method. 1313 switch(inAddress->mSelector) 1314 { 1315 case kAudioObjectPropertyBaseClass: 1316 case kAudioObjectPropertyClass: 1317 case kAudioObjectPropertyOwner: 1318 case kAudioObjectPropertyManufacturer: 1319 case kAudioObjectPropertyOwnedObjects: 1320 case kAudioPlugInPropertyBoxList: 1321 case kAudioPlugInPropertyTranslateUIDToBox: 1322 case kAudioPlugInPropertyDeviceList: 1323 case kAudioPlugInPropertyTranslateUIDToDevice: 1324 case kAudioPlugInPropertyResourceBundle: 1325 *outIsSettable = false; 1326 break; 1327 1328 default: 1329 theAnswer = kAudioHardwareUnknownPropertyError; 1330 break; 1331 }; 1332 1333 Done: 1334 return theAnswer; 1335 } 1336 1337 static OSStatus BlackHole_GetPlugInPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) 1338 { 1339 // This method returns the byte size of the property's data. 1340 1341 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) 1342 1343 // declare the local variables 1344 OSStatus theAnswer = 0; 1345 1346 // check the arguments 1347 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetPlugInPropertyDataSize: bad driver reference"); 1348 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetPlugInPropertyDataSize: no address"); 1349 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetPlugInPropertyDataSize: no place to put the return value"); 1350 FailWithAction(inObjectID != kObjectID_PlugIn, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetPlugInPropertyDataSize: not the plug-in object"); 1351 1352 // Note that for each object, this driver implements all the required properties plus a few 1353 // extras that are useful but not required. There is more detailed commentary about each 1354 // property in the BlackHole_GetPlugInPropertyData() method. 1355 switch(inAddress->mSelector) 1356 { 1357 case kAudioObjectPropertyBaseClass: 1358 *outDataSize = sizeof(AudioClassID); 1359 break; 1360 1361 case kAudioObjectPropertyClass: 1362 *outDataSize = sizeof(AudioClassID); 1363 break; 1364 1365 case kAudioObjectPropertyOwner: 1366 *outDataSize = sizeof(AudioObjectID); 1367 break; 1368 1369 case kAudioObjectPropertyManufacturer: 1370 *outDataSize = sizeof(CFStringRef); 1371 break; 1372 1373 case kAudioObjectPropertyOwnedObjects: 1374 if(gBox_Acquired) 1375 { 1376 *outDataSize = 2 * sizeof(AudioClassID); 1377 } 1378 else 1379 { 1380 *outDataSize = sizeof(AudioClassID); 1381 } 1382 break; 1383 1384 case kAudioPlugInPropertyBoxList: 1385 *outDataSize = sizeof(AudioClassID); 1386 break; 1387 1388 case kAudioPlugInPropertyTranslateUIDToBox: 1389 *outDataSize = sizeof(AudioObjectID); 1390 break; 1391 1392 case kAudioPlugInPropertyDeviceList: 1393 if(gBox_Acquired) 1394 { 1395 *outDataSize = sizeof(AudioClassID)*2; 1396 } 1397 else 1398 { 1399 *outDataSize = 0; 1400 } 1401 break; 1402 1403 case kAudioPlugInPropertyTranslateUIDToDevice: 1404 *outDataSize = sizeof(AudioObjectID); 1405 break; 1406 1407 case kAudioPlugInPropertyResourceBundle: 1408 *outDataSize = sizeof(CFStringRef); 1409 break; 1410 1411 default: 1412 theAnswer = kAudioHardwareUnknownPropertyError; 1413 break; 1414 }; 1415 1416 Done: 1417 return theAnswer; 1418 } 1419 1420 static OSStatus BlackHole_GetPlugInPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) 1421 { 1422 #pragma unused(inClientProcessID) 1423 1424 // declare the local variables 1425 OSStatus theAnswer = 0; 1426 UInt32 theNumberItemsToFetch; 1427 1428 // check the arguments 1429 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetPlugInPropertyData: bad driver reference"); 1430 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetPlugInPropertyData: no address"); 1431 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetPlugInPropertyData: no place to put the return value size"); 1432 FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetPlugInPropertyData: no place to put the return value"); 1433 FailWithAction(inObjectID != kObjectID_PlugIn, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetPlugInPropertyData: not the plug-in object"); 1434 1435 // Note that for each object, this driver implements all the required properties plus a few 1436 // extras that are useful but not required. 1437 // 1438 // Also, since most of the data that will get returned is static, there are few instances where 1439 // it is necessary to lock the state mutex. 1440 switch(inAddress->mSelector) 1441 { 1442 case kAudioObjectPropertyBaseClass: 1443 // The base class for kAudioPlugInClassID is kAudioObjectClassID 1444 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the plug-in"); 1445 *((AudioClassID*)outData) = kAudioObjectClassID; 1446 *outDataSize = sizeof(AudioClassID); 1447 break; 1448 1449 case kAudioObjectPropertyClass: 1450 // The class is always kAudioPlugInClassID for regular drivers 1451 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the plug-in"); 1452 *((AudioClassID*)outData) = kAudioPlugInClassID; 1453 *outDataSize = sizeof(AudioClassID); 1454 break; 1455 1456 case kAudioObjectPropertyOwner: 1457 // The plug-in doesn't have an owning object 1458 FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the plug-in"); 1459 *((AudioObjectID*)outData) = kAudioObjectUnknown; 1460 *outDataSize = sizeof(AudioObjectID); 1461 break; 1462 1463 case kAudioObjectPropertyManufacturer: 1464 // This is the human readable name of the maker of the plug-in. 1465 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the plug-in"); 1466 *((CFStringRef*)outData) = CFSTR("Apple Inc."); 1467 *outDataSize = sizeof(CFStringRef); 1468 break; 1469 1470 case kAudioObjectPropertyOwnedObjects: 1471 // Calculate the number of items that have been requested. Note that this 1472 // number is allowed to be smaller than the actual size of the list. In such 1473 // case, only that number of items will be returned 1474 theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); 1475 1476 // Clamp that to the number of boxes this driver implements (which is just 1) 1477 if(theNumberItemsToFetch > (gBox_Acquired ? 2 : 1)) 1478 { 1479 theNumberItemsToFetch = (gBox_Acquired ? 2 : 1); 1480 } 1481 1482 // Write the devices' object IDs into the return value 1483 if(theNumberItemsToFetch > 1) 1484 { 1485 ((AudioObjectID*)outData)[0] = kObjectID_Box; 1486 ((AudioObjectID*)outData)[0] = kObjectID_Device; 1487 } 1488 else if(theNumberItemsToFetch > 0) 1489 { 1490 ((AudioObjectID*)outData)[0] = kObjectID_Box; 1491 } 1492 1493 // Return how many bytes we wrote to 1494 *outDataSize = theNumberItemsToFetch * sizeof(AudioClassID); 1495 break; 1496 1497 case kAudioPlugInPropertyBoxList: 1498 // Calculate the number of items that have been requested. Note that this 1499 // number is allowed to be smaller than the actual size of the list. In such 1500 // case, only that number of items will be returned 1501 theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); 1502 1503 // Clamp that to the number of boxes this driver implements (which is just 1) 1504 if(theNumberItemsToFetch > 1) 1505 { 1506 theNumberItemsToFetch = 1; 1507 } 1508 1509 // Write the devices' object IDs into the return value 1510 if(theNumberItemsToFetch > 0) 1511 { 1512 ((AudioObjectID*)outData)[0] = kObjectID_Box; 1513 } 1514 1515 // Return how many bytes we wrote to 1516 *outDataSize = theNumberItemsToFetch * sizeof(AudioClassID); 1517 break; 1518 1519 case kAudioPlugInPropertyTranslateUIDToBox: 1520 // This property takes the CFString passed in the qualifier and converts that 1521 // to the object ID of the box it corresponds to. For this driver, there is 1522 // just the one box. Note that it is not an error if the string in the 1523 // qualifier doesn't match any devices. In such case, kAudioObjectUnknown is 1524 // the object ID to return. 1525 FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: not enough space for the return value of kAudioPlugInPropertyTranslateUIDToBox"); 1526 FailWithAction(inQualifierDataSize == sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: the qualifier is the wrong size for kAudioPlugInPropertyTranslateUIDToBox"); 1527 FailWithAction(inQualifierData == NULL, theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: no qualifier for kAudioPlugInPropertyTranslateUIDToBox"); 1528 1529 CFStringRef boxUID = get_box_uid(); 1530 1531 if(CFStringCompare(*((CFStringRef*)inQualifierData), boxUID, 0) == kCFCompareEqualTo) 1532 { 1533 CFStringRef formattedString = CFStringCreateWithFormat(NULL, NULL, CFSTR(kBox_UID), kNumber_Of_Channels); 1534 if(CFStringCompare(*((CFStringRef*)inQualifierData), formattedString, 0) == kCFCompareEqualTo) 1535 { 1536 *((AudioObjectID*)outData) = kObjectID_Box; 1537 } 1538 else 1539 { 1540 *((AudioObjectID*)outData) = kAudioObjectUnknown; 1541 } 1542 *outDataSize = sizeof(AudioObjectID); 1543 CFRelease(formattedString); 1544 1545 *((AudioObjectID*)outData) = kObjectID_Box; 1546 } 1547 else 1548 { 1549 *((AudioObjectID*)outData) = kAudioObjectUnknown; 1550 } 1551 *outDataSize = sizeof(AudioObjectID); 1552 CFRelease(boxUID); 1553 break; 1554 1555 case kAudioPlugInPropertyDeviceList: 1556 // Calculate the number of items that have been requested. Note that this 1557 // number is allowed to be smaller than the actual size of the list. In such 1558 // case, only that number of items will be returned 1559 theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); 1560 1561 // Clamp that to the number of devices this driver implements (which is just 1 if the 1562 // box has been acquired) 1563 if(theNumberItemsToFetch > (gBox_Acquired ? 2 : 0)) 1564 { 1565 theNumberItemsToFetch = (gBox_Acquired ? 2 : 0); 1566 } 1567 1568 // Write the devices' object IDs into the return value 1569 if(theNumberItemsToFetch > 1) 1570 { 1571 ((AudioObjectID*)outData)[0] = kObjectID_Device; 1572 ((AudioObjectID*)outData)[1] = kObjectID_Device2; 1573 } 1574 else if(theNumberItemsToFetch > 0) 1575 { 1576 ((AudioObjectID*)outData)[0] = kObjectID_Device; 1577 } 1578 1579 // Return how many bytes we wrote to 1580 *outDataSize = theNumberItemsToFetch * sizeof(AudioClassID); 1581 break; 1582 1583 case kAudioPlugInPropertyTranslateUIDToDevice: 1584 // This property takes the CFString passed in the qualifier and converts that 1585 // to the object ID of the device it corresponds to. For this driver, there is 1586 // just the one device. Note that it is not an error if the string in the 1587 // qualifier doesn't match any devices. In such case, kAudioObjectUnknown is 1588 // the object ID to return. 1589 FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: not enough space for the return value of kAudioPlugInPropertyTranslateUIDToDevice"); 1590 FailWithAction(inQualifierDataSize == sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: the qualifier is the wrong size for kAudioPlugInPropertyTranslateUIDToDevice"); 1591 FailWithAction(inQualifierData == NULL, theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: no qualifier for kAudioPlugInPropertyTranslateUIDToDevice"); 1592 1593 1594 1595 CFStringRef deviceUID = get_device_uid(); 1596 CFStringRef device2UID = get_device2_uid(); 1597 1598 if(CFStringCompare(*((CFStringRef*)inQualifierData), deviceUID, 0) == kCFCompareEqualTo) 1599 { 1600 *((AudioObjectID*)outData) = kObjectID_Device; 1601 } 1602 else if(CFStringCompare(*((CFStringRef*)inQualifierData), device2UID, 0) == kCFCompareEqualTo) 1603 { 1604 *((AudioObjectID*)outData) = kObjectID_Device2; 1605 } 1606 else 1607 { 1608 *((AudioObjectID*)outData) = kAudioObjectUnknown; 1609 } 1610 *outDataSize = sizeof(AudioObjectID); 1611 CFRelease(deviceUID); 1612 CFRelease(device2UID); 1613 break; 1614 1615 case kAudioPlugInPropertyResourceBundle: 1616 // The resource bundle is a path relative to the path of the plug-in's bundle. 1617 // To specify that the plug-in bundle itself should be used, we just return the 1618 // empty string. 1619 FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetPlugInPropertyData: not enough space for the return value of kAudioPlugInPropertyResourceBundle"); 1620 *((CFStringRef*)outData) = CFSTR(""); 1621 *outDataSize = sizeof(CFStringRef); 1622 break; 1623 1624 default: 1625 theAnswer = kAudioHardwareUnknownPropertyError; 1626 break; 1627 }; 1628 1629 Done: 1630 return theAnswer; 1631 } 1632 1633 static OSStatus BlackHole_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]) 1634 { 1635 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData, inDataSize, inData) 1636 1637 // declare the local variables 1638 OSStatus theAnswer = 0; 1639 1640 // check the arguments 1641 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_SetPlugInPropertyData: bad driver reference"); 1642 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetPlugInPropertyData: no address"); 1643 FailWithAction(outNumberPropertiesChanged == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetPlugInPropertyData: no place to return the number of properties that changed"); 1644 FailWithAction(outChangedAddresses == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetPlugInPropertyData: no place to return the properties that changed"); 1645 FailWithAction(inObjectID != kObjectID_PlugIn, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_SetPlugInPropertyData: not the plug-in object"); 1646 1647 // initialize the returned number of changed properties 1648 *outNumberPropertiesChanged = 0; 1649 1650 // Note that for each object, this driver implements all the required properties plus a few 1651 // extras that are useful but not required. There is more detailed commentary about each 1652 // property in the BlackHole_GetPlugInPropertyData() method. 1653 switch(inAddress->mSelector) 1654 { 1655 default: 1656 theAnswer = kAudioHardwareUnknownPropertyError; 1657 break; 1658 }; 1659 1660 Done: 1661 return theAnswer; 1662 } 1663 1664 #pragma mark Box Property Operations 1665 1666 static Boolean BlackHole_HasBoxProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) 1667 { 1668 // This method returns whether or not the box object has the given property. 1669 1670 #pragma unused(inClientProcessID) 1671 1672 // declare the local variables 1673 Boolean theAnswer = false; 1674 1675 // check the arguments 1676 FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "BlackHole_HasBoxProperty: bad driver reference"); 1677 FailIf(inAddress == NULL, Done, "BlackHole_HasBoxProperty: no address"); 1678 FailIf(inObjectID != kObjectID_Box, Done, "BlackHole_HasBoxProperty: not the box object"); 1679 1680 1681 // Note that for each object, this driver implements all the required properties plus a few 1682 // extras that are useful but not required. There is more detailed commentary about each 1683 // property in the BlackHole_GetBoxPropertyData() method. 1684 switch(inAddress->mSelector) 1685 { 1686 case kAudioObjectPropertyBaseClass: 1687 case kAudioObjectPropertyClass: 1688 case kAudioObjectPropertyOwner: 1689 case kAudioObjectPropertyName: 1690 case kAudioObjectPropertyModelName: 1691 case kAudioObjectPropertyManufacturer: 1692 case kAudioObjectPropertyOwnedObjects: 1693 case kAudioObjectPropertyIdentify: 1694 case kAudioObjectPropertySerialNumber: 1695 case kAudioObjectPropertyFirmwareVersion: 1696 case kAudioBoxPropertyBoxUID: 1697 case kAudioBoxPropertyTransportType: 1698 case kAudioBoxPropertyHasAudio: 1699 case kAudioBoxPropertyHasVideo: 1700 case kAudioBoxPropertyHasMIDI: 1701 case kAudioBoxPropertyIsProtected: 1702 case kAudioBoxPropertyAcquired: 1703 case kAudioBoxPropertyAcquisitionFailed: 1704 case kAudioBoxPropertyDeviceList: 1705 theAnswer = true; 1706 break; 1707 }; 1708 1709 Done: 1710 return theAnswer; 1711 } 1712 1713 static OSStatus BlackHole_IsBoxPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) 1714 { 1715 // This method returns whether or not the given property on the plug-in object can have its 1716 // value changed. 1717 1718 #pragma unused(inClientProcessID) 1719 1720 // declare the local variables 1721 OSStatus theAnswer = 0; 1722 1723 // check the arguments 1724 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_IsBoxPropertySettable: bad driver reference"); 1725 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsBoxPropertySettable: no address"); 1726 FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsBoxPropertySettable: no place to put the return value"); 1727 FailWithAction(inObjectID != kObjectID_Box, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_IsBoxPropertySettable: not the plug-in object"); 1728 1729 // Note that for each object, this driver implements all the required properties plus a few 1730 // extras that are useful but not required. There is more detailed commentary about each 1731 // property in the BlackHole_GetBoxPropertyData() method. 1732 switch(inAddress->mSelector) 1733 { 1734 case kAudioObjectPropertyBaseClass: 1735 case kAudioObjectPropertyClass: 1736 case kAudioObjectPropertyOwner: 1737 case kAudioObjectPropertyModelName: 1738 case kAudioObjectPropertyManufacturer: 1739 case kAudioObjectPropertyOwnedObjects: 1740 case kAudioObjectPropertySerialNumber: 1741 case kAudioObjectPropertyFirmwareVersion: 1742 case kAudioBoxPropertyBoxUID: 1743 case kAudioBoxPropertyTransportType: 1744 case kAudioBoxPropertyHasAudio: 1745 case kAudioBoxPropertyHasVideo: 1746 case kAudioBoxPropertyHasMIDI: 1747 case kAudioBoxPropertyIsProtected: 1748 case kAudioBoxPropertyAcquisitionFailed: 1749 case kAudioBoxPropertyDeviceList: 1750 *outIsSettable = false; 1751 break; 1752 1753 case kAudioObjectPropertyName: 1754 case kAudioObjectPropertyIdentify: 1755 case kAudioBoxPropertyAcquired: 1756 *outIsSettable = true; 1757 break; 1758 1759 default: 1760 theAnswer = kAudioHardwareUnknownPropertyError; 1761 break; 1762 }; 1763 1764 Done: 1765 return theAnswer; 1766 } 1767 1768 static OSStatus BlackHole_GetBoxPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) 1769 { 1770 // This method returns the byte size of the property's data. 1771 1772 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) 1773 1774 // declare the local variables 1775 OSStatus theAnswer = 0; 1776 1777 // check the arguments 1778 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetBoxPropertyDataSize: bad driver reference"); 1779 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetBoxPropertyDataSize: no address"); 1780 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetBoxPropertyDataSize: no place to put the return value"); 1781 FailWithAction(inObjectID != kObjectID_Box, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetBoxPropertyDataSize: not the plug-in object"); 1782 1783 // Note that for each object, this driver implements all the required properties plus a few 1784 // extras that are useful but not required. There is more detailed commentary about each 1785 // property in the BlackHole_GetBoxPropertyData() method. 1786 switch(inAddress->mSelector) 1787 { 1788 case kAudioObjectPropertyBaseClass: 1789 *outDataSize = sizeof(AudioClassID); 1790 break; 1791 1792 case kAudioObjectPropertyClass: 1793 *outDataSize = sizeof(AudioClassID); 1794 break; 1795 1796 case kAudioObjectPropertyOwner: 1797 *outDataSize = sizeof(AudioObjectID); 1798 break; 1799 1800 case kAudioObjectPropertyName: 1801 *outDataSize = sizeof(CFStringRef); 1802 break; 1803 1804 case kAudioObjectPropertyModelName: 1805 *outDataSize = sizeof(CFStringRef); 1806 break; 1807 1808 case kAudioObjectPropertyManufacturer: 1809 *outDataSize = sizeof(CFStringRef); 1810 break; 1811 1812 case kAudioObjectPropertyOwnedObjects: 1813 *outDataSize = 0; 1814 break; 1815 1816 case kAudioObjectPropertyIdentify: 1817 *outDataSize = sizeof(UInt32); 1818 break; 1819 1820 case kAudioObjectPropertySerialNumber: 1821 *outDataSize = sizeof(CFStringRef); 1822 break; 1823 1824 case kAudioObjectPropertyFirmwareVersion: 1825 *outDataSize = sizeof(CFStringRef); 1826 break; 1827 1828 case kAudioBoxPropertyBoxUID: 1829 *outDataSize = sizeof(CFStringRef); 1830 break; 1831 1832 case kAudioBoxPropertyTransportType: 1833 *outDataSize = sizeof(UInt32); 1834 break; 1835 1836 case kAudioBoxPropertyHasAudio: 1837 *outDataSize = sizeof(UInt32); 1838 break; 1839 1840 case kAudioBoxPropertyHasVideo: 1841 *outDataSize = sizeof(UInt32); 1842 break; 1843 1844 case kAudioBoxPropertyHasMIDI: 1845 *outDataSize = sizeof(UInt32); 1846 break; 1847 1848 case kAudioBoxPropertyIsProtected: 1849 *outDataSize = sizeof(UInt32); 1850 break; 1851 1852 case kAudioBoxPropertyAcquired: 1853 *outDataSize = sizeof(UInt32); 1854 break; 1855 1856 case kAudioBoxPropertyAcquisitionFailed: 1857 *outDataSize = sizeof(UInt32); 1858 break; 1859 1860 case kAudioBoxPropertyDeviceList: 1861 { 1862 pthread_mutex_lock(&gPlugIn_StateMutex); 1863 *outDataSize = gBox_Acquired ? sizeof(AudioObjectID) * 2 : 0; 1864 pthread_mutex_unlock(&gPlugIn_StateMutex); 1865 } 1866 break; 1867 1868 default: 1869 theAnswer = kAudioHardwareUnknownPropertyError; 1870 break; 1871 }; 1872 1873 Done: 1874 return theAnswer; 1875 } 1876 1877 static OSStatus BlackHole_GetBoxPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) 1878 { 1879 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) 1880 1881 // declare the local variables 1882 OSStatus theAnswer = 0; 1883 1884 // check the arguments 1885 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetBoxPropertyData: bad driver reference"); 1886 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetBoxPropertyData: no address"); 1887 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetBoxPropertyData: no place to put the return value size"); 1888 FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetBoxPropertyData: no place to put the return value"); 1889 FailWithAction(inObjectID != kObjectID_Box, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetBoxPropertyData: not the plug-in object"); 1890 1891 // Note that for each object, this driver implements all the required properties plus a few 1892 // extras that are useful but not required. 1893 // 1894 // Also, since most of the data that will get returned is static, there are few instances where 1895 // it is necessary to lock the state mutex. 1896 switch(inAddress->mSelector) 1897 { 1898 case kAudioObjectPropertyBaseClass: 1899 // The base class for kAudioBoxClassID is kAudioObjectClassID 1900 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the box"); 1901 *((AudioClassID*)outData) = kAudioObjectClassID; 1902 *outDataSize = sizeof(AudioClassID); 1903 break; 1904 1905 case kAudioObjectPropertyClass: 1906 // The class is always kAudioBoxClassID for regular drivers 1907 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the box"); 1908 *((AudioClassID*)outData) = kAudioBoxClassID; 1909 *outDataSize = sizeof(AudioClassID); 1910 break; 1911 1912 case kAudioObjectPropertyOwner: 1913 // The owner is the plug-in object 1914 FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the box"); 1915 *((AudioObjectID*)outData) = kObjectID_PlugIn; 1916 *outDataSize = sizeof(AudioObjectID); 1917 break; 1918 1919 case kAudioObjectPropertyName: 1920 // This is the human readable name of the maker of the box. 1921 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the box"); 1922 pthread_mutex_lock(&gPlugIn_StateMutex); 1923 *((CFStringRef*)outData) = gBox_Name; 1924 pthread_mutex_unlock(&gPlugIn_StateMutex); 1925 if(*((CFStringRef*)outData) != NULL) 1926 { 1927 CFRetain(*((CFStringRef*)outData)); 1928 } 1929 *outDataSize = sizeof(CFStringRef); 1930 break; 1931 1932 case kAudioObjectPropertyModelName: 1933 // This is the human readable name of the maker of the box. 1934 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the box"); 1935 *((CFStringRef*)outData) = CFSTR("BlackHole"); 1936 *outDataSize = sizeof(CFStringRef); 1937 break; 1938 1939 case kAudioObjectPropertyManufacturer: 1940 // This is the human readable name of the maker of the box. 1941 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the box"); 1942 *((CFStringRef*)outData) = CFSTR("Existential Audio Inc."); 1943 *outDataSize = sizeof(CFStringRef); 1944 break; 1945 1946 case kAudioObjectPropertyOwnedObjects: 1947 // This returns the objects directly owned by the object. Boxes don't own anything. 1948 *outDataSize = 0; 1949 break; 1950 1951 case kAudioObjectPropertyIdentify: 1952 // This is used to highling the device in the UI, but it's value has no meaning 1953 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyIdentify for the box"); 1954 *((UInt32*)outData) = 0; 1955 *outDataSize = sizeof(UInt32); 1956 break; 1957 1958 case kAudioObjectPropertySerialNumber: 1959 // This is the human readable serial number of the box. 1960 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertySerialNumber for the box"); 1961 *((CFStringRef*)outData) = CFSTR("dd658747-4b9a-4de8-a001-c6a2ef1bb235"); 1962 *outDataSize = sizeof(CFStringRef); 1963 break; 1964 1965 case kAudioObjectPropertyFirmwareVersion: 1966 // This is the human readable firmware version of the box. 1967 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyFirmwareVersion for the box"); 1968 *((CFStringRef*)outData) = CFSTR("0.5.1"); 1969 *outDataSize = sizeof(CFStringRef); 1970 break; 1971 1972 case kAudioBoxPropertyBoxUID: 1973 // Boxes have UIDs the same as devices 1974 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the box"); 1975 1976 *((CFStringRef*)outData) = get_box_uid(); 1977 break; 1978 1979 case kAudioBoxPropertyTransportType: 1980 // This value represents how the device is attached to the system. This can be 1981 // any 32 bit integer, but common values for this property are defined in 1982 // <CoreAudio/AudioHardwareBase.h> 1983 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioDevicePropertyTransportType for the box"); 1984 *((UInt32*)outData) = kAudioDeviceTransportTypeVirtual; 1985 *outDataSize = sizeof(UInt32); 1986 break; 1987 1988 case kAudioBoxPropertyHasAudio: 1989 // Indicates whether or not the box has audio capabilities 1990 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyHasAudio for the box"); 1991 *((UInt32*)outData) = 1; 1992 *outDataSize = sizeof(UInt32); 1993 break; 1994 1995 case kAudioBoxPropertyHasVideo: 1996 // Indicates whether or not the box has video capabilities 1997 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyHasVideo for the box"); 1998 *((UInt32*)outData) = 0; 1999 *outDataSize = sizeof(UInt32); 2000 break; 2001 2002 case kAudioBoxPropertyHasMIDI: 2003 // Indicates whether or not the box has MIDI capabilities 2004 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyHasMIDI for the box"); 2005 *((UInt32*)outData) = 0; 2006 *outDataSize = sizeof(UInt32); 2007 break; 2008 2009 case kAudioBoxPropertyIsProtected: 2010 // Indicates whether or not the box has requires authentication to use 2011 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyIsProtected for the box"); 2012 *((UInt32*)outData) = 0; 2013 *outDataSize = sizeof(UInt32); 2014 break; 2015 2016 case kAudioBoxPropertyAcquired: 2017 // When set to a non-zero value, the device is acquired for use by the local machine 2018 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyAcquired for the box"); 2019 pthread_mutex_lock(&gPlugIn_StateMutex); 2020 *((UInt32*)outData) = gBox_Acquired ? 1 : 0; 2021 pthread_mutex_unlock(&gPlugIn_StateMutex); 2022 *outDataSize = sizeof(UInt32); 2023 break; 2024 2025 case kAudioBoxPropertyAcquisitionFailed: 2026 // This is used for notifications to say when an attempt to acquire a device has failed. 2027 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetBoxPropertyData: not enough space for the return value of kAudioBoxPropertyAcquisitionFailed for the box"); 2028 *((UInt32*)outData) = 0; 2029 *outDataSize = sizeof(UInt32); 2030 break; 2031 2032 case kAudioBoxPropertyDeviceList: 2033 // This is used to indicate which devices came from this box 2034 pthread_mutex_lock(&gPlugIn_StateMutex); 2035 if(gBox_Acquired) 2036 { 2037 if(inDataSize < sizeof(AudioObjectID)) 2038 { 2039 theAnswer = kAudioHardwareBadPropertySizeError; 2040 *outDataSize = 0; 2041 } 2042 else 2043 { 2044 if (inDataSize >= sizeof(AudioObjectID) * 2) 2045 { 2046 ((AudioObjectID*)outData)[0] = kObjectID_Device; 2047 ((AudioObjectID*)outData)[1] = kObjectID_Device2; 2048 *outDataSize = sizeof(AudioObjectID) * 2; 2049 } 2050 else 2051 { 2052 ((AudioObjectID*)outData)[0] = kObjectID_Device; 2053 *outDataSize = sizeof(AudioObjectID) * 1; 2054 } 2055 } 2056 } 2057 else 2058 { 2059 *outDataSize = 0; 2060 } 2061 2062 pthread_mutex_unlock(&gPlugIn_StateMutex); 2063 break; 2064 2065 default: 2066 theAnswer = kAudioHardwareUnknownPropertyError; 2067 break; 2068 }; 2069 2070 Done: 2071 return theAnswer; 2072 } 2073 2074 static OSStatus BlackHole_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]) 2075 { 2076 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData, inDataSize, inData) 2077 2078 // declare the local variables 2079 OSStatus theAnswer = 0; 2080 2081 // check the arguments 2082 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_SetBoxPropertyData: bad driver reference"); 2083 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetBoxPropertyData: no address"); 2084 FailWithAction(outNumberPropertiesChanged == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetBoxPropertyData: no place to return the number of properties that changed"); 2085 FailWithAction(outChangedAddresses == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetBoxPropertyData: no place to return the properties that changed"); 2086 FailWithAction(inObjectID != kObjectID_Box, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_SetBoxPropertyData: not the box object"); 2087 2088 // initialize the returned number of changed properties 2089 *outNumberPropertiesChanged = 0; 2090 2091 // Note that for each object, this driver implements all the required properties plus a few 2092 // extras that are useful but not required. There is more detailed commentary about each 2093 // property in the BlackHole_GetPlugInPropertyData() method. 2094 switch(inAddress->mSelector) 2095 { 2096 case kAudioObjectPropertyName: 2097 // Boxes should allow their name to be editable 2098 { 2099 FailWithAction(inData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetBoxPropertyData: NULL data for kAudioObjectPropertyName"); 2100 FailWithAction(inDataSize != sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_SetBoxPropertyData: wrong size for the data for kAudioObjectPropertyName"); 2101 CFStringRef* theNewName = (CFStringRef*)inData; 2102 pthread_mutex_lock(&gPlugIn_StateMutex); 2103 if((theNewName != NULL) && (*theNewName != NULL)) 2104 { 2105 CFRetain(*theNewName); 2106 } 2107 if(gBox_Name != NULL) 2108 { 2109 CFRelease(gBox_Name); 2110 } 2111 gBox_Name = *theNewName; 2112 pthread_mutex_unlock(&gPlugIn_StateMutex); 2113 *outNumberPropertiesChanged = 1; 2114 outChangedAddresses[0].mSelector = kAudioObjectPropertyName; 2115 outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; 2116 outChangedAddresses[0].mElement = kAudioObjectPropertyElementMain; 2117 } 2118 break; 2119 2120 case kAudioObjectPropertyIdentify: 2121 // since we don't have any actual hardware to flash, we will schedule a notification for 2122 // this property off into the future as a testing thing. Note that a real implementation 2123 // of this property should only send the notification if the hardware wants the app to 2124 // flash it's UI for the device. 2125 { 2126 syslog(LOG_NOTICE, "The identify property has been set on the Box implemented by the BlackHole driver."); 2127 FailWithAction(inDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_SetBoxPropertyData: wrong size for the data for kAudioObjectPropertyIdentify"); 2128 dispatch_after(dispatch_time(0, 2ULL * 1000ULL * 1000ULL * 1000ULL), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() 2129 { 2130 AudioObjectPropertyAddress theAddress = { kAudioObjectPropertyIdentify, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain }; 2131 gPlugIn_Host->PropertiesChanged(gPlugIn_Host, kObjectID_Box, 1, &theAddress); 2132 }); 2133 } 2134 break; 2135 2136 case kAudioBoxPropertyAcquired: 2137 // When the box is acquired, it means the contents, namely the device, are available to the system 2138 { 2139 FailWithAction(inDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_SetBoxPropertyData: wrong size for the data for kAudioBoxPropertyAcquired"); 2140 pthread_mutex_lock(&gPlugIn_StateMutex); 2141 if(gBox_Acquired != (*((UInt32*)inData) != 0)) 2142 { 2143 // the new value is different from the old value, so save it 2144 gBox_Acquired = *((UInt32*)inData) != 0; 2145 gPlugIn_Host->WriteToStorage(gPlugIn_Host, CFSTR("box acquired"), gBox_Acquired ? kCFBooleanTrue : kCFBooleanFalse); 2146 2147 // and it means that this property and the device list property have changed 2148 *outNumberPropertiesChanged = 2; 2149 outChangedAddresses[0].mSelector = kAudioBoxPropertyAcquired; 2150 outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; 2151 outChangedAddresses[0].mElement = kAudioObjectPropertyElementMain; 2152 outChangedAddresses[1].mSelector = kAudioBoxPropertyDeviceList; 2153 outChangedAddresses[1].mScope = kAudioObjectPropertyScopeGlobal; 2154 outChangedAddresses[1].mElement = kAudioObjectPropertyElementMain; 2155 2156 // but it also means that the device list has changed for the plug-in too 2157 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() 2158 { 2159 AudioObjectPropertyAddress theAddress = { kAudioPlugInPropertyDeviceList, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain }; 2160 gPlugIn_Host->PropertiesChanged(gPlugIn_Host, kObjectID_PlugIn, 1, &theAddress); 2161 }); 2162 } 2163 pthread_mutex_unlock(&gPlugIn_StateMutex); 2164 } 2165 break; 2166 2167 default: 2168 theAnswer = kAudioHardwareUnknownPropertyError; 2169 break; 2170 }; 2171 2172 Done: 2173 return theAnswer; 2174 } 2175 2176 #pragma mark Device Property Operations 2177 2178 static Boolean BlackHole_HasDeviceProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) 2179 { 2180 // This method returns whether or not the given object has the given property. 2181 2182 #pragma unused(inClientProcessID) 2183 2184 // declare the local variables 2185 Boolean theAnswer = false; 2186 2187 // check the arguments 2188 FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "BlackHole_HasDeviceProperty: bad driver reference"); 2189 FailIf(inAddress == NULL, Done, "BlackHole_HasDeviceProperty: no address"); 2190 FailIf(inObjectID != kObjectID_Device && inObjectID != kObjectID_Device2, Done, "BlackHole_HasDeviceProperty: not the device object"); 2191 2192 // Note that for each object, this driver implements all the required properties plus a few 2193 // extras that are useful but not required. There is more detailed commentary about each 2194 // property in the BlackHole_GetDevicePropertyData() method. 2195 switch(inAddress->mSelector) 2196 { 2197 case kAudioObjectPropertyBaseClass: 2198 case kAudioObjectPropertyClass: 2199 case kAudioObjectPropertyOwner: 2200 case kAudioObjectPropertyName: 2201 case kAudioObjectPropertyManufacturer: 2202 case kAudioObjectPropertyOwnedObjects: 2203 case kAudioDevicePropertyDeviceUID: 2204 case kAudioDevicePropertyModelUID: 2205 case kAudioDevicePropertyTransportType: 2206 case kAudioDevicePropertyRelatedDevices: 2207 case kAudioDevicePropertyClockDomain: 2208 case kAudioDevicePropertyDeviceIsAlive: 2209 case kAudioDevicePropertyDeviceIsRunning: 2210 case kAudioObjectPropertyControlList: 2211 case kAudioDevicePropertyNominalSampleRate: 2212 case kAudioDevicePropertyAvailableNominalSampleRates: 2213 case kAudioDevicePropertyIsHidden: 2214 case kAudioDevicePropertyZeroTimeStampPeriod: 2215 case kAudioDevicePropertyIcon: 2216 case kAudioDevicePropertyStreams: 2217 theAnswer = true; 2218 break; 2219 2220 case kAudioDevicePropertyDeviceCanBeDefaultDevice: 2221 case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: 2222 case kAudioDevicePropertyLatency: 2223 case kAudioDevicePropertySafetyOffset: 2224 case kAudioDevicePropertyPreferredChannelsForStereo: 2225 case kAudioDevicePropertyPreferredChannelLayout: 2226 theAnswer = (inAddress->mScope == kAudioObjectPropertyScopeInput) || (inAddress->mScope == kAudioObjectPropertyScopeOutput); 2227 break; 2228 }; 2229 2230 Done: 2231 return theAnswer; 2232 } 2233 2234 static OSStatus BlackHole_IsDevicePropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) 2235 { 2236 // This method returns whether or not the given property on the object can have its value 2237 // changed. 2238 2239 #pragma unused(inClientProcessID) 2240 2241 // declare the local variables 2242 OSStatus theAnswer = 0; 2243 2244 // check the arguments 2245 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_IsDevicePropertySettable: bad driver reference"); 2246 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsDevicePropertySettable: no address"); 2247 FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsDevicePropertySettable: no place to put the return value"); 2248 FailWithAction(inObjectID != kObjectID_Device && inObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_IsDevicePropertySettable: not the device object"); 2249 2250 // Note that for each object, this driver implements all the required properties plus a few 2251 // extras that are useful but not required. There is more detailed commentary about each 2252 // property in the BlackHole_GetDevicePropertyData() method. 2253 switch(inAddress->mSelector) 2254 { 2255 case kAudioObjectPropertyBaseClass: 2256 case kAudioObjectPropertyClass: 2257 case kAudioObjectPropertyOwner: 2258 case kAudioObjectPropertyName: 2259 case kAudioObjectPropertyManufacturer: 2260 case kAudioObjectPropertyOwnedObjects: 2261 case kAudioDevicePropertyDeviceUID: 2262 case kAudioDevicePropertyModelUID: 2263 case kAudioDevicePropertyTransportType: 2264 case kAudioDevicePropertyRelatedDevices: 2265 case kAudioDevicePropertyClockDomain: 2266 case kAudioDevicePropertyDeviceIsAlive: 2267 case kAudioDevicePropertyDeviceIsRunning: 2268 case kAudioDevicePropertyDeviceCanBeDefaultDevice: 2269 case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: 2270 case kAudioDevicePropertyLatency: 2271 case kAudioDevicePropertyStreams: 2272 case kAudioObjectPropertyControlList: 2273 case kAudioDevicePropertySafetyOffset: 2274 case kAudioDevicePropertyAvailableNominalSampleRates: 2275 case kAudioDevicePropertyIsHidden: 2276 case kAudioDevicePropertyPreferredChannelsForStereo: 2277 case kAudioDevicePropertyPreferredChannelLayout: 2278 case kAudioDevicePropertyZeroTimeStampPeriod: 2279 case kAudioDevicePropertyIcon: 2280 *outIsSettable = false; 2281 break; 2282 2283 case kAudioDevicePropertyNominalSampleRate: 2284 *outIsSettable = true; 2285 break; 2286 2287 default: 2288 theAnswer = kAudioHardwareUnknownPropertyError; 2289 break; 2290 }; 2291 2292 Done: 2293 return theAnswer; 2294 } 2295 2296 static OSStatus BlackHole_GetDevicePropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) 2297 { 2298 // This method returns the byte size of the property's data. 2299 2300 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) 2301 2302 // declare the local variables 2303 OSStatus theAnswer = 0; 2304 2305 // check the arguments 2306 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetDevicePropertyDataSize: bad driver reference"); 2307 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetDevicePropertyDataSize: no address"); 2308 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetDevicePropertyDataSize: no place to put the return value"); 2309 FailWithAction(inObjectID != kObjectID_Device && inObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetDevicePropertyDataSize: not the device object"); 2310 2311 // Note that for each object, this driver implements all the required properties plus a few 2312 // extras that are useful but not required. There is more detailed commentary about each 2313 // property in the BlackHole_GetDevicePropertyData() method. 2314 switch(inAddress->mSelector) 2315 { 2316 case kAudioObjectPropertyBaseClass: 2317 *outDataSize = sizeof(AudioClassID); 2318 break; 2319 2320 case kAudioObjectPropertyClass: 2321 *outDataSize = sizeof(AudioClassID); 2322 break; 2323 2324 case kAudioObjectPropertyOwner: 2325 *outDataSize = sizeof(AudioObjectID); 2326 break; 2327 2328 case kAudioObjectPropertyName: 2329 *outDataSize = sizeof(CFStringRef); 2330 break; 2331 2332 case kAudioObjectPropertyManufacturer: 2333 *outDataSize = sizeof(CFStringRef); 2334 break; 2335 2336 case kAudioObjectPropertyOwnedObjects: 2337 *outDataSize = device_object_list_size(inAddress->mScope, inObjectID) * sizeof(AudioObjectID); 2338 break; 2339 2340 case kAudioDevicePropertyDeviceUID: 2341 *outDataSize = sizeof(CFStringRef); 2342 break; 2343 2344 case kAudioDevicePropertyModelUID: 2345 *outDataSize = sizeof(CFStringRef); 2346 break; 2347 2348 case kAudioDevicePropertyTransportType: 2349 *outDataSize = sizeof(UInt32); 2350 break; 2351 2352 case kAudioDevicePropertyRelatedDevices: 2353 *outDataSize = sizeof(AudioObjectID); 2354 break; 2355 2356 case kAudioDevicePropertyClockDomain: 2357 *outDataSize = sizeof(UInt32); 2358 break; 2359 2360 case kAudioDevicePropertyDeviceIsAlive: 2361 *outDataSize = sizeof(AudioClassID); 2362 break; 2363 2364 case kAudioDevicePropertyDeviceIsRunning: 2365 *outDataSize = sizeof(UInt32); 2366 break; 2367 2368 case kAudioDevicePropertyDeviceCanBeDefaultDevice: 2369 *outDataSize = sizeof(UInt32); 2370 break; 2371 2372 case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: 2373 *outDataSize = sizeof(UInt32); 2374 break; 2375 2376 case kAudioDevicePropertyLatency: 2377 *outDataSize = sizeof(UInt32); 2378 break; 2379 2380 case kAudioDevicePropertyStreams: 2381 *outDataSize = device_stream_list_size(inAddress->mScope, inObjectID) * sizeof(AudioObjectID); 2382 break; 2383 2384 case kAudioObjectPropertyControlList: 2385 *outDataSize = device_control_list_size(inAddress->mScope, inObjectID) * sizeof(AudioObjectID); 2386 break; 2387 2388 case kAudioDevicePropertySafetyOffset: 2389 *outDataSize = sizeof(UInt32); 2390 break; 2391 2392 case kAudioDevicePropertyNominalSampleRate: 2393 *outDataSize = sizeof(Float64); 2394 break; 2395 2396 case kAudioDevicePropertyAvailableNominalSampleRates: 2397 *outDataSize = kDevice_SampleRatesSize * sizeof(AudioValueRange); 2398 break; 2399 2400 case kAudioDevicePropertyIsHidden: 2401 *outDataSize = sizeof(UInt32); 2402 break; 2403 2404 case kAudioDevicePropertyPreferredChannelsForStereo: 2405 *outDataSize = 2 * sizeof(UInt32); 2406 break; 2407 2408 case kAudioDevicePropertyPreferredChannelLayout: 2409 *outDataSize = offsetof(AudioChannelLayout, mChannelDescriptions) + (kNumber_Of_Channels * sizeof(AudioChannelDescription)); 2410 break; 2411 2412 case kAudioDevicePropertyZeroTimeStampPeriod: 2413 *outDataSize = sizeof(UInt32); 2414 break; 2415 2416 case kAudioDevicePropertyIcon: 2417 *outDataSize = sizeof(CFURLRef); 2418 break; 2419 2420 default: 2421 theAnswer = kAudioHardwareUnknownPropertyError; 2422 break; 2423 }; 2424 2425 Done: 2426 return theAnswer; 2427 } 2428 2429 static OSStatus BlackHole_GetDevicePropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) 2430 { 2431 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) 2432 2433 // declare the local variables 2434 OSStatus theAnswer = 0; 2435 UInt32 theNumberItemsToFetch; 2436 UInt32 theItemIndex; 2437 2438 // check the arguments 2439 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetDevicePropertyData: bad driver reference"); 2440 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetDevicePropertyData: no address"); 2441 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetDevicePropertyData: no place to put the return value size"); 2442 FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetDevicePropertyData: no place to put the return value"); 2443 FailWithAction(inObjectID != kObjectID_Device && inObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetDevicePropertyData: not the device object"); 2444 2445 // Note that for each object, this driver implements all the required properties plus a few 2446 // extras that are useful but not required. 2447 // 2448 // Also, since most of the data that will get returned is static, there are few instances where 2449 // it is necessary to lock the state mutex. 2450 switch(inAddress->mSelector) 2451 { 2452 case kAudioObjectPropertyBaseClass: 2453 // The base class for kAudioDeviceClassID is kAudioObjectClassID 2454 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the device"); 2455 *((AudioClassID*)outData) = kAudioObjectClassID; 2456 *outDataSize = sizeof(AudioClassID); 2457 break; 2458 2459 case kAudioObjectPropertyClass: 2460 // The class is always kAudioDeviceClassID for devices created by drivers 2461 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioObjectPropertyClass for the device"); 2462 *((AudioClassID*)outData) = kAudioDeviceClassID; 2463 *outDataSize = sizeof(AudioClassID); 2464 break; 2465 2466 case kAudioObjectPropertyOwner: 2467 // The device's owner is the plug-in object 2468 FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the device"); 2469 *((AudioObjectID*)outData) = kObjectID_PlugIn; 2470 *outDataSize = sizeof(AudioObjectID); 2471 break; 2472 2473 case kAudioObjectPropertyName: 2474 // This is the human readable name of the device. 2475 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the device"); 2476 2477 switch (inObjectID) { 2478 case kObjectID_Device: 2479 *((CFStringRef*)outData) = get_device_name(); 2480 *outDataSize = sizeof(CFStringRef); 2481 break; 2482 2483 case kObjectID_Device2: 2484 *((CFStringRef*)outData) = get_device2_name(); 2485 *outDataSize = sizeof(CFStringRef); 2486 break; 2487 } 2488 break; 2489 2490 case kAudioObjectPropertyManufacturer: 2491 // This is the human readable name of the maker of the plug-in. 2492 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer for the device"); 2493 *((CFStringRef*)outData) = CFSTR(kManufacturer_Name); 2494 *outDataSize = sizeof(CFStringRef); 2495 break; 2496 2497 case kAudioObjectPropertyOwnedObjects: 2498 // Calculate the number of items that have been requested. Note that this 2499 // number is allowed to be smaller than the actual size of the list. In such 2500 // case, only that number of items will be returned 2501 theNumberItemsToFetch = minimum(inDataSize / sizeof(AudioObjectID), device_object_list_size(inAddress->mScope, inObjectID)); 2502 2503 // fill out the list with the right objects 2504 switch (inObjectID) { 2505 case kObjectID_Device: 2506 for (UInt32 i = 0, k = 0; k < theNumberItemsToFetch; i++) 2507 { 2508 if (kDevice_ObjectList[i].scope == inAddress->mScope || inAddress->mScope == kAudioObjectPropertyScopeGlobal) 2509 { 2510 ((AudioObjectID*)outData)[k++] = kDevice_ObjectList[i].id; 2511 } 2512 } 2513 break; 2514 2515 case kObjectID_Device2: 2516 for (UInt32 i = 0, k = 0; k < theNumberItemsToFetch; i++) 2517 { 2518 if (kDevice2_ObjectList[i].scope == inAddress->mScope || inAddress->mScope == kAudioObjectPropertyScopeGlobal) 2519 { 2520 ((AudioObjectID*)outData)[k++] = kDevice2_ObjectList[i].id; 2521 } 2522 } 2523 break; 2524 } 2525 2526 // report how much we wrote 2527 *outDataSize = theNumberItemsToFetch * sizeof(AudioObjectID); 2528 break; 2529 2530 case kAudioDevicePropertyDeviceUID: 2531 // This is a CFString that is a persistent token that can identify the same 2532 // audio device across boot sessions. Note that two instances of the same 2533 // device must have different values for this property. 2534 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceUID for the device"); 2535 2536 switch (inObjectID) { 2537 case kObjectID_Device: 2538 *((CFStringRef*)outData) = get_device_uid(); 2539 *outDataSize = sizeof(CFStringRef); 2540 break; 2541 2542 case kObjectID_Device2: 2543 *((CFStringRef*)outData) = get_device2_uid(); 2544 *outDataSize = sizeof(CFStringRef); 2545 break; 2546 } 2547 2548 2549 break; 2550 2551 case kAudioDevicePropertyModelUID: 2552 // This is a CFString that is a persistent token that can identify audio 2553 // devices that are the same kind of device. Note that two instances of the 2554 // save device must have the same value for this property. 2555 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyModelUID for the device"); 2556 2557 *((CFStringRef*)outData) = get_device_model_uid(); 2558 *outDataSize = sizeof(CFStringRef); 2559 break; 2560 2561 case kAudioDevicePropertyTransportType: 2562 // This value represents how the device is attached to the system. This can be 2563 // any 32 bit integer, but common values for this property are defined in 2564 // <CoreAudio/AudioHardwareBase.h> 2565 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyTransportType for the device"); 2566 *((UInt32*)outData) = kAudioDeviceTransportTypeVirtual; 2567 *outDataSize = sizeof(UInt32); 2568 break; 2569 2570 case kAudioDevicePropertyRelatedDevices: 2571 // The related devices property identifys device objects that are very closely 2572 // related. Generally, this is for relating devices that are packaged together 2573 // in the hardware such as when the input side and the output side of a piece 2574 // of hardware can be clocked separately and therefore need to be represented 2575 // as separate AudioDevice objects. In such case, both devices would report 2576 // that they are related to each other. Note that at minimum, a device is 2577 // related to itself, so this list will always be at least one item long. 2578 2579 // Calculate the number of items that have been requested. Note that this 2580 // number is allowed to be smaller than the actual size of the list. In such 2581 // case, only that number of items will be returned 2582 theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); 2583 2584 // we only have the one device... 2585 if(theNumberItemsToFetch > 1) 2586 { 2587 theNumberItemsToFetch = 1; 2588 } 2589 2590 // Write the devices' object IDs into the return value 2591 if(theNumberItemsToFetch > 0) 2592 { 2593 switch (inObjectID) { 2594 case kObjectID_Device: 2595 ((AudioObjectID*)outData)[0] = kObjectID_Device; 2596 break; 2597 2598 case kObjectID_Device2: 2599 ((AudioObjectID*)outData)[0] = kObjectID_Device2; 2600 break; 2601 } 2602 2603 } 2604 2605 // report how much we wrote 2606 *outDataSize = theNumberItemsToFetch * sizeof(AudioObjectID); 2607 break; 2608 2609 case kAudioDevicePropertyClockDomain: 2610 // This property allows the device to declare what other devices it is 2611 // synchronized with in hardware. The way it works is that if two devices have 2612 // the same value for this property and the value is not zero, then the two 2613 // devices are synchronized in hardware. Note that a device that either can't 2614 // be synchronized with others or doesn't know should return 0 for this 2615 // property. 2616 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyClockDomain for the device"); 2617 *((UInt32*)outData) = 0; 2618 *outDataSize = sizeof(UInt32); 2619 break; 2620 2621 case kAudioDevicePropertyDeviceIsAlive: 2622 // This property returns whether or not the device is alive. Note that it is 2623 // not uncommon for a device to be dead but still momentarily available in the 2624 // device list. In the case of this device, it will always be alive. 2625 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceIsAlive for the device"); 2626 *((UInt32*)outData) = 1; 2627 *outDataSize = sizeof(UInt32); 2628 break; 2629 2630 case kAudioDevicePropertyDeviceIsRunning: 2631 // This property returns whether or not IO is running for the device. Note that 2632 // we need to take both the state lock to check this value for thread safety. 2633 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceIsRunning for the device"); 2634 pthread_mutex_lock(&gPlugIn_StateMutex); 2635 if (inObjectID == kObjectID_Device) { 2636 *((UInt32*)outData) = ((gDevice_IOIsRunning > 0) > 0) ? 1 : 0; 2637 } 2638 2639 switch (inObjectID) { 2640 case kObjectID_Device: 2641 *((UInt32*)outData) = ((gDevice_IOIsRunning > 0) > 0) ? 1 : 0; 2642 break; 2643 case kObjectID_Device2: 2644 *((UInt32*)outData) = ((gDevice2_IOIsRunning > 0) > 0) ? 1 : 0; 2645 break; 2646 default: 2647 *((UInt32*)outData) = 0; 2648 break; 2649 } 2650 2651 pthread_mutex_unlock(&gPlugIn_StateMutex); 2652 *outDataSize = sizeof(UInt32); 2653 break; 2654 2655 case kAudioDevicePropertyDeviceCanBeDefaultDevice: 2656 // This property returns whether or not the device wants to be able to be the 2657 // default device for content. This is the device that iTunes and QuickTime 2658 // will use to play their content on and FaceTime will use as it's microhphone. 2659 // Nearly all devices should allow for this. 2660 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceCanBeDefaultDevice for the device"); 2661 *((UInt32*)outData) = kCanBeDefaultDevice; 2662 *outDataSize = sizeof(UInt32); 2663 break; 2664 2665 case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: 2666 // This property returns whether or not the device wants to be the system 2667 // default device. This is the device that is used to play interface sounds and 2668 // other incidental or UI-related sounds on. Most devices should allow this 2669 // although devices with lots of latency may not want to. 2670 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceCanBeDefaultSystemDevice for the device"); 2671 *((UInt32*)outData) = kCanBeDefaultSystemDevice; 2672 *outDataSize = sizeof(UInt32); 2673 break; 2674 2675 case kAudioDevicePropertyLatency: 2676 // This property returns the presentation latency of the device. For this, 2677 // device, the value is 0 due to the fact that it always vends silence. 2678 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyLatency for the device"); 2679 *((UInt32*)outData) = 0; 2680 *outDataSize = sizeof(UInt32); 2681 break; 2682 2683 case kAudioDevicePropertyStreams: 2684 // Calculate the number of items that have been requested. Note that this 2685 // number is allowed to be smaller than the actual size of the list. In such 2686 // case, only that number of items will be returned 2687 theNumberItemsToFetch = minimum(inDataSize / sizeof(AudioObjectID), device_stream_list_size(inAddress->mScope, inObjectID)); 2688 2689 // fill out the list with as many objects as requested 2690 switch (inObjectID) { 2691 case kObjectID_Device: 2692 for (UInt32 i = 0, k = 0; k < theNumberItemsToFetch; i++) 2693 { 2694 if ((kDevice_ObjectList[i].type == kObjectType_Stream) && 2695 (kDevice_ObjectList[i].scope == inAddress->mScope || inAddress->mScope == kAudioObjectPropertyScopeGlobal)) 2696 { 2697 ((AudioObjectID*)outData)[k++] = kDevice_ObjectList[i].id; 2698 } 2699 } 2700 break; 2701 2702 case kObjectID_Device2: 2703 for (UInt32 i = 0, k = 0; k < theNumberItemsToFetch; i++) 2704 { 2705 if ((kDevice2_ObjectList[i].type == kObjectType_Stream) && 2706 (kDevice2_ObjectList[i].scope == inAddress->mScope || inAddress->mScope == kAudioObjectPropertyScopeGlobal)) 2707 { 2708 ((AudioObjectID*)outData)[k++] = kDevice2_ObjectList[i].id; 2709 } 2710 } 2711 break; 2712 } 2713 2714 // report how much we wrote 2715 *outDataSize = theNumberItemsToFetch * sizeof(AudioObjectID); 2716 break; 2717 2718 case kAudioObjectPropertyControlList: 2719 // Calculate the number of items that have been requested. Note that this 2720 // number is allowed to be smaller than the actual size of the list. In such 2721 // case, only that number of items will be returned 2722 2723 theNumberItemsToFetch = minimum(inDataSize / sizeof(AudioObjectID), device_control_list_size(inAddress->mScope, inObjectID)); 2724 2725 // fill out the list with as many objects as requested 2726 switch (inObjectID) { 2727 case kObjectID_Device: 2728 pthread_mutex_lock(&gPlugIn_StateMutex); 2729 for (UInt32 i = 0, k = 0; k < theNumberItemsToFetch; i++) 2730 { 2731 // TODO remove hack! There must be a better way than looking for a fixed i 2732 if ((kDevice_ObjectList[i].type == kObjectType_Control) && !(!gPitch_Adjust_Enabled && kDevice_ObjectList[i].id==kObjectID_Pitch_Adjust)) 2733 { 2734 ((AudioObjectID*)outData)[k++] = kDevice_ObjectList[i].id; 2735 } 2736 } 2737 pthread_mutex_unlock(&gPlugIn_StateMutex); 2738 break; 2739 2740 case kObjectID_Device2: 2741 for (UInt32 i = 0, k = 0; k < theNumberItemsToFetch; i++) 2742 { 2743 if ((kDevice_ObjectList[i].type == kObjectType_Control) && !(!gPitch_Adjust_Enabled && kDevice_ObjectList[i].id==kObjectID_Pitch_Adjust)) 2744 { 2745 ((AudioObjectID*)outData)[k++] = kDevice2_ObjectList[i].id; 2746 } 2747 } 2748 break; 2749 } 2750 2751 // report how much we wrote 2752 *outDataSize = theNumberItemsToFetch * sizeof(AudioObjectID); 2753 break; 2754 2755 case kAudioDevicePropertySafetyOffset: 2756 // This property returns the how close to now the HAL can read and write. For 2757 // this, device, the value is 0 due to the fact that it always vends silence. 2758 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertySafetyOffset for the device"); 2759 *((UInt32*)outData) = kLatency_Frame_Size; 2760 *outDataSize = sizeof(UInt32); 2761 break; 2762 2763 case kAudioDevicePropertyNominalSampleRate: 2764 // This property returns the nominal sample rate of the device. Note that we 2765 // only need to take the state lock to get this value. 2766 FailWithAction(inDataSize < sizeof(Float64), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyNominalSampleRate for the device"); 2767 pthread_mutex_lock(&gPlugIn_StateMutex); 2768 *((Float64*)outData) = gDevice_SampleRate; 2769 pthread_mutex_unlock(&gPlugIn_StateMutex); 2770 *outDataSize = sizeof(Float64); 2771 break; 2772 2773 case kAudioDevicePropertyAvailableNominalSampleRates: 2774 // This returns all nominal sample rates the device supports as an array of 2775 // AudioValueRangeStructs. Note that for discrete sampler rates, the range 2776 // will have the minimum value equal to the maximum value. 2777 2778 // Calculate the number of items that have been requested. Note that this 2779 // number is allowed to be smaller than the actual size of the list. In such 2780 // case, only that number of items will be returned 2781 theNumberItemsToFetch = inDataSize / sizeof(AudioValueRange); 2782 2783 // clamp it to the number of items we have 2784 if(theNumberItemsToFetch > kDevice_SampleRatesSize) 2785 { 2786 theNumberItemsToFetch = kDevice_SampleRatesSize; 2787 } 2788 2789 // fill out the return array 2790 for(UInt32 i = 0; i < theNumberItemsToFetch; i++) 2791 { 2792 ((AudioValueRange*)outData)[i].mMinimum = kDevice_SampleRates[i]; 2793 ((AudioValueRange*)outData)[i].mMaximum = kDevice_SampleRates[i]; 2794 } 2795 2796 // report how much we wrote 2797 *outDataSize = theNumberItemsToFetch * sizeof(AudioValueRange); 2798 break; 2799 2800 case kAudioDevicePropertyIsHidden: 2801 // This returns whether or not the device is visible to clients. 2802 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyIsHidden for the device"); 2803 2804 switch (inObjectID) { 2805 case kObjectID_Device: 2806 *((UInt32*)outData) = kDevice_IsHidden; 2807 break; 2808 2809 case kObjectID_Device2: 2810 *((UInt32*)outData) = kDevice2_IsHidden; 2811 break; 2812 } 2813 *outDataSize = sizeof(UInt32); 2814 break; 2815 2816 case kAudioDevicePropertyPreferredChannelsForStereo: 2817 // This property returns which two channels to use as left/right for stereo 2818 // data by default. Note that the channel numbers are 1-based.xz 2819 FailWithAction(inDataSize < (2 * sizeof(UInt32)), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyPreferredChannelsForStereo for the device"); 2820 ((UInt32*)outData)[0] = 1; 2821 ((UInt32*)outData)[1] = 2; 2822 *outDataSize = 2 * sizeof(UInt32); 2823 break; 2824 2825 case kAudioDevicePropertyPreferredChannelLayout: 2826 // This property returns the default AudioChannelLayout to use for the device 2827 // by default. For this device, we return a stereo ACL. 2828 { 2829 // calculate how big the 2830 UInt32 theACLSize = offsetof(AudioChannelLayout, mChannelDescriptions) + (kNumber_Of_Channels * sizeof(AudioChannelDescription)); 2831 FailWithAction(inDataSize < theACLSize, theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyPreferredChannelLayout for the device"); 2832 ((AudioChannelLayout*)outData)->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions; 2833 ((AudioChannelLayout*)outData)->mChannelBitmap = 0; 2834 ((AudioChannelLayout*)outData)->mNumberChannelDescriptions = kNumber_Of_Channels; 2835 for(theItemIndex = 0; theItemIndex < kNumber_Of_Channels; ++theItemIndex) 2836 { 2837 ((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mChannelLabel = kAudioChannelLabel_Left + theItemIndex; 2838 ((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mChannelFlags = 0; 2839 ((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mCoordinates[0] = 0; 2840 ((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mCoordinates[1] = 0; 2841 ((AudioChannelLayout*)outData)->mChannelDescriptions[theItemIndex].mCoordinates[2] = 0; 2842 } 2843 *outDataSize = theACLSize; 2844 } 2845 break; 2846 2847 case kAudioDevicePropertyZeroTimeStampPeriod: 2848 // This property returns how many frames the HAL should expect to see between 2849 // successive sample times in the zero time stamps this device provides. 2850 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyZeroTimeStampPeriod for the device"); 2851 *((UInt32*)outData) = kDevice_RingBufferSize; 2852 *outDataSize = sizeof(UInt32); 2853 break; 2854 2855 case kAudioDevicePropertyIcon: 2856 { 2857 // This is a CFURL that points to the device's Icon in the plug-in's resource bundle. 2858 FailWithAction(inDataSize < sizeof(CFURLRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetDevicePropertyData: not enough space for the return value of kAudioDevicePropertyDeviceUID for the device"); 2859 CFBundleRef theBundle = CFBundleGetBundleWithIdentifier(CFSTR(kPlugIn_BundleID)); 2860 FailWithAction(theBundle == NULL, theAnswer = kAudioHardwareUnspecifiedError, Done, "BlackHole_GetDevicePropertyData: could not get the plug-in bundle for kAudioDevicePropertyIcon"); 2861 CFURLRef theURL = CFBundleCopyResourceURL(theBundle, CFSTR(kPlugIn_Icon), NULL, NULL); 2862 FailWithAction(theURL == NULL, theAnswer = kAudioHardwareUnspecifiedError, Done, "BlackHole_GetDevicePropertyData: could not get the URL for kAudioDevicePropertyIcon"); 2863 *((CFURLRef*)outData) = theURL; 2864 *outDataSize = sizeof(CFURLRef); 2865 } 2866 break; 2867 2868 default: 2869 theAnswer = kAudioHardwareUnknownPropertyError; 2870 break; 2871 }; 2872 2873 Done: 2874 return theAnswer; 2875 } 2876 2877 static OSStatus BlackHole_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]) 2878 { 2879 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) 2880 2881 // declare the local variables 2882 OSStatus theAnswer = 0; 2883 Float64 theOldSampleRate; 2884 2885 // check the arguments 2886 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_SetDevicePropertyData: bad driver reference"); 2887 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetDevicePropertyData: no address"); 2888 FailWithAction(outNumberPropertiesChanged == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetDevicePropertyData: no place to return the number of properties that changed"); 2889 FailWithAction(outChangedAddresses == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetDevicePropertyData: no place to return the properties that changed"); 2890 FailWithAction(inObjectID != kObjectID_Device && inObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_SetDevicePropertyData: not the device object"); 2891 2892 // initialize the returned number of changed properties 2893 *outNumberPropertiesChanged = 0; 2894 2895 // Note that for each object, this driver implements all the required properties plus a few 2896 // extras that are useful but not required. There is more detailed commentary about each 2897 // property in the BlackHole_GetDevicePropertyData() method. 2898 switch(inAddress->mSelector) 2899 { 2900 case kAudioDevicePropertyNominalSampleRate: 2901 // Changing the sample rate needs to be handled via the 2902 // RequestConfigChange/PerformConfigChange machinery. 2903 2904 // check the arguments 2905 FailWithAction(inDataSize != sizeof(Float64), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_SetDevicePropertyData: wrong size for the data for kAudioDevicePropertyNominalSampleRate"); 2906 FailWithAction(!is_valid_sample_rate(*(const Float64*)inData), theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetDevicePropertyData: unsupported value for kAudioDevicePropertyNominalSampleRate"); 2907 2908 // make sure that the new value is different than the old value 2909 pthread_mutex_lock(&gPlugIn_StateMutex); 2910 theOldSampleRate = gDevice_SampleRate; 2911 gDevice_RequestedSampleRate = *((const Float64*)inData); 2912 pthread_mutex_unlock(&gPlugIn_StateMutex); 2913 if(*((const Float64*)inData) != theOldSampleRate) 2914 { 2915 // we dispatch this so that the change can happen asynchronously 2916 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ gPlugIn_Host->RequestDeviceConfigurationChange(gPlugIn_Host, kObjectID_Device, ChangeAction_SetSampleRate, NULL); }); 2917 } 2918 break; 2919 2920 default: 2921 theAnswer = kAudioHardwareUnknownPropertyError; 2922 break; 2923 }; 2924 2925 Done: 2926 return theAnswer; 2927 } 2928 2929 #pragma mark Stream Property Operations 2930 2931 static Boolean BlackHole_HasStreamProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) 2932 { 2933 // This method returns whether or not the given object has the given property. 2934 2935 #pragma unused(inClientProcessID) 2936 2937 // declare the local variables 2938 Boolean theAnswer = false; 2939 2940 // check the arguments 2941 FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "BlackHole_HasStreamProperty: bad driver reference"); 2942 FailIf(inAddress == NULL, Done, "BlackHole_HasStreamProperty: no address"); 2943 FailIf((inObjectID != kObjectID_Stream_Input) && (inObjectID != kObjectID_Stream_Output), Done, "BlackHole_HasStreamProperty: not a stream object"); 2944 2945 // Note that for each object, this driver implements all the required properties plus a few 2946 // extras that are useful but not required. There is more detailed commentary about each 2947 // property in the BlackHole_GetStreamPropertyData() method. 2948 switch(inAddress->mSelector) 2949 { 2950 case kAudioObjectPropertyBaseClass: 2951 case kAudioObjectPropertyClass: 2952 case kAudioObjectPropertyOwner: 2953 case kAudioObjectPropertyOwnedObjects: 2954 case kAudioStreamPropertyIsActive: 2955 case kAudioStreamPropertyDirection: 2956 case kAudioStreamPropertyTerminalType: 2957 case kAudioStreamPropertyStartingChannel: 2958 case kAudioStreamPropertyLatency: 2959 case kAudioStreamPropertyVirtualFormat: 2960 case kAudioStreamPropertyPhysicalFormat: 2961 case kAudioStreamPropertyAvailableVirtualFormats: 2962 case kAudioStreamPropertyAvailablePhysicalFormats: 2963 theAnswer = true; 2964 break; 2965 }; 2966 2967 Done: 2968 return theAnswer; 2969 } 2970 2971 static OSStatus BlackHole_IsStreamPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) 2972 { 2973 // This method returns whether or not the given property on the object can have its value 2974 // changed. 2975 2976 #pragma unused(inClientProcessID) 2977 2978 // declare the local variables 2979 OSStatus theAnswer = 0; 2980 2981 // check the arguments 2982 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_IsStreamPropertySettable: bad driver reference"); 2983 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsStreamPropertySettable: no address"); 2984 FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsStreamPropertySettable: no place to put the return value"); 2985 FailWithAction((inObjectID != kObjectID_Stream_Input) && (inObjectID != kObjectID_Stream_Output), theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_IsStreamPropertySettable: not a stream object"); 2986 2987 // Note that for each object, this driver implements all the required properties plus a few 2988 // extras that are useful but not required. There is more detailed commentary about each 2989 // property in the BlackHole_GetStreamPropertyData() method. 2990 switch(inAddress->mSelector) 2991 { 2992 case kAudioObjectPropertyBaseClass: 2993 case kAudioObjectPropertyClass: 2994 case kAudioObjectPropertyOwner: 2995 case kAudioObjectPropertyOwnedObjects: 2996 case kAudioStreamPropertyDirection: 2997 case kAudioStreamPropertyTerminalType: 2998 case kAudioStreamPropertyStartingChannel: 2999 case kAudioStreamPropertyLatency: 3000 case kAudioStreamPropertyAvailableVirtualFormats: 3001 case kAudioStreamPropertyAvailablePhysicalFormats: 3002 *outIsSettable = false; 3003 break; 3004 3005 case kAudioStreamPropertyIsActive: 3006 case kAudioStreamPropertyVirtualFormat: 3007 case kAudioStreamPropertyPhysicalFormat: 3008 *outIsSettable = true; 3009 break; 3010 3011 default: 3012 theAnswer = kAudioHardwareUnknownPropertyError; 3013 break; 3014 }; 3015 3016 Done: 3017 return theAnswer; 3018 } 3019 3020 static OSStatus BlackHole_GetStreamPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) 3021 { 3022 // This method returns the byte size of the property's data. 3023 3024 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) 3025 3026 // declare the local variables 3027 OSStatus theAnswer = 0; 3028 3029 // check the arguments 3030 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetStreamPropertyDataSize: bad driver reference"); 3031 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetStreamPropertyDataSize: no address"); 3032 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetStreamPropertyDataSize: no place to put the return value"); 3033 FailWithAction((inObjectID != kObjectID_Stream_Input) && (inObjectID != kObjectID_Stream_Output), theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetStreamPropertyDataSize: not a stream object"); 3034 3035 // Note that for each object, this driver implements all the required properties plus a few 3036 // extras that are useful but not required. There is more detailed commentary about each 3037 // property in the BlackHole_GetStreamPropertyData() method. 3038 switch(inAddress->mSelector) 3039 { 3040 case kAudioObjectPropertyBaseClass: 3041 *outDataSize = sizeof(AudioClassID); 3042 break; 3043 3044 case kAudioObjectPropertyClass: 3045 *outDataSize = sizeof(AudioClassID); 3046 break; 3047 3048 case kAudioObjectPropertyOwner: 3049 *outDataSize = sizeof(AudioObjectID); 3050 break; 3051 3052 case kAudioObjectPropertyOwnedObjects: 3053 *outDataSize = 0 * sizeof(AudioObjectID); 3054 break; 3055 3056 case kAudioStreamPropertyIsActive: 3057 *outDataSize = sizeof(UInt32); 3058 break; 3059 3060 case kAudioStreamPropertyDirection: 3061 *outDataSize = sizeof(UInt32); 3062 break; 3063 3064 case kAudioStreamPropertyTerminalType: 3065 *outDataSize = sizeof(UInt32); 3066 break; 3067 3068 case kAudioStreamPropertyStartingChannel: 3069 *outDataSize = sizeof(UInt32); 3070 break; 3071 3072 case kAudioStreamPropertyLatency: 3073 *outDataSize = sizeof(UInt32); 3074 break; 3075 3076 case kAudioStreamPropertyVirtualFormat: 3077 case kAudioStreamPropertyPhysicalFormat: 3078 *outDataSize = sizeof(AudioStreamBasicDescription); 3079 break; 3080 3081 case kAudioStreamPropertyAvailableVirtualFormats: 3082 case kAudioStreamPropertyAvailablePhysicalFormats: 3083 *outDataSize = kDevice_SampleRatesSize * sizeof(AudioStreamRangedDescription); 3084 break; 3085 3086 default: 3087 theAnswer = kAudioHardwareUnknownPropertyError; 3088 break; 3089 }; 3090 3091 Done: 3092 return theAnswer; 3093 } 3094 3095 static OSStatus BlackHole_GetStreamPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) 3096 { 3097 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) 3098 3099 // declare the local variables 3100 OSStatus theAnswer = 0; 3101 UInt32 theNumberItemsToFetch; 3102 3103 // check the arguments 3104 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetStreamPropertyData: bad driver reference"); 3105 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetStreamPropertyData: no address"); 3106 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetStreamPropertyData: no place to put the return value size"); 3107 FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetStreamPropertyData: no place to put the return value"); 3108 FailWithAction((inObjectID != kObjectID_Stream_Input) && (inObjectID != kObjectID_Stream_Output), theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetStreamPropertyData: not a stream object"); 3109 3110 // Note that for each object, this driver implements all the required properties plus a few 3111 // extras that are useful but not required. 3112 // 3113 // Also, since most of the data that will get returned is static, there are few instances where 3114 // it is necessary to lock the state mutex. 3115 switch(inAddress->mSelector) 3116 { 3117 case kAudioObjectPropertyBaseClass: 3118 // The base class for kAudioStreamClassID is kAudioObjectClassID 3119 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetStreamPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the stream"); 3120 *((AudioClassID*)outData) = kAudioObjectClassID; 3121 *outDataSize = sizeof(AudioClassID); 3122 break; 3123 3124 case kAudioObjectPropertyClass: 3125 // The class is always kAudioStreamClassID for streams created by drivers 3126 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetStreamPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the stream"); 3127 *((AudioClassID*)outData) = kAudioStreamClassID; 3128 *outDataSize = sizeof(AudioClassID); 3129 break; 3130 3131 case kAudioObjectPropertyOwner: 3132 // The stream's owner is the device object 3133 FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetStreamPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the stream"); 3134 *((AudioObjectID*)outData) = kObjectID_Device; 3135 *outDataSize = sizeof(AudioObjectID); 3136 break; 3137 3138 case kAudioObjectPropertyOwnedObjects: 3139 // Streams do not own any objects 3140 *outDataSize = 0 * sizeof(AudioObjectID); 3141 break; 3142 3143 case kAudioStreamPropertyIsActive: 3144 // This property tells the device whether or not the given stream is going to 3145 // be used for IO. Note that we need to take the state lock to examine this 3146 // value. 3147 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyIsActive for the stream"); 3148 pthread_mutex_lock(&gPlugIn_StateMutex); 3149 *((UInt32*)outData) = (inObjectID == kObjectID_Stream_Input) ? gStream_Input_IsActive : gStream_Output_IsActive; 3150 pthread_mutex_unlock(&gPlugIn_StateMutex); 3151 *outDataSize = sizeof(UInt32); 3152 break; 3153 3154 case kAudioStreamPropertyDirection: 3155 // This returns whether the stream is an input stream or an output stream. 3156 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyDirection for the stream"); 3157 *((UInt32*)outData) = (inObjectID == kObjectID_Stream_Input) ? 1 : 0; 3158 *outDataSize = sizeof(UInt32); 3159 break; 3160 3161 case kAudioStreamPropertyTerminalType: 3162 // This returns a value that indicates what is at the other end of the stream 3163 // such as a speaker or headphones, or a microphone. Values for this property 3164 // are defined in <CoreAudio/AudioHardwareBase.h> 3165 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyTerminalType for the stream"); 3166 *((UInt32*)outData) = (inObjectID == kObjectID_Stream_Input) ? kAudioStreamTerminalTypeMicrophone : kAudioStreamTerminalTypeSpeaker; 3167 *outDataSize = sizeof(UInt32); 3168 break; 3169 3170 case kAudioStreamPropertyStartingChannel: 3171 // This property returns the absolute channel number for the first channel in 3172 // the stream. For example, if a device has two output streams with two 3173 // channels each, then the starting channel number for the first stream is 1 3174 // and the starting channel number fo the second stream is 3. 3175 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyStartingChannel for the stream"); 3176 *((UInt32*)outData) = 1; 3177 *outDataSize = sizeof(UInt32); 3178 break; 3179 3180 case kAudioStreamPropertyLatency: 3181 // This property returns any additional presentation latency the stream has. 3182 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyStartingChannel for the stream"); 3183 *((UInt32*)outData) = kLatency_Frame_Size; 3184 *outDataSize = sizeof(UInt32); 3185 break; 3186 3187 case kAudioStreamPropertyVirtualFormat: 3188 case kAudioStreamPropertyPhysicalFormat: 3189 // This returns the current format of the stream in an 3190 // AudioStreamBasicDescription. Note that we need to hold the state lock to get 3191 // this value. 3192 // Note that for devices that don't override the mix operation, the virtual 3193 // format has to be the same as the physical format. 3194 FailWithAction(inDataSize < sizeof(AudioStreamBasicDescription), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetStreamPropertyData: not enough space for the return value of kAudioStreamPropertyVirtualFormat for the stream"); 3195 pthread_mutex_lock(&gPlugIn_StateMutex); 3196 ((AudioStreamBasicDescription*)outData)->mSampleRate = gDevice_SampleRate; 3197 ((AudioStreamBasicDescription*)outData)->mFormatID = kAudioFormatLinearPCM; 3198 ((AudioStreamBasicDescription*)outData)->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; 3199 ((AudioStreamBasicDescription*)outData)->mBytesPerPacket = kBytes_Per_Channel * kNumber_Of_Channels; 3200 ((AudioStreamBasicDescription*)outData)->mFramesPerPacket = 1; 3201 ((AudioStreamBasicDescription*)outData)->mBytesPerFrame = kBytes_Per_Channel * kNumber_Of_Channels; 3202 ((AudioStreamBasicDescription*)outData)->mChannelsPerFrame = kNumber_Of_Channels; 3203 ((AudioStreamBasicDescription*)outData)->mBitsPerChannel = kBits_Per_Channel; 3204 pthread_mutex_unlock(&gPlugIn_StateMutex); 3205 *outDataSize = sizeof(AudioStreamBasicDescription); 3206 break; 3207 3208 case kAudioStreamPropertyAvailableVirtualFormats: 3209 case kAudioStreamPropertyAvailablePhysicalFormats: 3210 // This returns an array of AudioStreamRangedDescriptions that describe what 3211 // formats are supported. 3212 3213 // Calculate the number of items that have been requested. Note that this 3214 // number is allowed to be smaller than the actual size of the list. In such 3215 // case, only that number of items will be returned 3216 theNumberItemsToFetch = inDataSize / sizeof(AudioStreamRangedDescription); 3217 3218 // clamp it to the number of items we have 3219 if(theNumberItemsToFetch > kDevice_SampleRatesSize) 3220 { 3221 theNumberItemsToFetch = kDevice_SampleRatesSize; 3222 } 3223 3224 // fill out the return array 3225 for(UInt32 i = 0; i < theNumberItemsToFetch; i++) 3226 { 3227 ((AudioStreamRangedDescription*)outData)[i].mFormat.mSampleRate = kDevice_SampleRates[i]; 3228 ((AudioStreamRangedDescription*)outData)[i].mFormat.mFormatID = kAudioFormatLinearPCM; 3229 ((AudioStreamRangedDescription*)outData)[i].mFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; 3230 ((AudioStreamRangedDescription*)outData)[i].mFormat.mBytesPerPacket = kBytes_Per_Frame; 3231 ((AudioStreamRangedDescription*)outData)[i].mFormat.mFramesPerPacket = 1; 3232 ((AudioStreamRangedDescription*)outData)[i].mFormat.mBytesPerFrame = kBytes_Per_Frame; 3233 ((AudioStreamRangedDescription*)outData)[i].mFormat.mChannelsPerFrame = kNumber_Of_Channels; 3234 ((AudioStreamRangedDescription*)outData)[i].mFormat.mBitsPerChannel = kBits_Per_Channel; 3235 ((AudioStreamRangedDescription*)outData)[i].mSampleRateRange.mMinimum = kDevice_SampleRates[i]; 3236 ((AudioStreamRangedDescription*)outData)[i].mSampleRateRange.mMaximum = kDevice_SampleRates[i]; 3237 } 3238 3239 // report how much we wrote 3240 *outDataSize = theNumberItemsToFetch * sizeof(AudioStreamRangedDescription); 3241 break; 3242 3243 default: 3244 theAnswer = kAudioHardwareUnknownPropertyError; 3245 break; 3246 }; 3247 3248 Done: 3249 return theAnswer; 3250 } 3251 3252 static OSStatus BlackHole_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]) 3253 { 3254 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) 3255 3256 // declare the local variables 3257 OSStatus theAnswer = 0; 3258 Float64 theOldSampleRate; 3259 3260 // check the arguments 3261 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_SetStreamPropertyData: bad driver reference"); 3262 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetStreamPropertyData: no address"); 3263 FailWithAction(outNumberPropertiesChanged == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetStreamPropertyData: no place to return the number of properties that changed"); 3264 FailWithAction(outChangedAddresses == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetStreamPropertyData: no place to return the properties that changed"); 3265 FailWithAction((inObjectID != kObjectID_Stream_Input) && (inObjectID != kObjectID_Stream_Output), theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_SetStreamPropertyData: not a stream object"); 3266 3267 // initialize the returned number of changed properties 3268 *outNumberPropertiesChanged = 0; 3269 3270 // Note that for each object, this driver implements all the required properties plus a few 3271 // extras that are useful but not required. There is more detailed commentary about each 3272 // property in the BlackHole_GetStreamPropertyData() method. 3273 switch(inAddress->mSelector) 3274 { 3275 case kAudioStreamPropertyIsActive: 3276 // Changing the active state of a stream doesn't affect IO or change the structure 3277 // so we can just save the state and send the notification. 3278 FailWithAction(inDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_SetStreamPropertyData: wrong size for the data for kAudioDevicePropertyNominalSampleRate"); 3279 pthread_mutex_lock(&gPlugIn_StateMutex); 3280 if(inObjectID == kObjectID_Stream_Input) 3281 { 3282 if(gStream_Input_IsActive != (*((const UInt32*)inData) != 0)) 3283 { 3284 gStream_Input_IsActive = *((const UInt32*)inData) != 0; 3285 *outNumberPropertiesChanged = 1; 3286 outChangedAddresses[0].mSelector = kAudioStreamPropertyIsActive; 3287 outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; 3288 outChangedAddresses[0].mElement = kAudioObjectPropertyElementMain; 3289 } 3290 } 3291 else 3292 { 3293 if(gStream_Output_IsActive != (*((const UInt32*)inData) != 0)) 3294 { 3295 gStream_Output_IsActive = *((const UInt32*)inData) != 0; 3296 *outNumberPropertiesChanged = 1; 3297 outChangedAddresses[0].mSelector = kAudioStreamPropertyIsActive; 3298 outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; 3299 outChangedAddresses[0].mElement = kAudioObjectPropertyElementMain; 3300 } 3301 } 3302 pthread_mutex_unlock(&gPlugIn_StateMutex); 3303 break; 3304 3305 case kAudioStreamPropertyVirtualFormat: 3306 case kAudioStreamPropertyPhysicalFormat: 3307 // Changing the stream format needs to be handled via the 3308 // RequestConfigChange/PerformConfigChange machinery. Note that because this 3309 // device only supports 2 channel 32 bit float data, the only thing that can 3310 // change is the sample rate. 3311 FailWithAction(inDataSize != sizeof(AudioStreamBasicDescription), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_SetStreamPropertyData: wrong size for the data for kAudioStreamPropertyPhysicalFormat"); 3312 FailWithAction(((const AudioStreamBasicDescription*)inData)->mFormatID != kAudioFormatLinearPCM, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "BlackHole_SetStreamPropertyData: unsupported format ID for kAudioStreamPropertyPhysicalFormat"); 3313 FailWithAction(((const AudioStreamBasicDescription*)inData)->mFormatFlags != (kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked), theAnswer = kAudioDeviceUnsupportedFormatError, Done, "BlackHole_SetStreamPropertyData: unsupported format flags for kAudioStreamPropertyPhysicalFormat"); 3314 FailWithAction(((const AudioStreamBasicDescription*)inData)->mBytesPerPacket != kBytes_Per_Frame, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "BlackHole_SetStreamPropertyData: unsupported bytes per packet for kAudioStreamPropertyPhysicalFormat"); 3315 FailWithAction(((const AudioStreamBasicDescription*)inData)->mFramesPerPacket != 1, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "BlackHole_SetStreamPropertyData: unsupported frames per packet for kAudioStreamPropertyPhysicalFormat"); 3316 FailWithAction(((const AudioStreamBasicDescription*)inData)->mBytesPerFrame != kBytes_Per_Frame, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "BlackHole_SetStreamPropertyData: unsupported bytes per frame for kAudioStreamPropertyPhysicalFormat"); 3317 FailWithAction(((const AudioStreamBasicDescription*)inData)->mChannelsPerFrame != kNumber_Of_Channels, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "BlackHole_SetStreamPropertyData: unsupported channels per frame for kAudioStreamPropertyPhysicalFormat"); 3318 FailWithAction(((const AudioStreamBasicDescription*)inData)->mBitsPerChannel != kBits_Per_Channel, theAnswer = kAudioDeviceUnsupportedFormatError, Done, "BlackHole_SetStreamPropertyData: unsupported bits per channel for kAudioStreamPropertyPhysicalFormat"); 3319 FailWithAction(!is_valid_sample_rate(((const AudioStreamBasicDescription*)inData)->mSampleRate), theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetStreamPropertyData: unsupported sample rate for kAudioStreamPropertyPhysicalFormat"); 3320 3321 // If we made it this far, the requested format is something we support, so make sure the sample rate is actually different 3322 pthread_mutex_lock(&gPlugIn_StateMutex); 3323 theOldSampleRate = gDevice_SampleRate; 3324 gDevice_RequestedSampleRate = ((const AudioStreamBasicDescription*)inData)->mSampleRate; 3325 pthread_mutex_unlock(&gPlugIn_StateMutex); 3326 if(((const AudioStreamBasicDescription*)inData)->mSampleRate != theOldSampleRate) 3327 { 3328 // we dispatch this so that the change can happen asynchronously 3329 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ gPlugIn_Host->RequestDeviceConfigurationChange(gPlugIn_Host, kObjectID_Device, ChangeAction_SetSampleRate, NULL); }); 3330 } 3331 break; 3332 3333 default: 3334 theAnswer = kAudioHardwareUnknownPropertyError; 3335 break; 3336 }; 3337 3338 Done: 3339 return theAnswer; 3340 } 3341 3342 #pragma mark Control Property Operations 3343 3344 static Boolean BlackHole_HasControlProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress) 3345 { 3346 // This method returns whether or not the given object has the given property. 3347 3348 #pragma unused(inClientProcessID) 3349 3350 // declare the local variables 3351 Boolean theAnswer = false; 3352 3353 // check the arguments 3354 FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "BlackHole_HasControlProperty: bad driver reference"); 3355 FailIf(inAddress == NULL, Done, "BlackHole_HasControlProperty: no address"); 3356 3357 // Note that for each object, this driver implements all the required properties plus a few 3358 // extras that are useful but not required. There is more detailed commentary about each 3359 // property in the BlackHole_GetControlPropertyData() method. 3360 switch(inObjectID) 3361 { 3362 case kObjectID_Volume_Input_Master: 3363 case kObjectID_Volume_Output_Master: 3364 switch(inAddress->mSelector) 3365 { 3366 case kAudioObjectPropertyBaseClass: 3367 case kAudioObjectPropertyClass: 3368 case kAudioObjectPropertyOwner: 3369 case kAudioObjectPropertyOwnedObjects: 3370 case kAudioControlPropertyScope: 3371 case kAudioControlPropertyElement: 3372 case kAudioLevelControlPropertyScalarValue: 3373 case kAudioLevelControlPropertyDecibelValue: 3374 case kAudioLevelControlPropertyDecibelRange: 3375 case kAudioLevelControlPropertyConvertScalarToDecibels: 3376 case kAudioLevelControlPropertyConvertDecibelsToScalar: 3377 theAnswer = true; 3378 break; 3379 }; 3380 break; 3381 3382 case kObjectID_Mute_Input_Master: 3383 case kObjectID_Mute_Output_Master: 3384 switch(inAddress->mSelector) 3385 { 3386 case kAudioObjectPropertyBaseClass: 3387 case kAudioObjectPropertyClass: 3388 case kAudioObjectPropertyOwner: 3389 case kAudioObjectPropertyOwnedObjects: 3390 case kAudioControlPropertyScope: 3391 case kAudioControlPropertyElement: 3392 case kAudioBooleanControlPropertyValue: 3393 theAnswer = true; 3394 break; 3395 }; 3396 break; 3397 3398 case kObjectID_Pitch_Adjust: 3399 switch(inAddress->mSelector) 3400 { 3401 case kAudioObjectPropertyBaseClass: 3402 case kAudioObjectPropertyClass: 3403 case kAudioObjectPropertyOwner: 3404 case kAudioObjectPropertyOwnedObjects: 3405 case kAudioControlPropertyScope: 3406 case kAudioControlPropertyElement: 3407 case kAudioStereoPanControlPropertyValue: 3408 theAnswer = true; 3409 break; 3410 }; 3411 break; 3412 3413 case kObjectID_ClockSource: 3414 switch(inAddress->mSelector) 3415 { 3416 case kAudioObjectPropertyBaseClass: 3417 case kAudioObjectPropertyClass: 3418 case kAudioObjectPropertyOwner: 3419 case kAudioObjectPropertyOwnedObjects: 3420 case kAudioControlPropertyScope: 3421 case kAudioControlPropertyElement: 3422 case kAudioSelectorControlPropertyCurrentItem: 3423 case kAudioSelectorControlPropertyAvailableItems: 3424 case kAudioSelectorControlPropertyItemName: 3425 theAnswer = true; 3426 break; 3427 }; 3428 break; 3429 }; 3430 3431 Done: 3432 return theAnswer; 3433 } 3434 3435 static OSStatus BlackHole_IsControlPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable) 3436 { 3437 // This method returns whether or not the given property on the object can have its value 3438 // changed. 3439 3440 #pragma unused(inClientProcessID) 3441 3442 // declare the local variables 3443 OSStatus theAnswer = 0; 3444 3445 // check the arguments 3446 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_IsControlPropertySettable: bad driver reference"); 3447 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsControlPropertySettable: no address"); 3448 FailWithAction(outIsSettable == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_IsControlPropertySettable: no place to put the return value"); 3449 3450 // Note that for each object, this driver implements all the required properties plus a few 3451 // extras that are useful but not required. There is more detailed commentary about each 3452 // property in the BlackHole_GetControlPropertyData() method. 3453 switch(inObjectID) 3454 { 3455 case kObjectID_Volume_Input_Master: 3456 case kObjectID_Volume_Output_Master: 3457 switch(inAddress->mSelector) 3458 { 3459 case kAudioObjectPropertyBaseClass: 3460 case kAudioObjectPropertyClass: 3461 case kAudioObjectPropertyOwner: 3462 case kAudioObjectPropertyOwnedObjects: 3463 case kAudioControlPropertyScope: 3464 case kAudioControlPropertyElement: 3465 case kAudioLevelControlPropertyDecibelRange: 3466 case kAudioLevelControlPropertyConvertScalarToDecibels: 3467 case kAudioLevelControlPropertyConvertDecibelsToScalar: 3468 *outIsSettable = false; 3469 break; 3470 3471 case kAudioLevelControlPropertyScalarValue: 3472 case kAudioLevelControlPropertyDecibelValue: 3473 *outIsSettable = true; 3474 break; 3475 3476 default: 3477 theAnswer = kAudioHardwareUnknownPropertyError; 3478 break; 3479 }; 3480 break; 3481 3482 case kObjectID_Mute_Input_Master: 3483 case kObjectID_Mute_Output_Master: 3484 switch(inAddress->mSelector) 3485 { 3486 case kAudioObjectPropertyBaseClass: 3487 case kAudioObjectPropertyClass: 3488 case kAudioObjectPropertyOwner: 3489 case kAudioObjectPropertyOwnedObjects: 3490 case kAudioControlPropertyScope: 3491 case kAudioControlPropertyElement: 3492 *outIsSettable = false; 3493 break; 3494 3495 case kAudioBooleanControlPropertyValue: 3496 *outIsSettable = true; 3497 break; 3498 3499 default: 3500 theAnswer = kAudioHardwareUnknownPropertyError; 3501 break; 3502 }; 3503 break; 3504 3505 case kObjectID_Pitch_Adjust: 3506 switch(inAddress->mSelector) 3507 { 3508 case kAudioObjectPropertyBaseClass: 3509 case kAudioObjectPropertyClass: 3510 case kAudioObjectPropertyOwner: 3511 case kAudioObjectPropertyOwnedObjects: 3512 case kAudioControlPropertyScope: 3513 case kAudioControlPropertyElement: 3514 *outIsSettable = false; 3515 break; 3516 3517 case kAudioStereoPanControlPropertyValue: 3518 *outIsSettable = true; 3519 break; 3520 3521 default: 3522 theAnswer = kAudioHardwareUnknownPropertyError; 3523 break; 3524 }; 3525 break; 3526 case kObjectID_ClockSource: 3527 switch(inAddress->mSelector) 3528 { 3529 case kAudioObjectPropertyBaseClass: 3530 case kAudioObjectPropertyClass: 3531 case kAudioObjectPropertyOwner: 3532 case kAudioObjectPropertyOwnedObjects: 3533 case kAudioControlPropertyScope: 3534 case kAudioControlPropertyElement: 3535 *outIsSettable = false; 3536 break; 3537 3538 case kAudioSelectorControlPropertyCurrentItem: 3539 *outIsSettable = true; 3540 break; 3541 3542 default: 3543 theAnswer = kAudioHardwareUnknownPropertyError; 3544 break; 3545 }; 3546 break; 3547 3548 default: 3549 theAnswer = kAudioHardwareBadObjectError; 3550 break; 3551 }; 3552 3553 Done: 3554 return theAnswer; 3555 } 3556 3557 static OSStatus BlackHole_GetControlPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize) 3558 { 3559 // This method returns the byte size of the property's data. 3560 3561 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) 3562 3563 // declare the local variables 3564 OSStatus theAnswer = 0; 3565 3566 // check the arguments 3567 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetControlPropertyDataSize: bad driver reference"); 3568 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetControlPropertyDataSize: no address"); 3569 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetControlPropertyDataSize: no place to put the return value"); 3570 3571 // Note that for each object, this driver implements all the required properties plus a few 3572 // extras that are useful but not required. There is more detailed commentary about each 3573 // property in the BlackHole_GetControlPropertyData() method. 3574 switch(inObjectID) 3575 { 3576 case kObjectID_Volume_Input_Master: 3577 case kObjectID_Volume_Output_Master: 3578 switch(inAddress->mSelector) 3579 { 3580 case kAudioObjectPropertyBaseClass: 3581 *outDataSize = sizeof(AudioClassID); 3582 break; 3583 3584 case kAudioObjectPropertyClass: 3585 *outDataSize = sizeof(AudioClassID); 3586 break; 3587 3588 case kAudioObjectPropertyOwner: 3589 *outDataSize = sizeof(AudioObjectID); 3590 break; 3591 3592 case kAudioObjectPropertyOwnedObjects: 3593 *outDataSize = 0 * sizeof(AudioObjectID); 3594 break; 3595 3596 case kAudioControlPropertyScope: 3597 *outDataSize = sizeof(AudioObjectPropertyScope); 3598 break; 3599 3600 case kAudioControlPropertyElement: 3601 *outDataSize = sizeof(AudioObjectPropertyElement); 3602 break; 3603 3604 case kAudioLevelControlPropertyScalarValue: 3605 *outDataSize = sizeof(Float32); 3606 break; 3607 3608 case kAudioLevelControlPropertyDecibelValue: 3609 *outDataSize = sizeof(Float32); 3610 break; 3611 3612 case kAudioLevelControlPropertyDecibelRange: 3613 *outDataSize = sizeof(AudioValueRange); 3614 break; 3615 3616 case kAudioLevelControlPropertyConvertScalarToDecibels: 3617 *outDataSize = sizeof(Float32); 3618 break; 3619 3620 case kAudioLevelControlPropertyConvertDecibelsToScalar: 3621 *outDataSize = sizeof(Float32); 3622 break; 3623 3624 default: 3625 theAnswer = kAudioHardwareUnknownPropertyError; 3626 break; 3627 }; 3628 break; 3629 3630 case kObjectID_Mute_Input_Master: 3631 case kObjectID_Mute_Output_Master: 3632 switch(inAddress->mSelector) 3633 { 3634 case kAudioObjectPropertyBaseClass: 3635 *outDataSize = sizeof(AudioClassID); 3636 break; 3637 3638 case kAudioObjectPropertyClass: 3639 *outDataSize = sizeof(AudioClassID); 3640 break; 3641 3642 case kAudioObjectPropertyOwner: 3643 *outDataSize = sizeof(AudioObjectID); 3644 break; 3645 3646 case kAudioObjectPropertyOwnedObjects: 3647 *outDataSize = 0 * sizeof(AudioObjectID); 3648 break; 3649 3650 case kAudioControlPropertyScope: 3651 *outDataSize = sizeof(AudioObjectPropertyScope); 3652 break; 3653 3654 case kAudioControlPropertyElement: 3655 *outDataSize = sizeof(AudioObjectPropertyElement); 3656 break; 3657 3658 case kAudioBooleanControlPropertyValue: 3659 *outDataSize = sizeof(UInt32); 3660 break; 3661 3662 default: 3663 theAnswer = kAudioHardwareUnknownPropertyError; 3664 break; 3665 }; 3666 break; 3667 3668 case kObjectID_Pitch_Adjust: 3669 switch(inAddress->mSelector) 3670 { 3671 case kAudioObjectPropertyBaseClass: 3672 *outDataSize = sizeof(AudioClassID); 3673 break; 3674 3675 case kAudioObjectPropertyClass: 3676 *outDataSize = sizeof(AudioClassID); 3677 break; 3678 3679 case kAudioObjectPropertyOwner: 3680 *outDataSize = sizeof(AudioObjectID); 3681 break; 3682 3683 case kAudioObjectPropertyOwnedObjects: 3684 *outDataSize = 0 * sizeof(AudioObjectID); 3685 break; 3686 3687 case kAudioControlPropertyScope: 3688 *outDataSize = sizeof(AudioObjectPropertyScope); 3689 break; 3690 3691 case kAudioControlPropertyElement: 3692 *outDataSize = sizeof(AudioObjectPropertyElement); 3693 break; 3694 3695 case kAudioStereoPanControlPropertyValue: 3696 *outDataSize = sizeof(Float32); 3697 break; 3698 3699 default: 3700 theAnswer = kAudioHardwareUnknownPropertyError; 3701 break; 3702 }; 3703 break; 3704 3705 case kObjectID_ClockSource: 3706 switch(inAddress->mSelector) 3707 { 3708 case kAudioObjectPropertyBaseClass: 3709 *outDataSize = sizeof(AudioClassID); 3710 break; 3711 case kAudioObjectPropertyClass: 3712 *outDataSize = sizeof(AudioClassID); 3713 break; 3714 case kAudioObjectPropertyOwner: 3715 *outDataSize = sizeof(AudioObjectID); 3716 break; 3717 case kAudioObjectPropertyOwnedObjects: 3718 *outDataSize = 0 * sizeof(AudioObjectID); 3719 break; 3720 case kAudioControlPropertyScope: 3721 *outDataSize = sizeof(AudioObjectPropertyScope); 3722 break; 3723 case kAudioControlPropertyElement: 3724 *outDataSize = sizeof(AudioObjectPropertyElement); 3725 break; 3726 3727 case kAudioSelectorControlPropertyCurrentItem: 3728 *outDataSize = sizeof(UInt32); 3729 break; 3730 3731 case kAudioSelectorControlPropertyAvailableItems: 3732 *outDataSize = kClockSource_NumberItems * sizeof(UInt32); 3733 break; 3734 case kAudioSelectorControlPropertyItemName: 3735 *outDataSize = sizeof(CFStringRef); 3736 break; 3737 default: 3738 theAnswer = kAudioHardwareUnknownPropertyError; 3739 break; 3740 }; 3741 break; 3742 default: 3743 theAnswer = kAudioHardwareBadObjectError; 3744 break; 3745 }; 3746 3747 Done: 3748 return theAnswer; 3749 } 3750 3751 static OSStatus BlackHole_GetControlPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData) 3752 { 3753 #pragma unused(inClientProcessID, inQualifierData, inQualifierDataSize) 3754 3755 // declare the local variables 3756 OSStatus theAnswer = 0; 3757 UInt32 theNumberItemsToFetch; 3758 UInt32 theItemIndex; 3759 3760 // check the arguments 3761 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetControlPropertyData: bad driver reference"); 3762 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetControlPropertyData: no address"); 3763 FailWithAction(outDataSize == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetControlPropertyData: no place to put the return value size"); 3764 FailWithAction(outData == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetControlPropertyData: no place to put the return value"); 3765 3766 // Note that for each object, this driver implements all the required properties plus a few 3767 // extras that are useful but not required. 3768 // 3769 // Also, since most of the data that will get returned is static, there are few instances where 3770 // it is necessary to lock the state mutex. 3771 switch(inObjectID) 3772 { 3773 case kObjectID_Volume_Input_Master: 3774 case kObjectID_Volume_Output_Master: 3775 switch(inAddress->mSelector) 3776 { 3777 case kAudioObjectPropertyBaseClass: 3778 // The base class for kAudioVolumeControlClassID is kAudioLevelControlClassID 3779 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the volume control"); 3780 *((AudioClassID*)outData) = kAudioLevelControlClassID; 3781 *outDataSize = sizeof(AudioClassID); 3782 break; 3783 3784 case kAudioObjectPropertyClass: 3785 // Volume controls are of the class, kAudioVolumeControlClassID 3786 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the volume control"); 3787 *((AudioClassID*)outData) = kAudioVolumeControlClassID; 3788 *outDataSize = sizeof(AudioClassID); 3789 break; 3790 3791 case kAudioObjectPropertyOwner: 3792 // The control's owner is the device object 3793 FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the volume control"); 3794 *((AudioObjectID*)outData) = kObjectID_Device; 3795 *outDataSize = sizeof(AudioObjectID); 3796 break; 3797 3798 case kAudioObjectPropertyOwnedObjects: 3799 // Controls do not own any objects 3800 *outDataSize = 0 * sizeof(AudioObjectID); 3801 break; 3802 3803 case kAudioControlPropertyScope: 3804 // This property returns the scope that the control is attached to. 3805 FailWithAction(inDataSize < sizeof(AudioObjectPropertyScope), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyScope for the volume control"); 3806 *((AudioObjectPropertyScope*)outData) = (inObjectID == kObjectID_Volume_Input_Master) ? kAudioObjectPropertyScopeInput : kAudioObjectPropertyScopeOutput; 3807 *outDataSize = sizeof(AudioObjectPropertyScope); 3808 break; 3809 3810 case kAudioControlPropertyElement: 3811 // This property returns the element that the control is attached to. 3812 FailWithAction(inDataSize < sizeof(AudioObjectPropertyElement), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyElement for the volume control"); 3813 *((AudioObjectPropertyElement*)outData) = kAudioObjectPropertyElementMain; 3814 *outDataSize = sizeof(AudioObjectPropertyElement); 3815 break; 3816 3817 case kAudioLevelControlPropertyScalarValue: 3818 // This returns the value of the control in the normalized range of 0 to 1. 3819 // Note that we need to take the state lock to examine the value. 3820 FailWithAction(inDataSize < sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioLevelControlPropertyScalarValue for the volume control"); 3821 pthread_mutex_lock(&gPlugIn_StateMutex); 3822 *((Float32*)outData) = volume_to_scalar(gVolume_Master_Value); 3823 pthread_mutex_unlock(&gPlugIn_StateMutex); 3824 *outDataSize = sizeof(Float32); 3825 break; 3826 3827 case kAudioLevelControlPropertyDecibelValue: 3828 // This returns the dB value of the control. 3829 // Note that we need to take the state lock to examine the value. 3830 FailWithAction(inDataSize < sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioLevelControlPropertyDecibelValue for the volume control"); 3831 pthread_mutex_lock(&gPlugIn_StateMutex); 3832 *((Float32*)outData) = gVolume_Master_Value; 3833 pthread_mutex_unlock(&gPlugIn_StateMutex); 3834 *((Float32*)outData) = volume_to_decibel(*((Float32*)outData)); 3835 3836 // report how much we wrote 3837 *outDataSize = sizeof(Float32); 3838 break; 3839 3840 case kAudioLevelControlPropertyDecibelRange: 3841 // This returns the dB range of the control. 3842 FailWithAction(inDataSize < sizeof(AudioValueRange), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioLevelControlPropertyDecibelRange for the volume control"); 3843 ((AudioValueRange*)outData)->mMinimum = kVolume_MinDB; 3844 ((AudioValueRange*)outData)->mMaximum = kVolume_MaxDB; 3845 *outDataSize = sizeof(AudioValueRange); 3846 break; 3847 3848 case kAudioLevelControlPropertyConvertScalarToDecibels: 3849 // This takes the scalar value in outData and converts it to dB. 3850 FailWithAction(inDataSize < sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioLevelControlPropertyDecibelValue for the volume control"); 3851 3852 // clamp the value to be between 0 and 1 3853 if(*((Float32*)outData) < 0.0) 3854 { 3855 *((Float32*)outData) = 0; 3856 } 3857 if(*((Float32*)outData) > 1.0) 3858 { 3859 *((Float32*)outData) = 1.0; 3860 } 3861 3862 // Note that we square the scalar value before converting to dB so as to 3863 // provide a better curve for the slider 3864 *((Float32*)outData) *= *((Float32*)outData); 3865 *((Float32*)outData) = kVolume_MinDB + (*((Float32*)outData) * (kVolume_MaxDB - kVolume_MinDB)); 3866 3867 // report how much we wrote 3868 *outDataSize = sizeof(Float32); 3869 break; 3870 3871 case kAudioLevelControlPropertyConvertDecibelsToScalar: 3872 // This takes the dB value in outData and converts it to scalar. 3873 FailWithAction(inDataSize < sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioLevelControlPropertyDecibelValue for the volume control"); 3874 3875 // clamp the value to be between kVolume_MinDB and kVolume_MaxDB 3876 if(*((Float32*)outData) < kVolume_MinDB) 3877 { 3878 *((Float32*)outData) = kVolume_MinDB; 3879 } 3880 if(*((Float32*)outData) > kVolume_MaxDB) 3881 { 3882 *((Float32*)outData) = kVolume_MaxDB; 3883 } 3884 3885 // Note that we square the scalar value before converting to dB so as to 3886 // provide a better curve for the slider. We undo that here. 3887 *((Float32*)outData) = *((Float32*)outData) - kVolume_MinDB; 3888 *((Float32*)outData) /= kVolume_MaxDB - kVolume_MinDB; 3889 *((Float32*)outData) = sqrtf(*((Float32*)outData)); 3890 3891 // report how much we wrote 3892 *outDataSize = sizeof(Float32); 3893 break; 3894 3895 default: 3896 theAnswer = kAudioHardwareUnknownPropertyError; 3897 break; 3898 }; 3899 break; 3900 3901 case kObjectID_Mute_Input_Master: 3902 case kObjectID_Mute_Output_Master: 3903 switch(inAddress->mSelector) 3904 { 3905 case kAudioObjectPropertyBaseClass: 3906 // The base class for kAudioMuteControlClassID is kAudioBooleanControlClassID 3907 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the mute control"); 3908 *((AudioClassID*)outData) = kAudioBooleanControlClassID; 3909 *outDataSize = sizeof(AudioClassID); 3910 break; 3911 3912 case kAudioObjectPropertyClass: 3913 // Mute controls are of the class, kAudioMuteControlClassID 3914 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the mute control"); 3915 *((AudioClassID*)outData) = kAudioMuteControlClassID; 3916 *outDataSize = sizeof(AudioClassID); 3917 break; 3918 3919 case kAudioObjectPropertyOwner: 3920 // The control's owner is the device object 3921 FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the mute control"); 3922 *((AudioObjectID*)outData) = kObjectID_Device; 3923 *outDataSize = sizeof(AudioObjectID); 3924 break; 3925 3926 case kAudioObjectPropertyOwnedObjects: 3927 // Controls do not own any objects 3928 *outDataSize = 0 * sizeof(AudioObjectID); 3929 break; 3930 3931 case kAudioControlPropertyScope: 3932 // This property returns the scope that the control is attached to. 3933 FailWithAction(inDataSize < sizeof(AudioObjectPropertyScope), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyScope for the mute control"); 3934 *((AudioObjectPropertyScope*)outData) = (inObjectID == kObjectID_Mute_Input_Master) ? kAudioObjectPropertyScopeInput : kAudioObjectPropertyScopeOutput; 3935 *outDataSize = sizeof(AudioObjectPropertyScope); 3936 break; 3937 3938 case kAudioControlPropertyElement: 3939 // This property returns the element that the control is attached to. 3940 FailWithAction(inDataSize < sizeof(AudioObjectPropertyElement), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyElement for the mute control"); 3941 *((AudioObjectPropertyElement*)outData) = kAudioObjectPropertyElementMain; 3942 *outDataSize = sizeof(AudioObjectPropertyElement); 3943 break; 3944 3945 case kAudioBooleanControlPropertyValue: 3946 // This returns the value of the mute control where 0 means that mute is off 3947 // and audio can be heard and 1 means that mute is on and audio cannot be heard. 3948 // Note that we need to take the state lock to examine this value. 3949 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioBooleanControlPropertyValue for the mute control"); 3950 pthread_mutex_lock(&gPlugIn_StateMutex); 3951 *((UInt32*)outData) = gMute_Master_Value ? 1 : 0; 3952 pthread_mutex_unlock(&gPlugIn_StateMutex); 3953 *outDataSize = sizeof(UInt32); 3954 break; 3955 3956 default: 3957 theAnswer = kAudioHardwareUnknownPropertyError; 3958 break; 3959 }; 3960 break; 3961 3962 case kObjectID_Pitch_Adjust: 3963 switch(inAddress->mSelector) 3964 { 3965 case kAudioObjectPropertyBaseClass: 3966 // The base class for kAudioMuteControlClassID is kAudioBooleanControlClassID 3967 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the pitch control"); 3968 *((AudioClassID*)outData) = kAudioStereoPanControlClassID; 3969 *outDataSize = sizeof(AudioClassID); 3970 break; 3971 3972 case kAudioObjectPropertyClass: 3973 // Level controls are of the class, kAudioLevelControlClassID 3974 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the pitch control"); 3975 *((AudioClassID*)outData) = kAudioStereoPanControlClassID; 3976 *outDataSize = sizeof(AudioClassID); 3977 break; 3978 3979 case kAudioObjectPropertyOwner: 3980 // The control's owner is the device object 3981 FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the pitch control"); 3982 *((AudioObjectID*)outData) = kObjectID_Device; 3983 *outDataSize = sizeof(AudioObjectID); 3984 break; 3985 3986 case kAudioObjectPropertyOwnedObjects: 3987 // Controls do not own any objects 3988 *outDataSize = 0 * sizeof(AudioObjectID); 3989 break; 3990 3991 case kAudioControlPropertyScope: 3992 // This property returns the scope that the control is attached to. 3993 FailWithAction(inDataSize < sizeof(AudioObjectPropertyScope), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyScope for the pitch control"); 3994 *((AudioObjectPropertyScope*)outData) = kAudioObjectPropertyScopeOutput; 3995 *outDataSize = sizeof(AudioObjectPropertyScope); 3996 break; 3997 3998 case kAudioControlPropertyElement: 3999 // This property returns the element that the control is attached to. 4000 FailWithAction(inDataSize < sizeof(AudioObjectPropertyElement), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyElement for the pitch control"); 4001 *((AudioObjectPropertyElement*)outData) = kAudioObjectPropertyElementMain; 4002 *outDataSize = sizeof(AudioObjectPropertyElement); 4003 break; 4004 4005 case kAudioStereoPanControlPropertyValue: 4006 // This returns the value of the pitch control. 4007 // Note that we need to take the state lock to examine this value. 4008 FailWithAction(inDataSize < sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioLevelControlScalarValue for the pitch control"); 4009 pthread_mutex_lock(&gPlugIn_StateMutex); 4010 *((Float32*)outData) = (inObjectID == kObjectID_Pitch_Adjust) ? gPitch_Adjust : 0.5; 4011 pthread_mutex_unlock(&gPlugIn_StateMutex); 4012 *outDataSize = sizeof(Float32); 4013 break; 4014 4015 default: 4016 theAnswer = kAudioHardwareUnknownPropertyError; 4017 break; 4018 }; 4019 break; 4020 case kObjectID_ClockSource: 4021 switch(inAddress->mSelector) 4022 { 4023 case kAudioObjectPropertyBaseClass: 4024 // The base class for kAudioDataSourceControlClassID is kAudioSelectorControlClassID 4025 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass for the data source control"); 4026 *((AudioClassID*)outData) = kAudioSelectorControlClassID; 4027 *outDataSize = sizeof(AudioClassID); 4028 break; 4029 4030 case kAudioObjectPropertyClass: 4031 // Data Source controls are of the class, kAudioDataSourceControlClassID 4032 FailWithAction(inDataSize < sizeof(AudioClassID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyClass for the data source control"); 4033 *((AudioClassID*)outData) = kAudioClockSourceControlClassID; 4034 *outDataSize = sizeof(AudioClassID); 4035 break; 4036 4037 case kAudioObjectPropertyOwner: 4038 // The control's owner is the device object 4039 FailWithAction(inDataSize < sizeof(AudioObjectID), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioObjectPropertyOwner for the data source control"); 4040 *((AudioObjectID*)outData) = kObjectID_Device; 4041 *outDataSize = sizeof(AudioObjectID); 4042 break; 4043 4044 case kAudioObjectPropertyOwnedObjects: 4045 // Controls do not own any objects 4046 *outDataSize = 0 * sizeof(AudioObjectID); 4047 break; 4048 4049 case kAudioControlPropertyScope: 4050 // This property returns the scope that the control is attached to. 4051 FailWithAction(inDataSize < sizeof(AudioObjectPropertyScope), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyScope for the data source control"); 4052 *((AudioObjectPropertyScope*)outData) = kAudioObjectPropertyScopeGlobal; 4053 *outDataSize = sizeof(AudioObjectPropertyScope); 4054 break; 4055 4056 case kAudioControlPropertyElement: 4057 // This property returns the element that the control is attached to. 4058 FailWithAction(inDataSize < sizeof(AudioObjectPropertyElement), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioControlPropertyElement for the data source control"); 4059 *((AudioObjectPropertyElement*)outData) = kAudioObjectPropertyElementMain; 4060 *outDataSize = sizeof(AudioObjectPropertyElement); 4061 break; 4062 4063 case kAudioSelectorControlPropertyCurrentItem: 4064 // This returns the value of the data source selector. 4065 // Note that we need to take the state lock to examine this value. 4066 FailWithAction(inDataSize < sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioSelectorControlPropertyCurrentItem for the data source control"); 4067 pthread_mutex_lock(&gPlugIn_StateMutex); 4068 *((UInt32*)outData) = gClockSource_Value; 4069 pthread_mutex_unlock(&gPlugIn_StateMutex); 4070 *outDataSize = sizeof(UInt32); 4071 break; 4072 4073 case kAudioSelectorControlPropertyAvailableItems: 4074 // This returns the IDs for all the items the data source control supports. 4075 4076 // Calculate the number of items that have been requested. Note that this 4077 // number is allowed to be smaller than the actual size of the list. In such 4078 // case, only that number of items will be returned 4079 theNumberItemsToFetch = inDataSize / sizeof(UInt32); 4080 4081 // clamp it to the number of items we have 4082 if(theNumberItemsToFetch > kClockSource_NumberItems) 4083 { 4084 theNumberItemsToFetch = kClockSource_NumberItems; 4085 } 4086 4087 // fill out the return array 4088 for(theItemIndex = 0; theItemIndex < theNumberItemsToFetch; ++theItemIndex) 4089 { 4090 ((UInt32*)outData)[theItemIndex] = theItemIndex; 4091 } 4092 4093 // report how much we wrote 4094 *outDataSize = theNumberItemsToFetch * sizeof(UInt32); 4095 4096 break; 4097 4098 case kAudioSelectorControlPropertyItemName: 4099 // This returns the user-readable name for the selector item in the qualifier 4100 FailWithAction(inDataSize < sizeof(CFStringRef), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: not enough space for the return value of kAudioSelectorControlPropertyItemName for the clock source control"); 4101 FailWithAction(inQualifierDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_GetControlPropertyData: wrong size for the qualifier of kAudioSelectorControlPropertyItemName for the clock source control"); 4102 FailWithAction(*((const UInt32*)inQualifierData) >= kClockSource_NumberItems, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_GetControlPropertyData: the item in the qualifier is not valid for kAudioSelectorControlPropertyItemName for the data source control"); 4103 if (*(UInt32*)inQualifierData == 0) { 4104 *(CFStringRef*)outData = CFSTR(kClockSource_InternalFixed); 4105 } 4106 else if (*(UInt32*)inQualifierData == 1) { 4107 *(CFStringRef*)outData = CFSTR(kClockSource_InternalAdjustable); 4108 } 4109 //else { 4110 // *(CFStringRef*)outData = CFSTR("Unknown"); 4111 //} 4112 *outDataSize = sizeof(CFStringRef); 4113 4114 break; 4115 4116 default: 4117 theAnswer = kAudioHardwareUnknownPropertyError; 4118 break; 4119 }; 4120 break; 4121 default: 4122 theAnswer = kAudioHardwareBadObjectError; 4123 break; 4124 }; 4125 4126 Done: 4127 return theAnswer; 4128 } 4129 4130 static OSStatus BlackHole_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]) 4131 { 4132 #pragma unused(inClientProcessID, inQualifierDataSize, inQualifierData) 4133 4134 // declare the local variables 4135 OSStatus theAnswer = 0; 4136 Float32 theNewVolume; 4137 Float32 theNewPitch; 4138 UInt32 theNewSource; 4139 4140 // check the arguments 4141 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_SetControlPropertyData: bad driver reference"); 4142 FailWithAction(inAddress == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetControlPropertyData: no address"); 4143 FailWithAction(outNumberPropertiesChanged == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetControlPropertyData: no place to return the number of properties that changed"); 4144 FailWithAction(outChangedAddresses == NULL, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_SetControlPropertyData: no place to return the properties that changed"); 4145 4146 // initialize the returned number of changed properties 4147 *outNumberPropertiesChanged = 0; 4148 4149 // Note that for each object, this driver implements all the required properties plus a few 4150 // extras that are useful but not required. There is more detailed commentary about each 4151 // property in the BlackHole_GetControlPropertyData() method. 4152 switch(inObjectID) 4153 { 4154 case kObjectID_Volume_Input_Master: 4155 case kObjectID_Volume_Output_Master: 4156 switch(inAddress->mSelector) 4157 { 4158 case kAudioLevelControlPropertyScalarValue: 4159 // For the scalar volume, we clamp the new value to [0, 1]. Note that if this 4160 // value changes, it implies that the dB value changed too. 4161 FailWithAction(inDataSize != sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_SetControlPropertyData: wrong size for the data for kAudioLevelControlPropertyScalarValue"); 4162 theNewVolume = volume_from_scalar(*((const Float32*)inData)); 4163 if(theNewVolume < 0.0) 4164 { 4165 theNewVolume = 0.0; 4166 } 4167 else if(theNewVolume > 1.0) 4168 { 4169 theNewVolume = 1.0; 4170 } 4171 pthread_mutex_lock(&gPlugIn_StateMutex); 4172 if(gVolume_Master_Value != theNewVolume) 4173 { 4174 gVolume_Master_Value = theNewVolume; 4175 *outNumberPropertiesChanged = 2; 4176 outChangedAddresses[0].mSelector = kAudioLevelControlPropertyScalarValue; 4177 outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; 4178 outChangedAddresses[0].mElement = kAudioObjectPropertyElementMain; 4179 outChangedAddresses[1].mSelector = kAudioLevelControlPropertyDecibelValue; 4180 outChangedAddresses[1].mScope = kAudioObjectPropertyScopeGlobal; 4181 outChangedAddresses[1].mElement = kAudioObjectPropertyElementMain; 4182 } 4183 pthread_mutex_unlock(&gPlugIn_StateMutex); 4184 break; 4185 4186 case kAudioLevelControlPropertyDecibelValue: 4187 // For the dB value, we first convert it to a scalar value since that is how 4188 // the value is tracked. Note that if this value changes, it implies that the 4189 // scalar value changes as well. 4190 FailWithAction(inDataSize != sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_SetControlPropertyData: wrong size for the data for kAudioLevelControlPropertyScalarValue"); 4191 theNewVolume = *((const Float32*)inData); 4192 if(theNewVolume < kVolume_MinDB) 4193 { 4194 theNewVolume = kVolume_MinDB; 4195 } 4196 else if(theNewVolume > kVolume_MaxDB) 4197 { 4198 theNewVolume = kVolume_MaxDB; 4199 } 4200 theNewVolume = volume_from_decibel(theNewVolume); 4201 pthread_mutex_lock(&gPlugIn_StateMutex); 4202 if(gVolume_Master_Value != theNewVolume) 4203 { 4204 gVolume_Master_Value = theNewVolume; 4205 *outNumberPropertiesChanged = 2; 4206 outChangedAddresses[0].mSelector = kAudioLevelControlPropertyScalarValue; 4207 outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; 4208 outChangedAddresses[0].mElement = kAudioObjectPropertyElementMain; 4209 outChangedAddresses[1].mSelector = kAudioLevelControlPropertyDecibelValue; 4210 outChangedAddresses[1].mScope = kAudioObjectPropertyScopeGlobal; 4211 outChangedAddresses[1].mElement = kAudioObjectPropertyElementMain; 4212 } 4213 pthread_mutex_unlock(&gPlugIn_StateMutex); 4214 break; 4215 4216 default: 4217 theAnswer = kAudioHardwareUnknownPropertyError; 4218 break; 4219 }; 4220 break; 4221 4222 case kObjectID_Mute_Input_Master: 4223 case kObjectID_Mute_Output_Master: 4224 switch(inAddress->mSelector) 4225 { 4226 case kAudioBooleanControlPropertyValue: 4227 FailWithAction(inDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_SetControlPropertyData: wrong size for the data for kAudioBooleanControlPropertyValue"); 4228 pthread_mutex_lock(&gPlugIn_StateMutex); 4229 if(gMute_Master_Value != (*((const UInt32*)inData) != 0)) 4230 { 4231 gMute_Master_Value = *((const UInt32*)inData) != 0; 4232 *outNumberPropertiesChanged = 1; 4233 outChangedAddresses[0].mSelector = kAudioBooleanControlPropertyValue; 4234 outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; 4235 outChangedAddresses[0].mElement = kAudioObjectPropertyElementMain; 4236 } 4237 pthread_mutex_unlock(&gPlugIn_StateMutex); 4238 break; 4239 4240 default: 4241 theAnswer = kAudioHardwareUnknownPropertyError; 4242 break; 4243 }; 4244 break; 4245 4246 case kObjectID_Pitch_Adjust: 4247 switch(inAddress->mSelector) 4248 { 4249 case kAudioStereoPanControlPropertyValue: 4250 // For the scalar pitch, we clamp the new value to [0, 1]. 4251 FailWithAction(inDataSize != sizeof(Float32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_SetControlPropertyData: wrong size for the data for kAudioLevelControlPropertyScalarValue"); 4252 theNewPitch = *((const Float32*)inData); 4253 if(theNewPitch < 0.0) 4254 { 4255 theNewPitch = 0.0; 4256 } 4257 else if(theNewPitch > 1.0) 4258 { 4259 theNewPitch = 1.0; 4260 } 4261 pthread_mutex_lock(&gPlugIn_StateMutex); 4262 4263 if(gPitch_Adjust != theNewPitch) 4264 { 4265 gPitch_Adjust = theNewPitch; 4266 gDevice_AdjustedTicksPerFrame = gDevice_HostTicksPerFrame - gDevice_HostTicksPerFrame/100.0 * 2.0*(gPitch_Adjust - 0.5); 4267 *outNumberPropertiesChanged = 1; 4268 outChangedAddresses[0].mSelector = kAudioStereoPanControlPropertyValue; 4269 outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; 4270 outChangedAddresses[0].mElement = kAudioObjectPropertyElementMain; 4271 } 4272 pthread_mutex_unlock(&gPlugIn_StateMutex); 4273 break; 4274 4275 default: 4276 theAnswer = kAudioHardwareUnknownPropertyError; 4277 break; 4278 }; 4279 break; 4280 4281 case kObjectID_ClockSource: 4282 switch(inAddress->mSelector) 4283 { 4284 case kAudioSelectorControlPropertyCurrentItem: 4285 FailWithAction(inDataSize != sizeof(UInt32), theAnswer = kAudioHardwareBadPropertySizeError, Done, "BlackHole_SetControlPropertyData: wrong size for the data for kAudioSelectorControlPropertyCurrentItem"); 4286 theNewSource = *((const UInt32*)inData); 4287 if(theNewSource >= kClockSource_NumberItems) 4288 { 4289 theNewSource = kClockSource_NumberItems - 1; 4290 } 4291 pthread_mutex_lock(&gPlugIn_StateMutex); 4292 if(gClockSource_Value != theNewSource) 4293 { 4294 gClockSource_Value = theNewSource; 4295 UInt64 changeAction = (theNewSource > 0) ? ChangeAction_EnablePitchControl : ChangeAction_DisablePitchControl; 4296 4297 *outNumberPropertiesChanged = 1; 4298 outChangedAddresses[0].mSelector = kAudioSelectorControlPropertyCurrentItem; 4299 outChangedAddresses[0].mScope = kAudioObjectPropertyScopeGlobal; 4300 outChangedAddresses[0].mElement = kAudioObjectPropertyElementMain; 4301 4302 // Notify HAL about device configuration change 4303 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 4304 gPlugIn_Host->RequestDeviceConfigurationChange(gPlugIn_Host, kObjectID_Device, changeAction, NULL); 4305 }); 4306 } 4307 pthread_mutex_unlock(&gPlugIn_StateMutex); 4308 break; 4309 4310 default: 4311 theAnswer = kAudioHardwareUnknownPropertyError; 4312 break; 4313 }; 4314 break; 4315 4316 default: 4317 theAnswer = kAudioHardwareBadObjectError; 4318 break; 4319 }; 4320 4321 Done: 4322 return theAnswer; 4323 } 4324 4325 #pragma mark IO Operations 4326 4327 static OSStatus BlackHole_StartIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID) 4328 { 4329 // This call tells the device that IO is starting for the given client. When this routine 4330 // returns, the device's clock is running and it is ready to have data read/written. It is 4331 // important to note that multiple clients can have IO running on the device at the same time. 4332 // So, work only needs to be done when the first client starts. All subsequent starts simply 4333 // increment the counter. 4334 4335 DebugMsg("BlackHole_StartIO"); 4336 4337 #pragma unused(inClientID, inDeviceObjectID) 4338 4339 // declare the local variables 4340 OSStatus theAnswer = 0; 4341 4342 // check the arguments 4343 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_StartIO: bad driver reference"); 4344 FailWithAction(inDeviceObjectID != kObjectID_Device && inDeviceObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_StartIO: bad device ID"); 4345 FailWithAction(inDeviceObjectID == kObjectID_Device && gDevice_IOIsRunning == UINT64_MAX, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_StartIO: overflow error."); 4346 FailWithAction(inDeviceObjectID == kObjectID_Device2 && gDevice2_IOIsRunning == UINT64_MAX, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_StartIO: overflow error."); 4347 4348 // we need to hold the state lock 4349 pthread_mutex_lock(&gPlugIn_StateMutex); 4350 4351 4352 if (inDeviceObjectID == kObjectID_Device) { gDevice_IOIsRunning += 1; } 4353 if (inDeviceObjectID == kObjectID_Device2) { gDevice2_IOIsRunning += 1; } 4354 4355 // allocate ring buffer 4356 if ((gDevice_IOIsRunning || gDevice2_IOIsRunning) && gRingBuffer == NULL) 4357 { 4358 gDevice_NumberTimeStamps = 0; 4359 gDevice_AnchorSampleTime = 0; 4360 gDevice_AnchorHostTime = mach_absolute_time(); 4361 gDevice_PreviousTicks = 0; 4362 gRingBuffer = calloc(kRing_Buffer_Frame_Size * kNumber_Of_Channels, sizeof(Float32)); 4363 } 4364 4365 4366 // unlock the state lock 4367 pthread_mutex_unlock(&gPlugIn_StateMutex); 4368 4369 Done: 4370 return theAnswer; 4371 } 4372 4373 static OSStatus BlackHole_StopIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID) 4374 { 4375 // This call tells the device that the client has stopped IO. The driver can stop the hardware 4376 // once all clients have stopped. 4377 4378 #pragma unused(inClientID, inDeviceObjectID) 4379 4380 // declare the local variables 4381 OSStatus theAnswer = 0; 4382 4383 // check the arguments 4384 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_StopIO: bad driver reference"); 4385 FailWithAction(inDeviceObjectID != kObjectID_Device && inDeviceObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_StopIO: bad device ID"); 4386 FailWithAction(inDeviceObjectID == kObjectID_Device && gDevice_IOIsRunning == 0, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_StartIO: underflow error."); 4387 FailWithAction(inDeviceObjectID == kObjectID_Device2 && gDevice2_IOIsRunning == 0, theAnswer = kAudioHardwareIllegalOperationError, Done, "BlackHole_StartIO: underflow error."); 4388 4389 // we need to hold the state lock 4390 pthread_mutex_lock(&gPlugIn_StateMutex); 4391 4392 4393 if (inDeviceObjectID == kObjectID_Device) { gDevice_IOIsRunning -= 1; } 4394 if (inDeviceObjectID == kObjectID_Device2) { gDevice2_IOIsRunning -= 1; } 4395 4396 // free the ring buffer 4397 if (!gDevice_IOIsRunning && !gDevice2_IOIsRunning && gRingBuffer != NULL) 4398 { 4399 free(gRingBuffer); 4400 gRingBuffer = NULL; 4401 } 4402 4403 // unlock the state lock 4404 pthread_mutex_unlock(&gPlugIn_StateMutex); 4405 4406 Done: 4407 return theAnswer; 4408 } 4409 4410 static OSStatus BlackHole_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, Float64* outSampleTime, UInt64* outHostTime, UInt64* outSeed) 4411 { 4412 // This method returns the current zero time stamp for the device. The HAL models the timing of 4413 // a device as a series of time stamps that relate the sample time to a host time. The zero 4414 // time stamps are spaced such that the sample times are the value of 4415 // kAudioDevicePropertyZeroTimeStampPeriod apart. This is often modeled using a ring buffer 4416 // where the zero time stamp is updated when wrapping around the ring buffer. 4417 // 4418 // For this device, the zero time stamps' sample time increments every kDevice_RingBufferSize 4419 // frames and the host time increments by kDevice_RingBufferSize * gDevice_HostTicksPerFrame. 4420 4421 #pragma unused(inClientID, inDeviceObjectID) 4422 4423 // declare the local variables 4424 OSStatus theAnswer = 0; 4425 UInt64 theCurrentHostTime; 4426 Float64 theHostTicksPerRingBuffer; 4427 Float64 theAdjustedTicksPerRingBuffer; 4428 Float64 theNextTickOffset; 4429 UInt64 theNextHostTime; 4430 4431 // check the arguments 4432 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetZeroTimeStamp: bad driver reference"); 4433 FailWithAction(inDeviceObjectID != kObjectID_Device && inDeviceObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_GetZeroTimeStamp: bad device ID"); 4434 4435 // we need to hold the locks 4436 pthread_mutex_lock(&gDevice_IOMutex); 4437 4438 // get the current host time 4439 theCurrentHostTime = mach_absolute_time(); 4440 4441 // calculate the next host time 4442 theHostTicksPerRingBuffer = gDevice_HostTicksPerFrame * ((Float64)kDevice_RingBufferSize); 4443 if (gClockSource_Value > 0) { 4444 theAdjustedTicksPerRingBuffer = gDevice_AdjustedTicksPerFrame * ((Float64)kDevice_RingBufferSize); 4445 } 4446 else { 4447 theAdjustedTicksPerRingBuffer = gDevice_HostTicksPerFrame * ((Float64)kDevice_RingBufferSize); 4448 } 4449 4450 theNextTickOffset = gDevice_PreviousTicks + theAdjustedTicksPerRingBuffer; 4451 4452 theNextHostTime = gDevice_AnchorHostTime + ((UInt64)theNextTickOffset); 4453 4454 // go to the next time if the next host time is less than the current time 4455 if(theNextHostTime <= theCurrentHostTime) 4456 { 4457 ++gDevice_NumberTimeStamps; 4458 gDevice_PreviousTicks = theNextTickOffset; 4459 } 4460 4461 // set the return values 4462 *outSampleTime = gDevice_NumberTimeStamps * kDevice_RingBufferSize; 4463 *outHostTime = gDevice_AnchorHostTime + gDevice_PreviousTicks; 4464 *outSeed = 1; 4465 4466 // DebugMsg("SampleTime: %f \t HostTime: %llu", *outSampleTime, *outHostTime); 4467 4468 // unlock the state lock 4469 pthread_mutex_unlock(&gDevice_IOMutex); 4470 4471 Done: 4472 return theAnswer; 4473 } 4474 4475 static OSStatus BlackHole_WillDoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, Boolean* outWillDo, Boolean* outWillDoInPlace) 4476 { 4477 // This method returns whether or not the device will do a given IO operation. For this device, 4478 // we only support reading input data and writing output data. 4479 4480 #pragma unused(inClientID, inDeviceObjectID) 4481 4482 // declare the local variables 4483 OSStatus theAnswer = 0; 4484 4485 // check the arguments 4486 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_WillDoIOOperation: bad driver reference"); 4487 FailWithAction(inDeviceObjectID != kObjectID_Device && inDeviceObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_WillDoIOOperation: bad device ID"); 4488 4489 // figure out if we support the operation 4490 bool willDo = false; 4491 bool willDoInPlace = true; 4492 switch(inOperationID) 4493 { 4494 case kAudioServerPlugInIOOperationReadInput: 4495 willDo = true; 4496 willDoInPlace = true; 4497 break; 4498 4499 case kAudioServerPlugInIOOperationWriteMix: 4500 willDo = true; 4501 willDoInPlace = true; 4502 break; 4503 4504 }; 4505 4506 // fill out the return values 4507 if(outWillDo != NULL) 4508 { 4509 *outWillDo = willDo; 4510 } 4511 if(outWillDoInPlace != NULL) 4512 { 4513 *outWillDoInPlace = willDoInPlace; 4514 } 4515 4516 Done: 4517 return theAnswer; 4518 } 4519 4520 static OSStatus BlackHole_BeginIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo) 4521 { 4522 // This is called at the beginning of an IO operation. This device doesn't do anything, so just 4523 // check the arguments and return. 4524 4525 #pragma unused(inClientID, inOperationID, inIOBufferFrameSize, inIOCycleInfo, inDeviceObjectID) 4526 4527 // declare the local variables 4528 OSStatus theAnswer = 0; 4529 4530 // check the arguments 4531 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_BeginIOOperation: bad driver reference"); 4532 FailWithAction(inDeviceObjectID != kObjectID_Device && inDeviceObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_BeginIOOperation: bad device ID"); 4533 4534 Done: 4535 return theAnswer; 4536 } 4537 4538 static OSStatus BlackHole_DoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, AudioObjectID inStreamObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo, void* ioMainBuffer, void* ioSecondaryBuffer) 4539 { 4540 // This is called to actually perform a given operation. 4541 4542 #pragma unused(inClientID, inIOCycleInfo, ioSecondaryBuffer, inDeviceObjectID) 4543 4544 // declare the local variables 4545 OSStatus theAnswer = 0; 4546 4547 // check the arguments 4548 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_DoIOOperation: bad driver reference"); 4549 FailWithAction(inDeviceObjectID != kObjectID_Device && inDeviceObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_DoIOOperation: bad device ID"); 4550 FailWithAction((inStreamObjectID != kObjectID_Stream_Input) && (inStreamObjectID != kObjectID_Stream_Output), theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_DoIOOperation: bad stream ID"); 4551 4552 // Calculate the ring buffer offsets and splits. 4553 UInt64 mSampleTime = inOperationID == kAudioServerPlugInIOOperationReadInput ? inIOCycleInfo->mInputTime.mSampleTime : inIOCycleInfo->mOutputTime.mSampleTime; 4554 UInt32 ringBufferFrameLocationStart = mSampleTime % kRing_Buffer_Frame_Size; 4555 UInt32 firstPartFrameSize = kRing_Buffer_Frame_Size - ringBufferFrameLocationStart; 4556 UInt32 secondPartFrameSize = 0; 4557 4558 if (firstPartFrameSize >= inIOBufferFrameSize) 4559 { 4560 firstPartFrameSize = inIOBufferFrameSize; 4561 } 4562 else 4563 { 4564 secondPartFrameSize = inIOBufferFrameSize - firstPartFrameSize; 4565 } 4566 4567 // Keep track of last outputSampleTime and the cleared buffer status. 4568 static Float64 lastOutputSampleTime = 0; 4569 static Boolean isBufferClear = true; 4570 4571 // From BlackHole to Application 4572 if(inOperationID == kAudioServerPlugInIOOperationReadInput) 4573 { 4574 // If mute is one let's just fill the buffer with zeros or if there's no apps outputting audio 4575 if (gMute_Master_Value || lastOutputSampleTime - inIOBufferFrameSize < inIOCycleInfo->mInputTime.mSampleTime) 4576 { 4577 // Clear the ioMainBuffer 4578 vDSP_vclr(ioMainBuffer, 1, inIOBufferFrameSize * kNumber_Of_Channels); 4579 4580 // Clear the ring buffer. 4581 if (!isBufferClear) 4582 { 4583 vDSP_vclr(gRingBuffer, 1, kRing_Buffer_Frame_Size * kNumber_Of_Channels); 4584 isBufferClear = true; 4585 } 4586 } 4587 else 4588 { 4589 // Copy the buffers. 4590 memcpy(ioMainBuffer, gRingBuffer + ringBufferFrameLocationStart * kNumber_Of_Channels, firstPartFrameSize * kNumber_Of_Channels * sizeof(Float32)); 4591 memcpy((Float32*)ioMainBuffer + firstPartFrameSize * kNumber_Of_Channels, gRingBuffer, secondPartFrameSize * kNumber_Of_Channels * sizeof(Float32)); 4592 4593 // Finally we'll apply the output volume to the buffer. 4594 if(kEnableVolumeControl) 4595 { 4596 vDSP_vsmul(ioMainBuffer, 1, &gVolume_Master_Value, ioMainBuffer, 1, inIOBufferFrameSize * kNumber_Of_Channels); 4597 } 4598 4599 } 4600 } 4601 4602 // From Application to BlackHole 4603 if(inOperationID == kAudioServerPlugInIOOperationWriteMix) 4604 { 4605 4606 // Overload error. 4607 if (inIOCycleInfo->mCurrentTime.mSampleTime > inIOCycleInfo->mOutputTime.mSampleTime + inIOBufferFrameSize + kLatency_Frame_Size) 4608 { 4609 DebugMsg("BlackHole overload error. kAudioServerPlugInIOOperationWriteMix was unable to complete operation before the deadline. Try increasing the buffer frame size."); 4610 return kAudioHardwareUnspecifiedError; 4611 } 4612 4613 4614 // Copy the buffers. 4615 memcpy(gRingBuffer + ringBufferFrameLocationStart * kNumber_Of_Channels, ioMainBuffer, firstPartFrameSize * kNumber_Of_Channels * sizeof(Float32)); 4616 memcpy(gRingBuffer, (Float32*)ioMainBuffer + firstPartFrameSize * kNumber_Of_Channels, secondPartFrameSize * kNumber_Of_Channels * sizeof(Float32)); 4617 4618 // Save the last output time. 4619 lastOutputSampleTime = inIOCycleInfo->mOutputTime.mSampleTime + inIOBufferFrameSize; 4620 isBufferClear = false; 4621 } 4622 4623 Done: 4624 return theAnswer; 4625 } 4626 4627 static OSStatus BlackHole_EndIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo) 4628 { 4629 // This is called at the end of an IO operation. This device doesn't do anything, so just check 4630 // the arguments and return. 4631 4632 #pragma unused(inClientID, inOperationID, inIOBufferFrameSize, inIOCycleInfo, inDeviceObjectID) 4633 4634 // declare the local variables 4635 OSStatus theAnswer = 0; 4636 4637 // check the arguments 4638 FailWithAction(inDriver != gAudioServerPlugInDriverRef, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_EndIOOperation: bad driver reference"); 4639 FailWithAction(inDeviceObjectID != kObjectID_Device && inDeviceObjectID != kObjectID_Device2, theAnswer = kAudioHardwareBadObjectError, Done, "BlackHole_EndIOOperation: bad device ID"); 4640 4641 Done: 4642 return theAnswer; 4643 }