使用AVAudioEngine播放WAV数据 [英] Playing WAV data with AVAudioEngine

查看:149
本文介绍了使用AVAudioEngine播放WAV数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当前,我在音频线程上收到EXC_BAD_ACCESS错误,并且正在尝试推断出问题所在.

Currently, I'm getting an EXC_BAD_ACCESS error on the audio thread, and I'm trying to deduce what is going wrong.

.wav文件数据从Data转换为AVAudioPCMBuffer时,我需要首先剥离RIFF标头吗?

When converting .wav file data from Data to an AVAudioPCMBuffer, do I need to strip the RIFF header first?

import AVFoundation

public class Player : NSObject {
  let engine = AVAudioEngine()

  public override init() {
    super.init()
    do {
      let _ = engine.mainMixerNode
      try engine.start()
    } catch {
      print("Player error: \(error)")
    }
  }

  @objc public func play(_ data: Data) {
    let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 48000, channels: 2, interleaved: true)!
    let buffer = data.toPCMBuffer(format: format)!

    let player = AVAudioPlayerNode()
    engine.attach(player)
    engine.connect(player, to: engine.mainMixerNode, format: nil)

    player.scheduleBuffer(buffer, at: nil, completionCallbackType: .dataPlayedBack) {
      callbackType in
      // Nothing in here.
    }
    player.play()
  }
}

这是toPCMBuffer扩展名:

// Taken from: https://stackoverflow.com/a/52731480/2228559
extension Data {
  func toPCMBuffer(format: AVAudioFormat) -> AVAudioPCMBuffer? {
    let streamDesc = format.streamDescription.pointee
    let frameCapacity = UInt32(count) / streamDesc.mBytesPerFrame
    guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCapacity) else { return nil }

    buffer.frameLength = buffer.frameCapacity
    let audioBuffer = buffer.audioBufferList.pointee.mBuffers

    withUnsafeBytes { addr in
      audioBuffer.mData?.copyMemory(from: addr, byteCount: Int(audioBuffer.mDataByteSize))
    }

    return buffer
  }
}

注意:我不能使用AVAudioFile,因为.wav文件数据是通过有线方式加载的.

Note: I cannot use AVAudioFile because the .wav file data is loaded over-the-wire.

推荐答案

IDK,但如果我播放交错的AVAudioPCMBuffer,mac就会崩溃,如果它们不浮动数据,则音频会乱码,因此您可以转换为非交错的浮动数据:

IDK, but my mac crashes if I play interleaved AVAudioPCMBuffers, and garbled audio if they're not float data, so you could convert to non-interleaved float data:

@objc public func play(_ data: Data) {
    let sampleRate: Double = 48000

    let interleavedFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: sampleRate, channels: 2, interleaved: true)!
    let interleavedBuffer = data.toPCMBuffer(format: interleavedFormat)!

    let nonInterleavedFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: sampleRate, channels: 2, interleaved: false)!
    let nonInterleavedBuffer = AVAudioPCMBuffer(pcmFormat: nonInterleavedFormat, frameCapacity: interleavedBuffer.frameCapacity)!
    nonInterleavedBuffer.frameLength = interleavedBuffer.frameLength

    let converter = AVAudioConverter(from: interleavedFormat, to: nonInterleavedFormat)!
    try! converter.convert(to: nonInterleavedBuffer, from: interleavedBuffer)

    let player = AVAudioPlayerNode()

    engine.attach(player)
    engine.connect(player, to: engine.mainMixerNode, format: nil)

    player.scheduleBuffer(nonInterleavedBuffer, at: nil, completionCallbackType: .dataPlayedBack) {
        callbackType in
        // Nothing in here.
    }

    player.play()
}

extension Data {
    func toPCMBuffer(format: AVAudioFormat) -> AVAudioPCMBuffer? {
        assert(format.isInterleaved)

        let streamDesc = format.streamDescription.pointee
        let frameCapacity = UInt32(count) / streamDesc.mBytesPerFrame
        guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCapacity) else { return nil }

        buffer.frameLength = buffer.frameCapacity

        let b = UnsafeMutableBufferPointer(start: buffer.int16ChannelData![0], count: buffer.stride * Int(frameCapacity))
        let bytesCopied = self.copyBytes(to: b)
        assert(bytesCopied == count)

        return buffer
    }
}

这篇关于使用AVAudioEngine播放WAV数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆