AvaudioEngine - 如何以特定的采样率录制声音以供分析

9

我们正在开发一个从外部麦克风录制声音的项目。为了分析目的,我们需要大约5k Hz的采样率。

我们使用AvAudioEngine来录制声音。由于我们知道苹果设备无法以特定的速率进行录制,因此我们使用AVAudioConverter降低采样率。

但正如您所知,这类似于压缩,因此我们降低采样率越低,文件大小和时长也会相应减少。这目前正在发生(如果我在这方面有任何错误,请纠正我)。

问题

问题是,降低采样率会缩短文件长度,并影响计算和分析。例如,一小时的录音被降级为45分钟。假设我们对5分钟的时间间隔进行分析,那么这将导致错误。

最好的解决方案是什么?

查询

我们在互联网上搜索过,但我们无法弄清楚installTap上的缓冲区大小会产生什么影响。在当前代码中,我们将其设置为2688。

有人可以澄清吗?

代码

let bus = 0
let inputNode = engine.inputNode

let equalizer = AVAudioUnitEQ(numberOfBands: 2)

equalizer.bands[0].filterType = .lowPass
equalizer.bands[0].frequency = 3000
equalizer.bands[0].bypass = false

equalizer.bands[1].filterType = .highPass
equalizer.bands[1].frequency = 1000
equalizer.bands[1].bypass = false
engine.attach(equalizer) //Attach equalizer

// Connect nodes
engine.connect(inputNode, to: equalizer, format: inputNode.inputFormat(forBus: 0))
engine.connect(equalizer, to: engine.mainMixerNode, format: inputNode.inputFormat(forBus: 0))

// call before creating converter because this changes the mainMixer's output format
engine.prepare()

let outputFormat = AVAudioFormat(commonFormat: .pcmFormatInt16,
                                 sampleRate: 5000,
                                 channels: 1,
                                 interleaved: false)!

// Downsampling converter
guard let converter: AVAudioConverter = AVAudioConverter(from: engine.mainMixerNode.outputFormat(forBus: 0), to: outputFormat) else {
    print("Can't convert in to this format")
    return
}

engine.mainMixerNode.installTap(onBus: bus, bufferSize: 2688, format: nil) { (buffer, time) 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
        }
    }
    
    
    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)
    
    
    if status == .haveData {
        // Process with converted buffer
    }
}

do {
    try engine.start()
} catch {
    print("Can't start the engine: \(error)")
}

预期结果

我们可以接受对缓冲区的压缩,但我们希望输出文件具有相同的录制时长。如果我们录制10分钟,则输出文件应该包含10分钟的数据。


1
一旦您有解决方案,如果您能分享给我,我将不胜感激。 - Kushal Maniyar
我不知道你所说的“降低采样率,文件大小和持续时间受到相同影响”的意思。我也不理解“降低采样率会缩短文件长度”... 我想我理解了“1小时的录音被降级为45分钟”。那么,你是否惊讶于较低的采样率会导致更小的文件?这有什么问题吗?当你没有对转换后的缓冲区进行任何处理时,你怎么知道文件大小呢?它只是说“//使用转换后的缓冲区进行处理”。 - Nerdy Bunz
@NerdyBunz,我期望文件大小会减小,但我很惊讶为什么音频文件的持续时间也减少了。所以假设我们压缩任何音频,它会减小文件的大小而不是持续时间(总时间)。但在我的情况下,输出文件中的总录制时间与实际录制时间不同。 - Bhavin Vaghela
1个回答

2
数字化音频没有固有的持续时间,因为它可以以任何采样率播放。为了得到预期的文件持续时间,每个阶段的采样率必须符合您的预期:录制、处理和播放。
我怀疑可能发生了以下两种情况之一: A) 在installtap中接收到的缓冲区的采样率不是您所假设的那个...并且您正在从错误的格式进行转换。 B) 您正在以与您所假设的不同的采样率播放音频。(您如何知道您的播放器正在以5000hz播放?)
要检查这一点,您需要将过程分解成较小的部分,并检查每个阶段的采样率。

感谢您的清晰解释。在我的情况中,问题不在代码上,也不是播放问题。问题出现在将缓冲区写入文件时。我在接收回调时立即保存缓冲区。因此,我没有直接将缓冲区保存到文件中,而是将其存储在本地变量中,并在用户按下停止录制按钮后一次性将所有数据保存到文件中。我会为您找到问题的准确性点赞。谢谢 :) - Bhavin Vaghela

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