swift 以编程方式更改音频输入不会更改音频引擎输入AVFoundation

clj7thdc  于 2023-06-21  发布在  Swift
关注(0)|答案(1)|浏览(100)

我正在尝试从AVAudioSession.sharedInstance().availableInputs更改所有可用设备之间的音频输入(麦克风)。我使用AVAudioSession.routeChangeNotification来获取自动路由更改时,设备连接/断开,并更改首选输入与setPreferredInput,然后我重新启动我的audioEngine,它工作正常。
但当我尝试以编程方式更改首选输入时,它不会更改音频捕获inputNode。但保留最后连接的设备和捕获。
即使AVAudioSession.sharedInstance().currentRoute.inputs发生了变化,但audioEngine?.inputNode不会更改为setPreferredInput调用。
WhatsApp似乎没有任何问题。

任何建议或线索都非常感谢。谢谢
这是一些相关的代码段。enableMic编程是一个问题。

private var audioEngine: AVAudioEngine?
    private var inputNode: AVAudioNode!

    private func setupAudioSession() {
        do {
            let session = AVAudioSession.sharedInstance()
            try session.setCategory(.playAndRecord, options: [.defaultToSpeaker, .allowBluetooth])
            try session.setActive(true)
        } catch {
            print(error)
        }
    }

    func setupAudioEngine() {
        audioEngine?.stop()
        audioEngine?.reset()

        setupAudioSession()

        audioEngine = AVAudioEngine()
        inputNode = audioEngine?.inputNode

        guard let hardwareSampleRate = audioEngine?.inputNode.inputFormat(forBus: 0).sampleRate else { return }

        let format = AVAudioFormat(standardFormatWithSampleRate: hardwareSampleRate, channels: 1)

        let requiredBufferSize: AVAudioFrameCount = 4800

        inputNode.installTap(onBus: 0,
                             bufferSize: requiredBufferSize,
                             format: format
        ) { [self] (buffer: AVAudioPCMBuffer, _: AVAudioTime) in
            // process the buffer
        }

        audioEngine?.prepare()
    }

    func startRecording() {
        do {
            try audioEngine?.start()
        } catch {
            print("Could not start audioEngine: \(error)")
        }
    }

    private func observeRouteChanges() {
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(handleRouteChange),
                                               name: AVAudioSession.routeChangeNotification,
                                               object: nil)
    }

    @objc func handleRouteChange(notification: Notification) {
        guard let userInfo = notification.userInfo,
              let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
              let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
            return
        }

        switch reason {

        case .newDeviceAvailable, .oldDeviceUnavailable:
            // enableMic with current route AVAudioSession.sharedInstance().currentRoute.inputs.first

        default: ()
        }

    }

    private func enableMic(mic: AVAudioSessionPortDescription?) {
        let session = AVAudioSession.sharedInstance()
        do {
            try session.setPreferredInput(mic)
            // re-setup the audio engine and start
        } catch {
            print(error)
        }
    }
c3frrgcw

c3frrgcw1#

也是AVFoundation的新增功能。对于你的问题
以编程方式更改音频输入不会更改音频引擎输入AVFoundation?是的,即使您以编程方式更改输入设备,它也不会更改inputNode属性。
请查看Apple关于此属性的文档:

inputNode不会改变。这是预期的AVAudioEngineinputNode属性是一个计算属性,在第一次访问它时,它会被惰性地(按需)计算。

首次访问inputNode属性时,AVAudioEngine将使用AVAudioEngine的默认AVAudioNode渲染格式创建AVAudioInputNode类的单例示例,以表示硬件的音频输入连接。此示例在音频引擎的处理图中处理音频输入,音频输入格式由音频硬件的属性自动确定。
一旦创建了节点,对输入节点的后续调用将返回相同的示例。
为了证明我写了一个演示应用程序,我想你已经尝试过了。看看这个方法,它是用来开始点击输入节点:

public func startMonitoring(){
    /* with out this code, installTap will crash due to 'required condition is false: format.sampleRate == hwFormat.sampleRate' install tap
    audioEngine = AVAudioEngine()
    */
    let inputNode = audioEngine.inputNode
    let inputFormat = inputNode.outputFormat(forBus: 0)
    // Install a tap on the audio engine with the buffer size and the input format.
    debugPrint("inputFormat:",inputFormat)
    audioEngine.inputNode.installTap(onBus: 0, bufferSize: AVAudioFrameCount(bufferSize), format: inputFormat) { buffer, _ in
      self.audioMetering(buffer: buffer)
    }
    audioEngine.prepare()
    do {
      try audioEngine.start()
    } catch {
      debugPrint("\(error.localizedDescription)")
    }
  }

如果您尝试在AVAudioEngineConfigurationChange通知之后调用此方法,您会发现应用程序在audioEngine.inputNode.installTap(onBus:, bufferSize:,format:)处崩溃,特别是当您在AirPods和内置麦克风之间切换时。这是因为inputNode在输入设备发生物理变化时不会更新。就像文档中说的那样,“它是在第一次访问时按需创建的”。
我希望这能澄清懒惰示例化如何为AVAudioEngine的inputNode属性工作。如果你还有什么问题就告诉我。
顺便说一句,这里是演示仓库link。该演示都有自动音频输入切换和手动音频输入切换。

相关问题