【问题标题】:How does one use AVAudioConverter to convert from mono to stereo?如何使用 AVAudioConverter 将单声道转换为立体声?
【发布时间】:2022-01-17 14:02:48
【问题描述】:

我正在尝试使用 AVAudioEngine 而不是 AVAudioPlayer,因为我需要在播放音频时对每个数据包进行一些处理,但在我能做到这一点之前,我需要将 16 位 8khz 单声道音频数据转换为立体声,因此 AVAudioEngine 将播放它。这是我的(不完整的)尝试。我目前坚持如何让 AVAudioConverter 进行单声道到立体声的转换。如果我不使用 AVAudioConverter,iOS 运行时会抱怨输入格式与输出格式不匹配。如果我确实使用它(如下所示),运行时不会抱怨,但音频无法正确播放(可能是因为我没有正确进行单声道到立体声的转换)。感谢您提供任何帮助!

  private func loadAudioData(audioData: Data?) {
      // Load audio data into player

      guard let audio = audioData else {return}
      do {
          let inputAudioFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: Double(sampleRate), channels: 1, interleaved: false)
          let outputAudioFormat = self.audioEngine.mainMixerNode.outputFormat(forBus: 0)
          
          if inputAudioFormat != nil {
              let inputStreamDescription = inputAudioFormat?.streamDescription.pointee
              let outputStreamDescription = outputAudioFormat.streamDescription.pointee
              let count = UInt32(audio.count)
              if inputStreamDescription != nil && count > 0 {
                  if let ibpf = inputStreamDescription?.mBytesPerFrame {
                      let inputFrameCapacity = count / ibpf
                      let outputFrameCapacity = count / outputStreamDescription.mBytesPerFrame
                      self.pcmInputBuffer = AVAudioPCMBuffer(pcmFormat: inputAudioFormat!, frameCapacity: inputFrameCapacity)
                      self.pcmOutputBuffer = AVAudioPCMBuffer(pcmFormat: outputAudioFormat, frameCapacity: outputFrameCapacity)
          
                      if let input = self.pcmInputBuffer, let output = self.pcmOutputBuffer {
                          self.pcmConverter = AVAudioConverter(from: inputAudioFormat!, to: outputAudioFormat)
                          input.frameLength = input.frameCapacity
                      
                          let b = UnsafeMutableBufferPointer(start: input.int16ChannelData?[0], count: input.stride * Int(inputFrameCapacity))
                          let bytesCopied = audio.copyBytes(to: b)
                          assert(bytesCopied == count)
          
                          audioEngine.attach(playerNode)
                          audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: nil)
          
                          self.pcmConverter?.convert(to: output, error: nil) { packets, status in
                              status.pointee = .haveData
                              return self.pcmInputBuffer    // I know this is wrong, but i'm not sure how to do it correctly
                          }
                          try audioEngine.start()
                      }
                  }
              }
          }
      }
  }

【问题讨论】:

    标签: ios avaudioengine avaudioconverter


    【解决方案1】:

    推测性的错误答案

    pcmConverter?.channelMap = [0, 0]怎么样?

    实际答案

    您不需要使用音频转换器通道映射,因为默认情况下单声道到立体声AVAudioConverters 似乎复制了单声道。主要问题是outputFrameCapacity错了,你在调用audioEngine.prepare()或者启动引擎之前使用mainMixersoutputFormat

    假设sampleRate = 8000,修改后的解决方案如下所示:

    private func loadAudioData(audioData: Data?) throws  {
        // Load audio data into player
        
        guard let audio = audioData else {return}
        do {
            audioEngine.attach(playerNode)
            audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: nil)
            audioEngine.prepare() // https://stackoverflow.com/a/70392017/22147
            
            let outputAudioFormat = self.audioEngine.mainMixerNode.outputFormat(forBus: 0)
            guard let inputAudioFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: Double(sampleRate), channels: 1, interleaved: false) else { return }
            
            let inputStreamDescription = inputAudioFormat.streamDescription.pointee
            let outputStreamDescription = outputAudioFormat.streamDescription.pointee
            let count = UInt32(audio.count)
            if count > 0 {
                let ibpf = inputStreamDescription.mBytesPerFrame
                let inputFrameCapacity = count / ibpf
                let outputFrameCapacity = Float64(inputFrameCapacity) * outputStreamDescription.mSampleRate / inputStreamDescription.mSampleRate
                self.pcmInputBuffer = AVAudioPCMBuffer(pcmFormat: inputAudioFormat, frameCapacity: inputFrameCapacity)
                self.pcmOutputBuffer = AVAudioPCMBuffer(pcmFormat: outputAudioFormat, frameCapacity: AVAudioFrameCount(outputFrameCapacity))
                
                if let input = self.pcmInputBuffer, let output = self.pcmOutputBuffer {
                    self.pcmConverter = AVAudioConverter(from: inputAudioFormat, to: outputAudioFormat)
                    input.frameLength = input.frameCapacity
                    
                    let b = UnsafeMutableBufferPointer(start: input.int16ChannelData?[0], count: input.stride * Int(inputFrameCapacity))
                    let bytesCopied = audio.copyBytes(to: b)
                    assert(bytesCopied == count)
                    
                    self.pcmConverter?.convert(to: output, error: nil) { packets, status in
                        status.pointee = .haveData
                        return self.pcmInputBuffer    // I know this is wrong, but i'm not sure how to do it correctly
                    }
                    try audioEngine.start()
                    
                    self.playerNode.scheduleBuffer(output, completionHandler: nil)
                    self.playerNode.play()
                }
            }
        }
    }
    

    【讨论】:

    • 如果这就是答案,为什么是问题的形式?
    • 因为它是推测性的。
    • 那么它应该是评论,而不是答案。
    • 推测,因为它可能是上述问题的答案,但我觉得代码仍然会有问题。
    • @matt:没有规定应该将推测性答案作为评论发布,只要它仍然提出可能的解决方案。事实上,从那时起社区可以通过支持/反对投票对建议进行权衡,甚至可以说更好。
    猜你喜欢
    • 1970-01-01
    • 2017-01-22
    • 1970-01-01
    • 2019-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多