如何在 Avfoundation 中正确更改采样率

Posted

技术标签:

【中文标题】如何在 Avfoundation 中正确更改采样率【英文标题】:How to change sample rate properly in Avfoundation 【发布时间】:2018-10-27 05:00:12 【问题描述】:

我已经完成了这个简单的程序。它所做的只是同时记录和回放缓冲区。如果采样率为 44100 赫兹,一切正常,但如果我将采样率更改为 16000 或 8000,它根本不会产生任何声音,或者可能是一些听不见的白噪声。为什么会发生这种情况?

如何以不同的采样率记录?

我尝试过的以下代码:

import UIKit
import AVFoundation

class ViewController: UIViewController  

var engine = AVAudioEngine()
let player = AVAudioPlayerNode()
let audiosession = AVAudioSession.sharedInstance()
let newSrc:UnsafeMutablePointer<Float>! = nil
override func viewDidLoad() 
super.viewDidLoad()



let audioSession = AVAudioSession.sharedInstance()
print(audioSession.sampleRate) // here it prints 44100 hz. because it still using the internal mic.
do 

    try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, with: .allowBluetooth)
    try audioSession.setMode(AVAudioSessionModeDefault)
    try audioSession.setActive(true)

 catch 

print(audioSession.sampleRate) // here it will print 16000 hz if my bluetooth earbuds is connected, if not it will be 44100 hz.

let input = engine.inputNode
let bus = 0
let mixer = AVAudioMixerNode() // creating mixer as it is needed to set audio format

engine.attach(mixer)
engine.attach(player)
engine.connect(input, to: mixer, format: input.outputFormat(forBus: 0))

let inputFormat = input.inputFormat(forBus: bus)

engine.connect(player, to: engine.mainMixerNode, format: input.inputFormat(forBus: 0))

let fmt = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100.0, channels: 1, interleaved: false)

mixer.installTap(onBus: bus, bufferSize: 1024, format: fmt)  (buffer, time) -> Void in

    print(buffer.format)
    print(buffer.floatChannelData)
    print(buffer.format.streamDescription.pointee.mBytesPerFrame)
    self.player.scheduleBuffer(buffer)
    if self.player.isPlaying 
        print("true")
    



engine.prepare()
do
    try! engine.start()
    player.play()
 catch 
    print(error)




【问题讨论】:

【参考方案1】:

作为discussed here,AVAudioEngine 混音器节点和分接头都不会为您进行速率转换。实际上,在您的情况下,混音龙头不是抛出或记录错误,而是静默地(明白吗?)让您保持沉默。

由于您不能使用AVAudioMixerNode 进行速率转换,您可以将其替换为方便的AVAudioConverter,确保设置AVAudioPlayerNode 的正确输出格式,因为

在播放缓冲区时,有一个隐含的假设,即缓冲区是相同的 采样率作为节点的输出格式。

如果您不这样做,您可能会听到间隙和/或音高偏移的音频。

像这样

let input = engine.inputNode
let bus = 0
let inputFormat = input.inputFormat(forBus: bus)

engine.attach(player)

let fmt = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 8000, channels: 1, interleaved: false)!
engine.connect(player, to: engine.mainMixerNode, format: fmt)

let converter = AVAudioConverter(from: inputFormat, to: fmt)!

input.installTap(onBus: bus, bufferSize: 1024, format: inputFormat)  (buffer, time) -> Void in
    let inputCallback: AVAudioConverterInputBlock =  inNumPackets, outStatus in
        outStatus.pointee = AVAudioConverterInputStatus.haveData
        return buffer
    

    let convertedBuffer = AVAudioPCMBuffer(pcmFormat: fmt, frameCapacity: AVAudioFrameCount(fmt.sampleRate) * buffer.frameLength / AVAudioFrameCount(buffer.format.sampleRate))!

    var error: NSError? = nil
    let status = converter.convert(to: convertedBuffer, error: &error, withInputFrom: inputCallback)
    assert(status != .error)

    print(convertedBuffer.format)
    print(convertedBuffer.floatChannelData)
    print(convertedBuffer.format.streamDescription.pointee.mBytesPerFrame)
    self.player.scheduleBuffer(convertedBuffer)

【讨论】:

谢谢。我已经想通了,并通过使用 AVAudioConverter 使其工作。我真的认为这可以在不使用转换器的情况下完成,这就是为什么打开这个线程。关于播放器节点,是的,我听到了音调/移位,然后我的洞察力突然起作用了,我尝试将其设置为播放我用于 AudioConverter 的格式。但是您现在的回答清楚地表明,不使用 Converter 是不可能的,但我的问题是,为什么他们给了我们一个选项?如果我们不能设置录制格式,那为什么还有一个选项呢? 头文件doco说attempts to apply this as the format of the specified output bus. This should only be done when attaching to an output bus which is not connected to another node; an error will result otherwise.我很高兴它尝试了,但我不知道它什么时候应该工作,也不知道为什么它没有给出错误就失败了。【参考方案2】:

这个解决方案对我有用

let fmt = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: false)!
    inputNode.installTap(onBus: 0, bufferSize: 1024, format: fmt)  (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
        self.recognitionRequest?.append(buffer)
    

【讨论】:

以上是关于如何在 Avfoundation 中正确更改采样率的主要内容,如果未能解决你的问题,请参考以下文章

如何更改插孔音频中的采样率?

如何在 Windows 7 中更改音频输出设备的默认共享模式采样率?

如何获取音频块以在核心音频或 AVFoundation 中进行分析

iOS:在音频单元中动态更改采样率

在 iOS 上更改 AUGraph 的采样率

更改 AudioContext (getUserMedia) 的采样率