mirror of
https://github.com/morgan9e/VolumeControl
synced 2026-04-14 00:04:05 +09:00
411 lines
16 KiB
C++
411 lines
16 KiB
C++
// This file is part of Background Music.
|
|
//
|
|
// Background Music is free software: you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation, either version 2 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// Background Music is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//
|
|
// VC_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
|
|
// <CoreAudio/AudioHardwareBase.h>.
|
|
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<UInt32*>(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<AudioObjectID*>(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<UInt32*>(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<UInt32*>(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<UInt32*>(outData) = 1;
|
|
outDataSize = sizeof(UInt32);
|
|
break;
|
|
|
|
case kAudioDevicePropertyDeviceCanBeDefaultSystemDevice:
|
|
// This property returns whether or not the device wants to be the system
|
|
// default device. This is the device that is used to play interface sounds and
|
|
// other incidental or UI-related sounds on. Most devices should allow this
|
|
// although devices with lots of latency may not want to.
|
|
//
|
|
// 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<UInt32*>(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<UInt32*>(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<UInt32*>(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<UInt32*>(outData) = 0;
|
|
outDataSize = sizeof(UInt32);
|
|
break;
|
|
|
|
default:
|
|
VC_Object::GetPropertyData(inObjectID,
|
|
inClientPID,
|
|
inAddress,
|
|
inQualifierDataSize,
|
|
inQualifierData,
|
|
inDataSize,
|
|
outDataSize,
|
|
outData);
|
|
break;
|
|
};
|
|
}
|
|
|
|
#pragma clang assume_nonnull end
|
|
|