How to use Swift and AVFoundation to stream/record USB microphone input?

I have a custom USB device that includes a microphone. I can see the microphone on macOS when I plug in the device so I know that it is working with the kernel and AV subsystems. I can enumerate and reference the microphone using AVCaptureDevice but I have not been able to figure out how to use this device reference with AVAudioEngine. I'm trying to accomplish two things with this microphone.

  1. I want to stream audio from the microphone and have it rendered to the speakers on my MacBook Pro.
  2. I want to capture sound data from the microphone and forward it to a live streaming API.

To my mind, from what I've read, I need AVAudioEngine to do this but I'm having trouble determining from the documentation just how to go about it on macOS. It seems that there is a lot more information for iOS or iPadOS but since USB-C support is sparsely documented on those operating systems, I'm focusing on the desktop (macOS) for now.

Can I convert an AVCaptureDevice into and audio input for AVAudioEngine? If not, how can I accomplish what I'm trying to do using whatever is available on AVFoundation?

Answered by statemachinejunkie in 777623022

Trial & error coupled with scouring the web has yielded at least a partial answer:

import AVFoundation
import Cocoa
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let engine = AVAudioEngine()
let inputNode = engine.inputNode
let format = inputNode.inputFormat(forBus: 0)
let outputNode = engine.outputNode

engine.connect(inputNode, to: outputNode, format: format)
//var bufferCount = 0

//input.installTap(onBus: 0, bufferSize: 2048, format: nil) { audioBuffer, audioTime in
//    bufferCount += 1
//    print(bufferCount)
//}

engine.prepare()

do {
    try engine.start()
} catch {
    print(error)
}

Ridiculously simple but amazingly difficult to stumble upon. At least, that was my experience. Next, I need a way to enumerate the inputs so I can pick the microphone I want to use. Anyone have a clue on how that is accomplished on macOS? During my time researching this problem, I noticed that things are much easier and much better documented for iOS.

Accepted Answer

Trial & error coupled with scouring the web has yielded at least a partial answer:

import AVFoundation
import Cocoa
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let engine = AVAudioEngine()
let inputNode = engine.inputNode
let format = inputNode.inputFormat(forBus: 0)
let outputNode = engine.outputNode

engine.connect(inputNode, to: outputNode, format: format)
//var bufferCount = 0

//input.installTap(onBus: 0, bufferSize: 2048, format: nil) { audioBuffer, audioTime in
//    bufferCount += 1
//    print(bufferCount)
//}

engine.prepare()

do {
    try engine.start()
} catch {
    print(error)
}

Ridiculously simple but amazingly difficult to stumble upon. At least, that was my experience. Next, I need a way to enumerate the inputs so I can pick the microphone I want to use. Anyone have a clue on how that is accomplished on macOS? During my time researching this problem, I noticed that things are much easier and much better documented for iOS.

How to use Swift and AVFoundation to stream/record USB microphone input?
 
 
Q