I am struggling to see why the following low-level audio recording function - which is based on tn2091 - Device input using the HAL Output Audio Unit - (a great article, btw, although a bit dated, and it would be wonderful if it was updated to use Swift and non deprecated stuff at some point!) fails to work under macOS:
func createMicUnit() -> AUAudioUnit {
let compDesc = AudioComponentDescription(
componentType: kAudioUnitType_Output,
componentSubType: kAudioUnitSubType_HALOutput, // I am on macOS, os this is good
componentManufacturer: kAudioUnitManufacturer_Apple,
componentFlags: 0, componentFlagsMask: 0)
return try! AUAudioUnit(componentDescription: compDesc, options: [])
}
func startMic() {
// mic permision is already granted at this point, but let's check
let status = AVCaptureDevice.authorizationStatus(for: AVMediaType.audio)
precondition(status == .authorized) // yes, all good
let unit = createMicUnit()
unit.isInputEnabled = true
unit.isOutputEnabled = false
precondition(!unit.canPerformInput) // can't record yet, and know why?
print(deviceName(unit.deviceID)) // "MacBook Pro Speakers" - this is why
let micDeviceID = defaultInputDeviceID
print(deviceName(micDeviceID)) // "MacBook Pro Microphone" - this is better
try! unit.setDeviceID(micDeviceID) // let's switch device to mic
precondition(unit.canPerformInput) // now we can record
print("\(String(describing: unit.channelMap))") // channel map is "nil" by default
unit.channelMap = [0] // not sure if this helps or not
let sampleRate = deviceActualFrameRate(micDeviceID)
print(sampleRate) // 48000.0
let format = AVAudioFormat(
commonFormat: .pcmFormatFloat32, sampleRate: sampleRate,
channels: 1, interleaved: false)!
try! unit.outputBusses[1].setFormat(format)
unit.inputHandler = { flags, timeStamp, frameCount, bus in
fatalError("never gets here") // now the weird part - this is never called!
}
try! unit.allocateRenderResources()
try! unit.startHardware() // let's go!
print("mic should be working now... why it doesn't?")
// from now on the (UI) app continues its normal run loop
}
All sanity checks pass with flying colors but unit's inputHandler
is not being called. Any idea why?
Thank you!