// 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_AbstractDevice.cpp // VCDriver // // Copyright © 2017 Kyle Neideck // Copyright (C) 2013 Apple Inc. All Rights Reserved. // // Self Include #include "VC_AbstractDevice.h" // Local Includes #include "VC_Utils.h" // PublicUtility Includes #include "CAException.h" #include "CADebugMacros.h" #pragma clang assume_nonnull begin VC_AbstractDevice::VC_AbstractDevice(AudioObjectID inObjectID, AudioObjectID inOwnerObjectID) : VC_Object(inObjectID, kAudioDeviceClassID, kAudioObjectClassID, inOwnerObjectID) { } VC_AbstractDevice::~VC_AbstractDevice() { } #pragma mark Property Operations bool VC_AbstractDevice::HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const { bool theAnswer = false; switch(inAddress.mSelector) { case kAudioObjectPropertyName: case kAudioObjectPropertyManufacturer: case kAudioDevicePropertyDeviceUID: case kAudioDevicePropertyModelUID: case kAudioDevicePropertyTransportType: case kAudioDevicePropertyRelatedDevices: case kAudioDevicePropertyClockDomain: case kAudioDevicePropertyDeviceIsAlive: case kAudioDevicePropertyDeviceIsRunning: case kAudioDevicePropertyDeviceCanBeDefaultDevice: case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: case kAudioDevicePropertyLatency: case kAudioDevicePropertyStreams: case kAudioObjectPropertyControlList: case kAudioDevicePropertySafetyOffset: case kAudioDevicePropertyNominalSampleRate: case kAudioDevicePropertyAvailableNominalSampleRates: case kAudioDevicePropertyIsHidden: case kAudioDevicePropertyZeroTimeStampPeriod: theAnswer = true; break; default: theAnswer = VC_Object::HasProperty(inObjectID, inClientPID, inAddress); break; }; return theAnswer; } bool VC_AbstractDevice::IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const { bool theAnswer = false; switch(inAddress.mSelector) { case kAudioObjectPropertyName: case kAudioObjectPropertyManufacturer: case kAudioDevicePropertyDeviceUID: case kAudioDevicePropertyModelUID: case kAudioDevicePropertyTransportType: case kAudioDevicePropertyRelatedDevices: case kAudioDevicePropertyClockDomain: case kAudioDevicePropertyDeviceIsAlive: case kAudioDevicePropertyDeviceIsRunning: case kAudioDevicePropertyDeviceCanBeDefaultDevice: case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: case kAudioDevicePropertyLatency: case kAudioDevicePropertyStreams: case kAudioObjectPropertyControlList: case kAudioDevicePropertySafetyOffset: case kAudioDevicePropertyNominalSampleRate: case kAudioDevicePropertyAvailableNominalSampleRates: case kAudioDevicePropertyIsHidden: case kAudioDevicePropertyZeroTimeStampPeriod: theAnswer = false; break; default: theAnswer = VC_Object::IsPropertySettable(inObjectID, inClientPID, inAddress); break; }; return theAnswer; } UInt32 VC_AbstractDevice::GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData) const { UInt32 theAnswer = 0; switch(inAddress.mSelector) { case kAudioObjectPropertyName: theAnswer = sizeof(CFStringRef); break; case kAudioObjectPropertyManufacturer: theAnswer = sizeof(CFStringRef); break; case kAudioDevicePropertyDeviceUID: theAnswer = sizeof(CFStringRef); break; case kAudioDevicePropertyModelUID: theAnswer = sizeof(CFStringRef); break; case kAudioDevicePropertyTransportType: theAnswer = sizeof(UInt32); break; case kAudioDevicePropertyRelatedDevices: theAnswer = sizeof(AudioObjectID); break; case kAudioDevicePropertyClockDomain: theAnswer = sizeof(UInt32); break; case kAudioDevicePropertyDeviceCanBeDefaultDevice: theAnswer = sizeof(UInt32); break; case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: theAnswer = sizeof(UInt32); break; case kAudioDevicePropertyDeviceIsAlive: theAnswer = sizeof(AudioClassID); break; case kAudioDevicePropertyDeviceIsRunning: theAnswer = sizeof(UInt32); break; case kAudioDevicePropertyLatency: theAnswer = sizeof(UInt32); break; case kAudioDevicePropertyStreams: theAnswer = 0; break; case kAudioObjectPropertyControlList: theAnswer = 0; break; case kAudioDevicePropertySafetyOffset: theAnswer = sizeof(UInt32); break; case kAudioDevicePropertyNominalSampleRate: theAnswer = sizeof(Float64); break; case kAudioDevicePropertyAvailableNominalSampleRates: theAnswer = 0; break; case kAudioDevicePropertyIsHidden: theAnswer = sizeof(UInt32); break; case kAudioDevicePropertyZeroTimeStampPeriod: theAnswer = sizeof(UInt32); break; default: theAnswer = VC_Object::GetPropertyDataSize(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData); break; }; return theAnswer; } void VC_AbstractDevice::GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const { UInt32 theNumberItemsToFetch; switch(inAddress.mSelector) { case kAudioObjectPropertyName: case kAudioObjectPropertyManufacturer: case kAudioDevicePropertyDeviceUID: case kAudioDevicePropertyModelUID: case kAudioDevicePropertyDeviceIsRunning: case kAudioDevicePropertyZeroTimeStampPeriod: case kAudioDevicePropertyNominalSampleRate: case kAudioDevicePropertyAvailableNominalSampleRates: // Should be unreachable. Reaching this point would mean a concrete device has delegated // a required property that can't be handled by this class or its parent, VC_Object. // // See VC_Device for info about these properties. // // TODO: Write a test that checks all required properties for each subclass. VCAssert(false, "VC_AbstractDevice::GetPropertyData: Property %u not handled in subclass", inAddress.mSelector); // Throw in release builds. Throw(CAException(kAudioHardwareIllegalOperationError)); case kAudioDevicePropertyTransportType: // This value represents how the device is attached to the system. This can be // any 32 bit integer, but common values for this property are defined in // . ThrowIf(inDataSize < sizeof(UInt32), CAException(kAudioHardwareBadPropertySizeError), "VC_AbstractDevice::GetPropertyData: not enough space for the return value of " "kAudioDevicePropertyTransportType for the device"); // Default to virtual device. *reinterpret_cast(outData) = kAudioDeviceTransportTypeVirtual; outDataSize = sizeof(UInt32); break; case kAudioDevicePropertyRelatedDevices: // The related devices property identifies device objects that are very closely // related. Generally, this is for relating devices that are packaged together // in the hardware such as when the input side and the output side of a piece // of hardware can be clocked separately and therefore need to be represented // as separate AudioDevice objects. In such case, both devices would report // that they are related to each other. Note that at minimum, a device is // related to itself, so this list will always be at least one item long. // Calculate the number of items that have been requested. Note that this // number is allowed to be smaller than the actual size of the list. In such // case, only that number of items will be returned theNumberItemsToFetch = inDataSize / sizeof(AudioObjectID); // Default to only have the one device. if(theNumberItemsToFetch > 1) { theNumberItemsToFetch = 1; } // Write the devices' object IDs into the return value. if(theNumberItemsToFetch > 0) { reinterpret_cast(outData)[0] = GetObjectID(); } // Report how much we wrote. outDataSize = theNumberItemsToFetch * sizeof(AudioObjectID); break; case kAudioDevicePropertyClockDomain: // This property allows the device to declare what other devices it is // synchronized with in hardware. The way it works is that if two devices have // the same value for this property and the value is not zero, then the two // devices are synchronized in hardware. Note that a device that either can't // be synchronized with others or doesn't know should return 0 for this // property. // // Default to 0. ThrowIf(inDataSize < sizeof(UInt32), CAException(kAudioHardwareBadPropertySizeError), "VC_AbstractDevice::GetPropertyData: not enough space for the return value of " "kAudioDevicePropertyClockDomain for the device"); *reinterpret_cast(outData) = 0; outDataSize = sizeof(UInt32); break; case kAudioDevicePropertyDeviceIsAlive: // Default to alive. ThrowIf(inDataSize < sizeof(UInt32), CAException(kAudioHardwareBadPropertySizeError), "VC_AbstractDevice::GetPropertyData: not enough space for the return value of " "kAudioDevicePropertyDeviceIsAlive for the device"); *reinterpret_cast(outData) = 1; outDataSize = sizeof(UInt32); break; case kAudioDevicePropertyDeviceCanBeDefaultDevice: // This property returns whether or not the device wants to be able to be the // default device for content. This is the device that iTunes and QuickTime // will use to play their content on and FaceTime will use as it's microphone. // Nearly all devices should allow for this. // // Default to true. ThrowIf(inDataSize < sizeof(UInt32), CAException(kAudioHardwareBadPropertySizeError), "VC_AbstractDevice::GetPropertyData: not enough space for the return value of " "kAudioDevicePropertyDeviceCanBeDefaultDevice for the device"); *reinterpret_cast(outData) = 1; outDataSize = sizeof(UInt32); break; case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice: // This property returns whether or not the device wants to be the system // default device. This is the device that is used to play interface sounds and // other incidental or UI-related sounds on. Most devices should allow this // although devices with lots of latency may not want to. // // Default to true. ThrowIf(inDataSize < sizeof(UInt32), CAException(kAudioHardwareBadPropertySizeError), "VC_AbstractDevice::GetPropertyData: not enough space for the return value of " "kAudioDevicePropertyDeviceCanBeDefaultSystemDevice for the device"); *reinterpret_cast(outData) = 1; outDataSize = sizeof(UInt32); break; case kAudioDevicePropertyLatency: // This property returns the presentation latency of the device. Default to 0. ThrowIf(inDataSize < sizeof(UInt32), CAException(kAudioHardwareBadPropertySizeError), "VC_AbstractDevice::GetPropertyData: not enough space for the return value of " "kAudioDevicePropertyLatency for the device"); *reinterpret_cast(outData) = 0; outDataSize = sizeof(UInt32); break; case kAudioDevicePropertyStreams: // Default to not having any streams. outDataSize = 0; break; case kAudioObjectPropertyControlList: // Default to not having any controls. outDataSize = 0; break; case kAudioDevicePropertySafetyOffset: // This property returns the how close to now the HAL can read and write. Default to 0. ThrowIf(inDataSize < sizeof(UInt32), CAException(kAudioHardwareBadPropertySizeError), "VC_AbstractDevice::GetPropertyData: not enough space for the return value of " "kAudioDevicePropertySafetyOffset for the device"); *reinterpret_cast(outData) = 0; outDataSize = sizeof(UInt32); break; case kAudioDevicePropertyIsHidden: // This returns whether or not the device is visible to clients. Default to not hidden. ThrowIf(inDataSize < sizeof(UInt32), CAException(kAudioHardwareBadPropertySizeError), "VC_AbstractDevice::GetPropertyData: not enough space for the return value of " "kAudioDevicePropertyIsHidden for the device"); *reinterpret_cast(outData) = 0; outDataSize = sizeof(UInt32); break; default: VC_Object::GetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData); break; }; } #pragma clang assume_nonnull end