如何在Avfoundation中正确更改采样率 [英] How to change sample rate properly in Avfoundation
问题描述
我已经完成了这个简单的程序.它的作用是只记录并同时播放缓冲区.如果采样率是44100 Hz,一切正常,但是如果我将采样率更改为16000或8000,则它根本不会发出任何声音,或者可能会产生一些白噪声,这是不可审计的.为什么会这样?
I have done this simple program. what it does is it just record and play back the buffers simultaneously. Everything works fine if the sample rate is 44100 hz but if I change the sample rate to 16000 or 8000, it doesn't producing any sound at all or may be some white noise which is not audiable.Why is this happening?
如何记录不同的采样率?
How can I record with different sample rate?
以下我尝试过的代码:
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)
}
}
}
推荐答案
如在此处讨论,AVAudioEngine
混合器节点和分接头都不会为您进行费率转换.实际上,在您的情况下,调音器无声地敲打(明白吗?)可以使您保持沉默,而不是引发或记录错误.
As discussed here, neither AVAudioEngine
mixer nodes nor taps will do rate conversion for you. In fact in your case instead of throwing or logging an error, the mixer tap silently (get it?) gives you silence.
由于不能使用AVAudioMixerNode
进行费率转换,因此可以将其替换为方便的AVAudioConverter
,请确保设置AVAudioPlayerNode
的正确输出格式,因为
Since you can't use an AVAudioMixerNode
for rate conversion, you can replace it with the convenient AVAudioConverter
, making sure to set the correct output format of the AVAudioPlayerNode
because
在播放缓冲区时,有一个隐含的假设,即缓冲区处于同一位置 采样率作为节点的输出格式.
When playing buffers, there is an implicit assumption that the buffers are at the same sample rate as the node's output format.
如果不这样做,您可能会听到间隙和/或音高变化的音频.
If you don't do this you may hear gaps and/or pitch shifted audio.
像这样
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)
}
这篇关于如何在Avfoundation中正确更改采样率的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!