// This file is part of Background Music. // // Background Music is free software: you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation, either version 2 of the // License, or (at your option) any later version. // // Background Music is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Background Music. If not, see . // // VC_PlugIn.cpp // VCDriver // // Copyright © 2016, 2017 Kyle Neideck // Copyright (C) 2013 Apple Inc. All Rights Reserved. // // Based largely on SA_PlugIn.cpp from Apple's SimpleAudioDriver Plug-In sample code. // https://developer.apple.com/library/mac/samplecode/AudioDriverExamples // // Self Include #include "VC_PlugIn.h" // Local Includes #include "VC_Device.h" // PublicUtility Includes #include "CAException.h" #include "CADebugMacros.h" #include "CAPropertyAddress.h" #include "CADispatchQueue.h" #pragma mark Construction/Destruction pthread_once_t VC_PlugIn::sStaticInitializer = PTHREAD_ONCE_INIT; VC_PlugIn* VC_PlugIn::sInstance = NULL; AudioServerPlugInHostRef VC_PlugIn::sHost = NULL; VC_PlugIn& VC_PlugIn::GetInstance() { pthread_once(&sStaticInitializer, StaticInitializer); return *sInstance; } void VC_PlugIn::StaticInitializer() { try { sInstance = new VC_PlugIn; sInstance->Activate(); } catch(...) { DebugMsg("VC_PlugIn::StaticInitializer: failed to create the plug-in"); delete sInstance; sInstance = NULL; } } VC_PlugIn::VC_PlugIn() : VC_Object(kAudioObjectPlugInObject, kAudioPlugInClassID, kAudioObjectClassID, 0), mMutex("VC_PlugIn") { } VC_PlugIn::~VC_PlugIn() { } void VC_PlugIn::Deactivate() { CAMutex::Locker theLocker(mMutex); VC_Object::Deactivate(); // TODO: //_RemoveAllDevices(); } #pragma mark Property Operations bool VC_PlugIn::HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const { bool theAnswer = false; switch(inAddress.mSelector) { case kAudioObjectPropertyManufacturer: case kAudioPlugInPropertyDeviceList: case kAudioPlugInPropertyTranslateUIDToDevice: case kAudioPlugInPropertyResourceBundle: theAnswer = true; break; default: theAnswer = VC_Object::HasProperty(inObjectID, inClientPID, inAddress); }; return theAnswer; } bool VC_PlugIn::IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const { bool theAnswer = false; switch(inAddress.mSelector) { case kAudioObjectPropertyManufacturer: case kAudioPlugInPropertyDeviceList: case kAudioPlugInPropertyTranslateUIDToDevice: case kAudioPlugInPropertyResourceBundle: theAnswer = false; break; default: theAnswer = VC_Object::IsPropertySettable(inObjectID, inClientPID, inAddress); }; return theAnswer; } UInt32 VC_PlugIn::GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const { UInt32 theAnswer = 0; switch(inAddress.mSelector) { case kAudioObjectPropertyManufacturer: theAnswer = sizeof(CFStringRef); break; case kAudioObjectPropertyOwnedObjects: case kAudioPlugInPropertyDeviceList: theAnswer = sizeof(AudioObjectID); break; case kAudioPlugInPropertyTranslateUIDToDevice: theAnswer = sizeof(AudioObjectID); break; case kAudioPlugInPropertyResourceBundle: theAnswer = sizeof(CFStringRef); break; default: theAnswer = VC_Object::GetPropertyDataSize(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData); }; return theAnswer; } void VC_PlugIn::GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const { switch(inAddress.mSelector) { case kAudioObjectPropertyManufacturer: // This is the human readable name of the maker of the plug-in. ThrowIf(inDataSize < sizeof(CFStringRef), CAException(kAudioHardwareBadPropertySizeError), "VC_PlugIn::GetPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer"); *reinterpret_cast(outData) = CFSTR("VolumeControl"); outDataSize = sizeof(CFStringRef); break; case kAudioObjectPropertyOwnedObjects: // Fall through because this plug-in object only owns the device. case kAudioPlugInPropertyDeviceList: { AudioObjectID* theReturnedDeviceList = reinterpret_cast(outData); if(inDataSize >= sizeof(AudioObjectID)) { theReturnedDeviceList[0] = kObjectID_Device; outDataSize = sizeof(AudioObjectID); } else { outDataSize = 0; } } break; case kAudioPlugInPropertyTranslateUIDToDevice: { // This property translates the UID passed in the qualifier as a CFString into the // AudioObjectID for the device the UID refers to or kAudioObjectUnknown if no device // has the UID. ThrowIf(inQualifierDataSize < sizeof(CFStringRef), CAException(kAudioHardwareBadPropertySizeError), "VC_PlugIn::GetPropertyData: the qualifier size is too small for kAudioPlugInPropertyTranslateUIDToDevice"); ThrowIf(inDataSize < sizeof(AudioObjectID), CAException(kAudioHardwareBadPropertySizeError), "VC_PlugIn::GetPropertyData: not enough space for the return value of kAudioPlugInPropertyTranslateUIDToDevice"); CFStringRef theUID = *reinterpret_cast(inQualifierData); AudioObjectID* outID = reinterpret_cast(outData); if(CFEqual(theUID, VC_Device::GetInstance().CopyDeviceUID())) { DebugMsg("VC_PlugIn::GetPropertyData: Returning VCDevice for " "kAudioPlugInPropertyTranslateUIDToDevice"); *outID = kObjectID_Device; } else { LogWarning("VC_PlugIn::GetPropertyData: Returning kAudioObjectUnknown for " "kAudioPlugInPropertyTranslateUIDToDevice"); *outID = kAudioObjectUnknown; } outDataSize = sizeof(AudioObjectID); } break; case kAudioPlugInPropertyResourceBundle: // The resource bundle is a path relative to the path of the plug-in's bundle. // To specify that the plug-in bundle itself should be used, we just return the // empty string. ThrowIf(inDataSize < sizeof(AudioObjectID), CAException(kAudioHardwareBadPropertySizeError), "VC_GetPlugInPropertyData: not enough space for the return value of kAudioPlugInPropertyResourceBundle"); *reinterpret_cast(outData) = CFSTR(""); outDataSize = sizeof(CFStringRef); break; default: VC_Object::GetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); break; }; } void VC_PlugIn::SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData) { switch(inAddress.mSelector) { default: VC_Object::SetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData); break; }; }