mirror of
https://github.com/morgan9e/VolumeControl
synced 2026-04-14 00:04:05 +09:00
Initial commit
This commit is contained in:
240
driver/VC_Device.h
Normal file
240
driver/VC_Device.h
Normal file
@@ -0,0 +1,240 @@
|
||||
// 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_Device.h
|
||||
// VCDriver
|
||||
//
|
||||
// Copyright © 2016, 2017, 2019 Kyle Neideck
|
||||
// Copyright © 2019 Gordon Childs
|
||||
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_Device.h from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
#ifndef VCDriver__VC_Device
|
||||
#define VCDriver__VC_Device
|
||||
|
||||
// SuperClass Includes
|
||||
#include "VC_AbstractDevice.h"
|
||||
|
||||
// Local Includes
|
||||
#include "VC_Types.h"
|
||||
#include "VC_Stream.h"
|
||||
#include "VC_VolumeControl.h"
|
||||
#include "VC_MuteControl.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAMutex.h"
|
||||
#include "CAVolumeCurve.h"
|
||||
#include "CARingBuffer.h"
|
||||
|
||||
// System Includes
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <pthread.h>
|
||||
#include <atomic>
|
||||
|
||||
|
||||
class VC_Device
|
||||
:
|
||||
public VC_AbstractDevice
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
public:
|
||||
static VC_Device& GetInstance();
|
||||
|
||||
private:
|
||||
static void StaticInitializer();
|
||||
|
||||
protected:
|
||||
VC_Device(AudioObjectID inObjectID,
|
||||
const CFStringRef __nonnull inDeviceName,
|
||||
const CFStringRef __nonnull inDeviceUID,
|
||||
const CFStringRef __nonnull inDeviceModelUID,
|
||||
AudioObjectID inInputStreamID,
|
||||
AudioObjectID inOutputStreamID,
|
||||
AudioObjectID inOutputVolumeControlID,
|
||||
AudioObjectID inOutputMuteControlID);
|
||||
virtual ~VC_Device();
|
||||
|
||||
virtual void Activate();
|
||||
virtual void Deactivate();
|
||||
|
||||
private:
|
||||
void InitLoopback();
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
public:
|
||||
virtual bool HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual bool IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData) const;
|
||||
virtual void GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* __nonnull outData) const;
|
||||
virtual void SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, const void* __nonnull inData);
|
||||
|
||||
#pragma mark Device Property Operations
|
||||
|
||||
private:
|
||||
bool Device_HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
bool Device_IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
UInt32 Device_GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData) const;
|
||||
void Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* __nonnull outData) const;
|
||||
void Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, const void* __nonnull inData);
|
||||
|
||||
#pragma mark IO Operations
|
||||
|
||||
public:
|
||||
void StartIO(UInt32 inClientID);
|
||||
void StopIO(UInt32 inClientID);
|
||||
|
||||
void GetZeroTimeStamp(Float64& outSampleTime, UInt64& outHostTime, UInt64& outSeed);
|
||||
|
||||
void WillDoIOOperation(UInt32 inOperationID, bool& outWillDo, bool& outWillDoInPlace) const;
|
||||
void BeginIOOperation(UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo& inIOCycleInfo, UInt32 inClientID);
|
||||
void DoIOOperation(AudioObjectID inStreamObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo& inIOCycleInfo, void* __nonnull ioMainBuffer, void* __nullable ioSecondaryBuffer);
|
||||
void EndIOOperation(UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo& inIOCycleInfo, UInt32 inClientID);
|
||||
|
||||
private:
|
||||
void ReadInputData(UInt32 inIOBufferFrameSize, Float64 inSampleTime, void* __nonnull outBuffer);
|
||||
void WriteOutputData(UInt32 inIOBufferFrameSize, Float64 inSampleTime, const void* __nonnull inBuffer);
|
||||
|
||||
#pragma mark Accessors
|
||||
|
||||
public:
|
||||
Float64 GetSampleRate() const;
|
||||
void RequestSampleRate(Float64 inRequestedSampleRate);
|
||||
|
||||
private:
|
||||
/*!
|
||||
@return The Audio Object that has the ID inObjectID and belongs to this device.
|
||||
@throws CAException if there is no such Audio Object.
|
||||
*/
|
||||
const VC_Object& GetOwnedObjectByID(AudioObjectID inObjectID) const;
|
||||
VC_Object& GetOwnedObjectByID(AudioObjectID inObjectID);
|
||||
|
||||
/*! @return The number of Audio Objects belonging to this device, e.g. streams and controls. */
|
||||
UInt32 GetNumberOfSubObjects() const;
|
||||
/*! @return The number of Audio Objects with output scope belonging to this device. */
|
||||
UInt32 GetNumberOfOutputSubObjects() const;
|
||||
/*!
|
||||
@return The number of control Audio Objects with output scope belonging to this device, e.g.
|
||||
output volume and mute controls.
|
||||
*/
|
||||
UInt32 GetNumberOfOutputControls() const;
|
||||
/*!
|
||||
Set the device's sample rate.
|
||||
|
||||
Private because (after initialisation) this can only be called after asking the host to stop IO
|
||||
for the device. See VC_Device::PerformConfigChange and
|
||||
RequestDeviceConfigurationChange in AudioServerPlugIn.h.
|
||||
|
||||
@param inNewSampleRate The sample rate.
|
||||
@param force If true, set the sample rate on the device even if it's currently set to
|
||||
inNewSampleRate.
|
||||
@throws CAException if inNewSampleRate < 1 or if applying the sample rate to one of the streams
|
||||
fails.
|
||||
*/
|
||||
void SetSampleRate(Float64 inNewSampleRate, bool force = false);
|
||||
|
||||
/*! @return True if inObjectID is the ID of one of this device's streams. */
|
||||
inline bool IsStreamID(AudioObjectID inObjectID) const noexcept;
|
||||
|
||||
#pragma mark Hardware Accessors
|
||||
|
||||
private:
|
||||
void _HW_Open();
|
||||
void _HW_Close();
|
||||
kern_return_t _HW_StartIO();
|
||||
void _HW_StopIO();
|
||||
Float64 _HW_GetSampleRate() const;
|
||||
kern_return_t _HW_SetSampleRate(Float64 inNewSampleRate);
|
||||
UInt32 _HW_GetRingBufferFrameSize() const;
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
public:
|
||||
CFStringRef __nonnull CopyDeviceUID() const { return mDeviceUID; }
|
||||
void AddClient(const AudioServerPlugInClientInfo* __nonnull inClientInfo);
|
||||
void RemoveClient(const AudioServerPlugInClientInfo* __nonnull inClientInfo);
|
||||
/*!
|
||||
Apply a change requested with VC_PlugIn::Host_RequestDeviceConfigurationChange. See
|
||||
PerformDeviceConfigurationChange in AudioServerPlugIn.h.
|
||||
*/
|
||||
void PerformConfigChange(UInt64 inChangeAction, void* __nullable inChangeInfo);
|
||||
/*! Cancel a change requested with VC_PlugIn::Host_RequestDeviceConfigurationChange. */
|
||||
void AbortConfigChange(UInt64 inChangeAction, void* __nullable inChangeInfo);
|
||||
|
||||
private:
|
||||
static pthread_once_t sStaticInitializer;
|
||||
static VC_Device* __nonnull sInstance;
|
||||
|
||||
#define kDeviceName "Volume Control"
|
||||
#define kDeviceManufacturerName "VolumeControl"
|
||||
|
||||
const CFStringRef __nonnull mDeviceName;
|
||||
const CFStringRef __nonnull mDeviceUID;
|
||||
const CFStringRef __nonnull mDeviceModelUID;
|
||||
|
||||
bool mIsHidden;
|
||||
|
||||
enum
|
||||
{
|
||||
// The number of global/output sub-objects varies because the controls can be disabled.
|
||||
kNumberOfInputSubObjects = 1,
|
||||
|
||||
kNumberOfStreams = 2,
|
||||
kNumberOfInputStreams = 1,
|
||||
kNumberOfOutputStreams = 1
|
||||
};
|
||||
|
||||
CAMutex mStateMutex;
|
||||
CAMutex mIOMutex;
|
||||
|
||||
const Float64 kSampleRateDefault = 44100.0;
|
||||
// Before we can change sample rate, the host has to stop the device. The new sample rate is
|
||||
// stored here while it does.
|
||||
Float64 mPendingSampleRate = kSampleRateDefault;
|
||||
|
||||
#define kLoopbackRingBufferFrameSize 16384
|
||||
Float64 mLoopbackSampleRate;
|
||||
CARingBuffer mLoopbackRingBuffer;
|
||||
|
||||
// TODO: a comment explaining why we need a clock for loopback-only mode
|
||||
struct {
|
||||
Float64 hostTicksPerFrame = 0.0;
|
||||
UInt64 numberTimeStamps = 0;
|
||||
UInt64 anchorHostTime = 0;
|
||||
} mLoopbackTime;
|
||||
|
||||
VC_Stream mInputStream;
|
||||
VC_Stream mOutputStream;
|
||||
|
||||
enum class ChangeAction : UInt64
|
||||
{
|
||||
SetSampleRate
|
||||
};
|
||||
|
||||
VC_VolumeControl mVolumeControl;
|
||||
VC_MuteControl mMuteControl;
|
||||
|
||||
// Simple IO client counter to replace mClients-based tracking.
|
||||
std::atomic<UInt32> mIOClientCount{0};
|
||||
|
||||
};
|
||||
|
||||
#endif /* VCDriver__VC_Device */
|
||||
Reference in New Issue
Block a user