Как использовать AVAudioConverter для преобразования моно в стерео?

Я пытаюсь использовать AVAudioEngine вместо AVAudioPlayer, потому что мне нужно выполнить некоторую обработку для каждого пакета во время воспроизведения звука, но прежде чем я смогу зайти так далеко, мне нужно преобразовать 16-битные монофонические аудиоданные 8 кГц в стерео, чтобы 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()
                      }
                  }
              }
          }
      }
  }

1 ответ

Умозрительный, неверный ответ

Как насчет pcmConverter?.channelMap = [0, 0]?

Фактический ответ

Вам не нужно использовать карту каналов аудио конвертера, потому что моно в стерео AVAudioConverterкажется, что по умолчанию дублируется моноканал. Основные проблемы заключались в том, что outputFrameCapacityбыл неправ, и вы используете mainMixerс outputFormatпрежде чем звонить audioEngine.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()
            }
        }
    }
}
Другие вопросы по тегам