0

我正在尝试使用 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()
                      }
                  }
              }
          }
      }
  }
4

1 回答 1

0

投机,不正确的答案

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

实际答案

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

假设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()
            }
        }
    }
}
于 2022-01-15T11:52:05.163 回答