AVAudioEngine 降采样问题

6
我遇到了一个问题,需要对来自麦克风的音频进行下采样。我使用AVAudioEngine从麦克风中获取样本,以下是相关代码:
assert(self.engine.inputNode != nil)
let input = self.engine.inputNode!

let audioFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 8000, channels: 1, interleaved: false)    
let mixer = AVAudioMixerNode()
engine.attach(mixer)
engine.connect(input, to: mixer, format: input.inputFormat(forBus: 0))

do {
    try engine.start()

    mixer.installTap(onBus: 0, bufferSize: 1024, format: audioFormat, block: {
            (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
        //some code here
    })

} catch let error {
    print(error.localizedDescription)
}

这段代码在iPhone 5s上表现良好,因为麦克风输入为8000Hz,并且缓冲区被麦克风的数据填充。问题是我想从iPhone 6s(及以上版本)进行录制,其麦克风记录为16000Hz。如果我将混音器节点连接到引擎的主混音器节点中(使用以下代码),情况就变得奇怪了:
engine.connect(mixer, to: mainMixer, format: audioFormat)

这个方法实际上是可行的,我得到的缓冲区格式为8000Hz,声音经过完美的下采样处理,唯一的问题是声音也从扬声器中输出,而我不希望如此(如果我不连接它,缓冲区为空)。

有人知道如何解决这个问题吗?

非常感谢任何帮助、意见或想法。

3个回答

5

另一种使用 AVAudioConverterSwift 5 中处理此问题的方法。

let engine = AVAudioEngine()


func setup() {

    let input = engine.inputNode
    let bus = 0
    let inputFormat = input.outputFormat(forBus: bus )
    guard let outputFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 8000, channels: 1, interleaved: true), let converter = AVAudioConverter(from: inputFormat, to: outputFormat) else{
        return
    }

    input.installTap(onBus: bus, bufferSize: 1024, format: inputFormat) { (buffer, time) -> Void in
        var newBufferAvailable = true

        let inputCallback: AVAudioConverterInputBlock = { inNumPackets, outStatus in
            if newBufferAvailable {
                outStatus.pointee = .haveData
                newBufferAvailable = false
                return buffer
            } else {
                outStatus.pointee = .noDataNow
                return nil
            }
        }

        if let convertedBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: AVAudioFrameCount(outputFormat.sampleRate) * buffer.frameLength / AVAudioFrameCount(buffer.format.sampleRate)){
            var error: NSError?
            let status = converter.convert(to: convertedBuffer, error: &error, withInputFrom: inputCallback)
            assert(status != .error)

            // 8kHz buffers
            print(convertedBuffer.format)
        }
    }
    do {
        try engine.start()
    } catch { print(error) }
}

接受的方法只给了我空缓冲区。使用转换器的解决方案对我有用,谢谢! - bobski
嗨,伙计,你能帮我解决这个问题吗:https://dev59.com/eWAKtIcB2Jgan1znfj-Q - famfamfam
刚看到了。修复了崩溃,然后你可以通过设备获得48000。转换后你可以获得44100。 - dengST30

3

我通过将混音器的音量调整为0来解决了这个问题。

mixer.volume = 0

这使我能够利用引擎主混音器惊人的能力将任何采样率重新采样为所需的采样率,并且不会听到直接从扬声器中传出的麦克风反馈回路。如果有任何需要澄清的地方,请让我知道。

这是我的代码:

assert(self.engine.inputNode != nil)
let input = self.engine.inputNode!

let audioFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 8000, channels: 1, interleaved: false)    
let mixer = AVAudioMixerNode()
engine.attach(mixer)
engine.connect(input, to: mixer, format: input.inputFormat(forBus: 0))
mixer.volume = 0
engine.connect(mixer, to: mainMixer, format: audioFormat)

do {
    try engine.start()

    mixer.installTap(onBus: 0, bufferSize: 1024, format: audioFormat, block: {
        (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
        //some code here
    })

} catch let error {
    print(error.localizedDescription)
}

你在哪里定义了 "mainMixer"? - Osman
我写这段代码已经有一段时间了,但我95%确定那是AVAudioEngine的主混音节点。 - nullforlife
3
如果我使用这段代码,它会在缓冲区中给我所有的零。你知道我做错了什么吗?我正在使用iPhone 7,它的麦克风输入采样率为44100Hz。 - Robert Veringa
@RobertVeringa 奇怪,你是在真机上运行还是在模拟器上?因为在模拟器上无法工作。你也可以尝试测试我问题中的第一段代码,看看是否能从扬声器中听到任何声音。此外,你可以尝试将 AudioFormat 更改为 input.inputFormat(forBus: 0),以查看是否可以在缓冲区中获取任何数据。 - nullforlife
感谢您的回复。我不得不将音频引擎的首选采样率配置为16000Hz,并对其进行下采样以达到8000Hz。 - Robert Veringa
显示剩余5条评论

1
我发现唯一有效的改变采样率的方法是:
AVAudioSettings.sharedInstance().setPreferredSampleRate(...)

很遗憾,您不能保证获得所需的采样率,尽管似乎8000、12000、16000、22050、44100都有效。以下方法不起作用:1)在tap off engine.inputNode中设置自定义格式(异常);2)添加一个带有自定义格式的混音器,并对其进行分流(异常);3)添加一个混音器,将其与inputNode的格式连接,将该混音器与具有自定义格式的主混音器连接,然后删除输出节点的输入,以便不向扬声器发送音频并获得即时反馈(工作,但得到全零);4)在AVAudioEngine中根本不使用自定义格式,并使用AVAudioConverter从我的tap中的硬件速率转换[缓冲区的长度未设置,无法确定结果是否正确]。

2
@matt 这个答案在四个问题中都很相关,而且我在解决自己的问题时碰巧遇到了这四个问题,所以我觉得把它保存下来对其他人也会有帮助。那么我应该怎么做呢?选择一个地方回答并在其他地方放置链接吗? - prewett
对我没用,你能帮忙吗?https://dev59.com/eWAKtIcB2Jgan1znfj-Q - famfamfam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接