AVAudioSession .defaultToSpeaker 更改麦克风输入

AVAudioSession .defaultToSpeaker changes mic input

我有一个应用程序点击麦克风并根据麦克风输入播放声音(不必同时播放)下面的代码有效。但一个问题是输出在顶部的小扬声器上播放,而不是在底部的真正的扬声器上播放。我可以通过在播放器开始之前将3行放在下方来奇怪地解决这个问题,然后我可以听到扬声器的声音。 但随后麦克风停止收听!即使在玩家停止播放之后。基本上麦克风不喜欢

.defaultToSpeaker

有什么想法吗?

这里也记录了我正在尝试做的是正确的:

https://developer.apple.com/documentation/avfoundation/avaudiosession/categoryoptions/1616462-defaulttospeaker

更新: 我最小化了这个问题。没有播放器只有麦克风。下面的代码,当它是“.defaultToSpeaker”时,mic 不会 "work"。经过一些调试后,我意识到 defaultToSpeaker 将麦克风从 "bottom" 切换到 "front"。并且

 try preferredPort.setPreferredDataSource(source)

似乎无法再次将其更改为底部。 (我可以为此提供代码)并且当类别是 defaultToSpeaker 时,tap 缓冲区的帧长显然是 4800 而不是 4410。这种差异似乎在我的代码中造成了麻烦,因为我正好需要 44100。所以麦克风实际上在工作,但后来在代码中它失败了由于不同的 SR 来完成它的工作。下面的代码可以解释更多。

 func tapMicrophone() {
    try? AVAudioSession.sharedInstance().setActive(false)
    try? AVAudioSession.sharedInstance().setCategory(.playAndRecord,  options: [.defaultToSpeaker])
    //setBottomMic()
    try? AVAudioSession.sharedInstance().setActive(true)

    //tracker.start()
    let input = engine.inputNode
    let inputFormat = input.outputFormat(forBus: 0)
    let sampleRate = Double(11025)
    let outputFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: sampleRate, channels: 1, interleaved: true)!
    let converter = AVAudioConverter(from: inputFormat, to: outputFormat)!
    let inputBufferSize = 44100 //  100ms of 44.1K = 4410 samples.
    let sampleRateRatio = 44100 / sampleRate

    input.installTap(onBus: 0, bufferSize: AVAudioFrameCount(inputBufferSize), format: inputFormat) {
        buffer, time in
        var error: NSError? = nil
        let capacity = Int(Double(buffer.frameCapacity) / sampleRateRatio)
        let bufferPCM16 = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: AVAudioFrameCount(capacity))!
        converter.convert(to: bufferPCM16, error: &error) { inNumPackets, outStatus in
            outStatus.pointee = AVAudioConverterInputStatus.haveData
            return buffer
        }
    }

    engine.prepare()
    try! engine.start()

}

在这种情况下,我似乎有 2 个选择。解决麦克风级别的问题,如果可能,请使此代码与“.defaultToSpeaker”一起使用。或者不使用类别 .playandrecord 但在不需要 mic 时在 .playback 和 .record 之间切换。这似乎也不容易,因为它需要所有音频的大量 starting/stopping,这是激活和停用 AVAudioSession 所必需的。但如果这是要走的路,我可以提供更多代码。

看来我找到了解决办法。这其实很简单。当 AVAudioSession 类别为 .defaultToSpeaker(或 overrideOutputAudioPort)时,tap 输入缓冲区帧长显然从 4410 变为 4800。

无论使用哪个麦克风,都会发生这种奇怪的情况。所以使用

AVAudioSession.sharedInstance().setInputDataSource(datasource);

没有帮助。

这种差异似乎会在我的代码后面引起问题。所以麦克风实际上是工作的,但后来在代码中由于不同的帧长而无法完成它的工作。

Solution/workaround 是我基本上硬编码了 tap 中的帧长。因为我使用转换器,所以我不认为这是个问题。这意味着我可以设置“.defaultToSpeaker”并且麦克风仍按预期工作。

capacity = 4410 (DUH!)

也许有 different/better 方法可以解决这个问题。如果是这样,请随时添加您的答案。