diff --git a/native/driver/Source/Constants.swift b/native/driver/Source/Constants.swift index f87dfca..34ea631 100644 --- a/native/driver/Source/Constants.swift +++ b/native/driver/Source/Constants.swift @@ -30,7 +30,7 @@ let kDeviceManufacturer = "Bitgapp Ltd" let kBoxDefaultName = "eqMac Box" let kPlugInBundleId = "com.bitgapp.eqmac.driver" -let kBoxUID = "eqMacBox_UID" +let kBoxUID = "EQMBox" let kDeviceUID = "EQMDevice" let kDeviceModelUID = "EQMDeviceModelUID" diff --git a/native/driver/Source/EQMBox.swift b/native/driver/Source/EQMBox.swift index 2e343d8..f91bfde 100644 --- a/native/driver/Source/EQMBox.swift +++ b/native/driver/Source/EQMBox.swift @@ -137,7 +137,11 @@ class EQMBox: EQMObject { return .integer(0) case kAudioBoxPropertyDeviceList: // This is used to indicate which devices came from this box - return .integer(acquired ? kObjectID_Device : 0) + if acquired { + return .integer(kObjectID_Device) + } else { + return .null() + } default: return nil } } diff --git a/native/driver/Source/EQMDevice.swift b/native/driver/Source/EQMDevice.swift index 8d07d1a..aa24d4b 100644 --- a/native/driver/Source/EQMDevice.swift +++ b/native/driver/Source/EQMDevice.swift @@ -113,7 +113,7 @@ class EQMDevice: EQMObject { case kAudioObjectPropertyControlList: return 6 * sizeof(AudioObjectID.self) case kAudioDevicePropertySafetyOffset: return sizeof(UInt32.self) case kAudioDevicePropertyNominalSampleRate: return sizeof(Float64.self) - case kAudioDevicePropertyAvailableNominalSampleRates: return 6 * sizeof(AudioValueRange.self) + case kAudioDevicePropertyAvailableNominalSampleRates: return UInt32(kSupportedSamplingRates.count) * sizeof(AudioValueRange.self) case kAudioDevicePropertyIsHidden: return sizeof(UInt32.self) case kAudioDevicePropertyPreferredChannelsForStereo: return 2 * sizeof(UInt32.self) case kAudioDevicePropertyPreferredChannelLayout: @@ -183,7 +183,8 @@ class EQMDevice: EQMObject { kObjectID_Volume_Output_Master, kObjectID_Mute_Output_Master, kObjectID_DataSource_Output_Master, - ]) default: return .objectIDList([]) + ]) + default: return .objectIDList([]) } case kAudioDevicePropertyDeviceUID: // This is a CFString that is a persistent token that can identify the same @@ -335,7 +336,8 @@ class EQMDevice: EQMObject { mNumberChannelDescriptions: UInt32(kChannelCount), mChannelDescriptions: AudioChannelDescription() ) - let channelSize = MemoryLayout.stride * channelDescriptions.count + // INFO: have to go with channelDescriptions.count - 1 as MemoryLayout.stride might already contain the size for 1 channel + let channelSize = MemoryLayout.stride * channelDescriptions.count - 1 memcpy(&channelLayout.mChannelDescriptions, &channelDescriptions, channelSize) return .channelLayout(channelLayout) case kAudioDevicePropertyZeroTimeStampPeriod: diff --git a/native/driver/Source/EQMDriver.swift b/native/driver/Source/EQMDriver.swift index 0e6534b..2c7cd00 100644 --- a/native/driver/Source/EQMDriver.swift +++ b/native/driver/Source/EQMDriver.swift @@ -115,12 +115,12 @@ import CoreAudio.AudioServerPlugIn return "Unknown" } - static func calculateHostTicksPerFrame () -> Float64 { + 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 - return theHostClockFrequency / EQMDevice.sampleRate + hostTicksPerFrame = theHostClockFrequency / EQMDevice.sampleRate } } diff --git a/native/driver/Source/EQMInterface.swift b/native/driver/Source/EQMInterface.swift index ccf311b..88e3a86 100644 --- a/native/driver/Source/EQMInterface.swift +++ b/native/driver/Source/EQMInterface.swift @@ -84,11 +84,10 @@ func EQM_Initialize (inDriver: AudioServerPlugInDriverRef, inHost: AudioServerPl if (EQMBox.name == nil) { EQMBox.name = kBoxDefaultName } + + EQMDriver.calculateHostTicksPerFrame() - EQMBox.acquired = true - EQMDriver.hostTicksPerFrame = EQMDriver.calculateHostTicksPerFrame() - - return kAudioHardwareNoError + return noErr } func EQM_CreateDevice (inDriver: AudioServerPlugInDriverRef, inDescription: CFDictionary, inClientInfo: UnsafePointer, outDeviceObjectID: UnsafeMutablePointer) -> OSStatus { @@ -150,9 +149,9 @@ func EQM_PerformDeviceConfigurationChange (inDriver: AudioServerPlugInDriverRef, if !kSupportedSamplingRates.contains(where: { UInt64($0) == inChangeAction }) { return kAudioHardwareBadObjectError } EQMDevice.sampleRate = Float64(inChangeAction) - EQMDriver.hostTicksPerFrame = EQMDriver.calculateHostTicksPerFrame() + EQMDriver.calculateHostTicksPerFrame() - return kAudioHardwareNoError + return noErr } func EQM_AbortDeviceConfigurationChange (inDriver: AudioServerPlugInDriverRef, inDeviceObjectID: AudioObjectID, inChangeAction: UInt64, inChangeInfo: UnsafeMutableRawPointer?) -> OSStatus { diff --git a/native/driver/Source/EQMObject.swift b/native/driver/Source/EQMObject.swift index d2cb7c0..3f4e4c0 100644 --- a/native/driver/Source/EQMObject.swift +++ b/native/driver/Source/EQMObject.swift @@ -19,6 +19,7 @@ protocol EQMObject: class { enum EQMObjectProperty { // Primitives + case null (Void? = nil) case audioClassID(AudioClassID) case bool(CFBoolean) case string(CFString) @@ -43,6 +44,7 @@ enum EQMObjectProperty { func write(to address: UnsafeMutableRawPointer?, size: UnsafeMutablePointer, requestedSize: UInt32?) { switch self { + case .null(_): self.write(element: NSNull(), address: address, size: size) case .bool(let data): self.write(element: data, address: address, size: size) case .string(let data): self.write(element: data, address: address, size: size) case .float32(let data): self.write(element: data, address: address, size: size) @@ -67,10 +69,12 @@ enum EQMObjectProperty { private func write(element: T, address: UnsafeMutableRawPointer?, size: UnsafeMutablePointer) { - // Write data size - size.pointee = UInt32(MemoryLayout.size(ofValue: element)) - // Write data - address?.assumingMemoryBound(to: T.self).pointee = element + if T.self != NSNull.self { + size.pointee = UInt32(MemoryLayout.size(ofValue: element)) + address?.assumingMemoryBound(to: T.self).pointee = element + } else { + size.pointee = 0 + } } private func write(array arr: ContiguousArray, address: UnsafeMutableRawPointer?, size: UnsafeMutablePointer, requestedSize: UInt32?) { diff --git a/native/driver/Source/EQMPlugIn.swift b/native/driver/Source/EQMPlugIn.swift index 66fba9a..dfddf5a 100644 --- a/native/driver/Source/EQMPlugIn.swift +++ b/native/driver/Source/EQMPlugIn.swift @@ -32,18 +32,7 @@ class EQMPlugIn: EQMObject { static func isPropertySettable (objectID: AudioObjectID? = nil, address: AudioObjectPropertyAddress) -> Bool { switch address.mSelector { - case kAudioObjectPropertyBaseClass, - kAudioObjectPropertyClass, - kAudioObjectPropertyOwner, - kAudioObjectPropertyManufacturer, - kAudioObjectPropertyOwnedObjects, - kAudioPlugInPropertyBoxList, - kAudioPlugInPropertyTranslateUIDToBox, - kAudioPlugInPropertyDeviceList, - kAudioPlugInPropertyTranslateUIDToDevice, - kAudioPlugInPropertyResourceBundle, - kAudioObjectPropertyCustomPropertyInfoList: return true - default: return false + default: return false } } @@ -88,19 +77,35 @@ class EQMPlugIn: EQMObject { case kAudioObjectPropertyManufacturer: // This is the human readable name of the maker of the plug-in. return .string(kDeviceManufacturer as CFString) + case kAudioObjectPropertyOwnedObjects: - return .objectIDList([kObjectID_Box, kObjectID_Device]) + var objects = ContiguousArray() + objects.append(kObjectID_Box) + if EQMBox.acquired { + objects.append(kObjectID_Device) + } + return .objectIDList(objects) + case kAudioPlugInPropertyBoxList: return .objectIDList([kObjectID_Box]) + case kAudioPlugInPropertyTranslateUIDToBox: let uid = inData?.assumingMemoryBound(to: CFString?.self).pointee + if (CFStringCompare(uid, kBoxUID as CFString, .init(rawValue: 0)) == CFComparisonResult.compareEqualTo) { return .integer(kObjectID_Box) } else { return .integer(kAudioObjectUnknown) } + case kAudioPlugInPropertyDeviceList: - return .objectIDList([kObjectID_Device]) + var devices = ContiguousArray() + + if EQMBox.acquired { + devices.append(kObjectID_Device) + } + return .objectIDList(devices) + case kAudioPlugInPropertyTranslateUIDToDevice: // This property takes the CFString passed in the qualifier and converts that // to the object ID of the device it corresponds to. For this driver, there is diff --git a/native/driver/Source/EQMStream.swift b/native/driver/Source/EQMStream.swift index 387e747..4abd9c3 100644 --- a/native/driver/Source/EQMStream.swift +++ b/native/driver/Source/EQMStream.swift @@ -72,7 +72,7 @@ class EQMStream: EQMObject { case kAudioStreamPropertyVirtualFormat, kAudioStreamPropertyPhysicalFormat: return sizeof(AudioStreamBasicDescription.self) case kAudioStreamPropertyAvailableVirtualFormats, - kAudioStreamPropertyAvailablePhysicalFormats: return 6 * sizeof(AudioStreamRangedDescription.self) + kAudioStreamPropertyAvailablePhysicalFormats: return UInt32(kSupportedSamplingRates.count) * sizeof(AudioStreamRangedDescription.self) default: return nil }