forked from bitgapp/eqMac
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEQMDriver.swift
More file actions
142 lines (124 loc) · 5.1 KB
/
Copy pathEQMDriver.swift
File metadata and controls
142 lines (124 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//
// EQMDriver.swift
// eqMac
//
// Created by Nodeful on 12/08/2021.
// Copyright © 2021 Bitgapp. All rights reserved.
//
import Foundation
import CoreAudio.AudioServerPlugIn
import Atomics
import Shared
@objc class EQMDriver: NSObject {
static var host: AudioServerPlugInHostRef?
static var hostTicksPerFrame: Float64?
static private var _interface: AudioServerPlugInDriverInterface?
static private var _interfacePtr: UnsafeMutablePointer<AudioServerPlugInDriverInterface>?
static var refCounter = ManagedAtomic<UInt32>(0)
@objc public static var ref: AudioServerPlugInDriverRef?
static var mutex = Mutex()
@objc
public static func create (allocator: CFAllocator!, requestedTypeUUID: CFUUID!) -> UnsafeMutableRawPointer? {
// This is the CFPlugIn factory function. Its job is to create the implementation for the given
// type provided that the type is supported. Because this driver is simple and all its
// initialization is handled via static iniitalization when the bundle is loaded, all that
// needs to be done is to return the AudioServerPlugInDriverRef that points to the driver's
// interface. A more complicated driver would create any base line objects it needs to satisfy
// the IUnknown methods that are used to discover that actual interface to talk to the driver.
// The majority of the driver's initilization should be handled in the Initialize() method of
// the driver's AudioServerPlugInDriverInterface.
if !CFEqual(requestedTypeUUID, kAudioServerPluginTypeUUID) {
return nil
}
return UnsafeMutableRawPointer(createRef())
}
private static func createRef () -> AudioServerPlugInDriverRef {
if ref != nil {
return ref!
}
_interface = AudioServerPlugInDriverInterface(
_reserved: nil,
QueryInterface: EQM_QueryInterface,
AddRef: EQM_AddRef,
Release: EQM_Release,
Initialize: EQM_Initialize,
CreateDevice: EQM_CreateDevice,
DestroyDevice: EQM_DestroyDevice,
AddDeviceClient: EQM_AddDeviceClient,
RemoveDeviceClient: EQM_RemoveDeviceClient,
PerformDeviceConfigurationChange: EQM_PerformDeviceConfigurationChange,
AbortDeviceConfigurationChange: EQM_AbortDeviceConfigurationChange,
HasProperty: EQM_HasProperty,
IsPropertySettable: EQM_IsPropertySettable,
GetPropertyDataSize: EQM_GetPropertyDataSize,
GetPropertyData: EQM_GetPropertyData,
SetPropertyData: EQM_SetPropertyData,
StartIO: EQM_StartIO,
StopIO: EQM_StopIO,
GetZeroTimeStamp: EQM_GetZeroTimeStamp,
WillDoIOOperation: EQM_WillDoIOOperation,
BeginIOOperation: EQM_BeginIOOperation,
DoIOOperation: EQM_DoIOOperation,
EndIOOperation: EQM_EndIOOperation
)
_interfacePtr = withUnsafeMutablePointer(to: &_interface!) { $0 }
ref = withUnsafeMutablePointer(to: &_interfacePtr) { $0 }
return ref!
}
static func validateDriver (_ driverPointer: UnsafeMutableRawPointer?, reference: AudioServerPlugInDriverRef = EQMDriver.ref!) -> Bool {
guard driverPointer != nil else { return false }
let driver = driverPointer!.assumingMemoryBound(to: (UnsafeMutablePointer<AudioServerPlugInDriverInterface>?).self)
let valid = reference == driver
return valid
}
static func validateObject (_ objectID: AudioObjectID) -> Bool {
if getEQMObject(from: objectID) != nil {
return true
}
log("Invalid object for ID: \(objectID)")
return false
}
static func getEQMObject(from objectID: AudioObjectID) -> EQMObject.Type? {
switch objectID {
case kObjectID_PlugIn: return EQMPlugIn.self
case kObjectID_Device: return EQMDevice.self
case kObjectID_Stream_Input,
kObjectID_Stream_Output: return EQMStream.self
case kObjectID_Volume_Output_Master,
kObjectID_Mute_Output_Master,
kObjectID_DataSource_Output_Master: return EQMControl.self
default: return nil
}
}
static func getEQMObjectClassName (from objectID: AudioObjectID) -> String {
switch objectID {
case kObjectID_PlugIn: return "🟢 PlugIn"
case kObjectID_Device: return "🔴 Device"
case kObjectID_Stream_Input,
kObjectID_Stream_Output: return "🟠 Stream"
case kObjectID_Volume_Output_Master,
kObjectID_Mute_Output_Master,
kObjectID_DataSource_Output_Master: return "🔵 Control"
default: return "⚫️ Unknown"
}
}
static func calculateHostTicksPerFrame () {
// calculate the host ticks per frame
var theTimeBaseInfo = mach_timebase_info()
mach_timebase_info(&theTimeBaseInfo)
var theHostClockFrequency = Float64(theTimeBaseInfo.denom) / Float64(theTimeBaseInfo.numer)
theHostClockFrequency *= 1000000000.0
hostTicksPerFrame = theHostClockFrequency / EQMDevice.sampleRate
}
static func propertiesUpdated (objectId: AudioObjectID, changedProperties: [AudioObjectPropertyAddress]) {
guard let host = host, changedProperties.count > 0 else {
return
}
_ = host.pointee.PropertiesChanged(
host,
objectId,
UInt32(changedProperties.count),
changedProperties
)
}
}