首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将AVAudioPCMBuffer转换为另一个AVAudioPCMBuffer

将AVAudioPCMBuffer转换为另一个AVAudioPCMBuffer
EN

Stack Overflow用户
提问于 2018-02-16 16:51:13
回答 1查看 2.6K关注 0票数 3

我正在尝试将一个确定的AVAudioPCMBuffer (44.1khz,1ch,float32,非交错)转换为使用AVAudioConverter的另一个AVAudioPCMBuffer (16 and,1ch,int16,非交织),并使用AVAudioFile编写它。我的代码使用库AudioKit和tap AKLazyTap来获取每个确定时间的缓冲区,基于这个源:

https://github.com/AudioKit/AudioKit/tree/master/AudioKit/Common/Taps/Lazy%20Tap

以下是我的实现:

代码语言:javascript
运行
复制
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)
  }

}

(请注意,AVAudioConverterAVAudioFile正在被重用,那里的初始化并不代表我的代码上的真正实现,只是为了简化并使其更容易理解。)

frameCapacityoutputBuffer: AVAudioPCMBuffer上设置为512时,音频就会被切断。有任何方法可以为这个缓冲区找到正确的frameCapacity吗?

使用Swift 4和AudioKit 4.1编写。

非常感谢!

EN

回答 1

Stack Overflow用户

发布于 2018-02-19 17:36:48

我设法解决了在Tap上安装inputNode的问题,如下所示:

代码语言:javascript
运行
复制
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)

  })

}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48831411

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档