我正在尝试使用AVAudioPCMBuffer
将确定的AVAudioPCMBuffer
(44.1khz,1ch,float32,不交错)转换为另一个AVAudioConverter
(16khz,1ch,int16,不交错)用AVAudioFile
写下来。
我的代码使用库AudioKit
和点击AKLazyTap
根据此来源在每个确定的时间获取缓冲区:
https://github.com/AudioKit/AudioKit/tree/master/AudioKit/Common/Taps/Lazy%20Tap
这是我的实施:
lazy var downAudioFormat: AVAudioFormat = {
let avAudioChannelLayout = AVAudioChannelLayout(layoutTag: kAudioChannelLayoutTag_Mono)!
return AVAudioFormat(
commonFormat: .pcmFormatInt16,
sampleRate: 16000,
interleaved: false,
channelLayout: avAudioChannelLayout)
}()
//...
AKSettings.sampleRate = 44100
AKSettings.numberOfChannels = AVAudioChannelCount(1)
AKSettings.ioBufferDuration = 0.002
AKSettings.defaultToSpeaker = true
//...
let mic = AKMicrophone()
let originalAudioFormat: AVAudioFormat = mic.avAudioNode.outputFormat(forBus: 0) //41.100, 1ch, float32...
let inputFrameCapacity = AVAudioFrameCount(1024)
//I don't think this is correct, the audio is getting chopped...
//How to calculate it correctly?
let outputFrameCapacity = AVAudioFrameCount(512)
guard let inputBuffer = AVAudioPCMBuffer(
pcmFormat: originalAudioFormat,
frameCapacity: inputFrameCapacity) else {
fatalError()
}
// Your timer should fire equal to or faster than your buffer duration
bufferTimer = Timer.scheduledTimer(
withTimeInterval: AKSettings.ioBufferDuration/2,
repeats: true) { [weak self] _ in
guard let unwrappedSelf = self else {
return
}
unwrappedSelf.lazyTap?.fillNextBuffer(inputBuffer, timeStamp: nil)
// This is important, since we're polling for samples, sometimes
//it's empty, and sometimes it will be double what it was the last call.
if inputBuffer.frameLength == 0 {
return
}
//This converter is only create once, as the AVAudioFile. Ignore this code I call a function instead.
let converter = AVAudioConverter(from: originalAudioFormat, to: downAudioFormat)
converter.sampleRateConverterAlgorithm = AVSampleRateConverterAlgorithm_Normal
converter.sampleRateConverterQuality = .min
converter.bitRateStrategy = AVAudioBitRateStrategy_Constant
guard let outputBuffer = AVAudioPCMBuffer(
pcmFormat: converter.outputFormat,
frameCapacity: outputFrameCapacity) else {
print("Failed to create new buffer")
return
}
let inputBlock: AVAudioConverterInputBlock = { inNumPackets, outStatus in
outStatus.pointee = AVAudioConverterInputStatus.haveData
return inputBuffer
}
var error: NSError?
let status: AVAudioConverterOutputStatus = converter.convert(
to: outputBuffer,
error: &error,
withInputFrom: inputBlock)
switch status {
case .error:
if let unwrappedError: NSError = error {
print(unwrappedError)
}
return
default: break
}
//Only created once, instead of this code my code uses a function to verify if the AVAudioFile has been created, ignore it.
outputAVAudioFile = try AVAudioFile(
forWriting: unwrappedCacheFilePath,
settings: format.settings,
commonFormat: format.commonFormat,
interleaved: false)
do {
try outputAVAudioFile?.write(from: avAudioPCMBuffer)
} catch {
print(error)
}
}
(请注意,AVAudioConverter
和AVAudioFile
正在重复使用,初始化并不能代表我的代码上的实际实现,只是为了简化并使其更简单理解。)
frameCapacity
上的outputBuffer: AVAudioPCMBuffer
设置为512,音频会被切断。有没有办法为这个缓冲区发现正确的frameCapacity
?
使用Swift 4和AudioKit 4.1编写。
非常感谢!
答案 0 :(得分:1)
我设法解决了这个问题,在Tap
上安装了inputNode
,如下所示:
lazy var downAudioFormat: AVAudioFormat = {
let avAudioChannelLayout = AVAudioChannelLayout(layoutTag: kAudioChannelLayoutTag_Mono)!
return AVAudioFormat(
commonFormat: .pcmFormatInt16,
sampleRate: SAMPLE_RATE,
interleaved: true,
channelLayout: avAudioChannelLayout)
}()
private func addBufferListener(_ avAudioNode: AVAudioNode) {
let originalAudioFormat: AVAudioFormat = avAudioNode.inputFormat(forBus: 0)
let downSampleRate: Double = downAudioFormat.sampleRate
let ratio: Float = Float(originalAudioFormat.sampleRate)/Float(downSampleRate)
let converter: AVAudioConverter = buildConverter(originalAudioFormat)
avAudioNode.installTap(
onBus: 0,
bufferSize: AVAudioFrameCount(downSampleRate * 2),
format: originalAudioFormat,
block: { (buffer: AVAudioPCMBuffer!, _ : AVAudioTime!) -> Void in
let capacity = UInt32(Float(buffer.frameCapacity)/ratio)
guard let outputBuffer = AVAudioPCMBuffer(
pcmFormat: self.downAudioFormat,
frameCapacity: capacity) else {
print("Failed to create new buffer")
return
}
let inputBlock: AVAudioConverterInputBlock = { inNumPackets, outStatus in
outStatus.pointee = AVAudioConverterInputStatus.haveData
return buffer
}
var error: NSError?
let status: AVAudioConverterOutputStatus = converter.convert(
to: outputBuffer,
error: &error,
withInputFrom: inputBlock)
switch status {
case .error:
if let unwrappedError: NSError = error {
print("Error \(unwrappedError)"))
}
return
default: break
}
self.delegate?.flushAudioBuffer(outputBuffer)
})
}