AVAudioSession .defaultToSpeaker更改麦克风输入

时间:2019-04-29 20:19:35

标签: ios swift avfoundation avaudioplayer audiokit

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

  

.defaultToSpeaker

有什么主意吗?

这里也记录了我想要做的是正确的:

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

更新: 我最小化了问题。没有球员只是麦克风。下面的代码是“ .defaultToSpeaker”时,mic无法“工作”。经过一些调试后,我意识到defaultToSpeaker将麦克风从“底部”切换到“前部”。还有

 try preferredPort.setPreferredDataSource(source)

似乎无法再次将其更改为底部。 (我可以为此提供代码)并且当category为defaultToSpeaker时,tap缓冲区的帧长显然是4800,而不是4410。这种差异似乎在我的代码中造成了麻烦,因为我需要44100。所以mic实际上正在工作,但是稍后在代码中失败了由于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类别,而是在不需要麦克风时在.playback和.record之间切换。这似乎也不容易,因为它需要大量启动/停止所有音频,这对于激活和停用AVAudioSession是必需的。但是,如果这样做,我可以提供更多代码。

1 个答案:

答案 0 :(得分:7)

感谢所有花时间发表评论的人。我从每条评论中学到了新东西。好像我找到了解决方案。这实际上很简单。当AVAudioSession类别为.defaultToSpeaker(或overrideOutputAudioPort)时,抽头输入缓冲区的帧长显然从4410变为4800。

无论使用哪种麦克风,这种情况都会发生。因此,使用

AVAudioSession.sharedInstance().setInputDataSource(datasource);

没有帮助。

这种差异似乎在我的代码后面会引起问题。因此mic实际上可以正常工作,但是在后来的代码中,由于帧长不同,它无法完成工作。

解决方案/解决方法是基本上将水龙头的帧长硬编码。由于我使用转换器,所以我认为这不会成为问题。这意味着我可以设置“ .defaultToSpeaker”,并且麦克风仍按预期运行。

  

容量= 4410(DUH!)

也许有不同/更好的方法来解决此问题。因此,请随意添加答案。