You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Swift开发:如何将Data中的PCM音频数据导入AVAudioPCMBuffer?

播放原始PCM音频数据的两种Swift实现方案

你已经把PCM文件加载到Data变量里了,下面是你提到的两种方案的具体实现——注意所有PCM相关参数(采样率、位深、声道数)要和你的原始PCM文件一致,不然会播放异常。

方案一:添加WAV头后用AVAudioPlayer播放

WAV格式本质是「PCM数据+标准文件头」,我们可以手动生成符合规范的WAV头,和原始PCM数据拼接后写入临时文件,再用AVAudioPlayer播放。

实现代码

import AVFoundation

// 生成带WAV头的Data
func makeWAVData(from rawPCM: Data, sampleRate: Double, bitDepth: Int, channels: Int) -> Data {
    let sampleCount = rawPCM.count / (bitDepth / 8)
    let byteRate = Int(sampleRate) * channels * (bitDepth / 8)
    
    var wavData = Data()
    
    // WAV文件头
    wavData.append(contentsOf: "RIFF".utf8)
    wavData.append(intToByteArray(value: 36 + rawPCM.count, length: 4)) // 文件总大小
    wavData.append(contentsOf: "WAVE".utf8)
    
    // fmt子块(PCM格式固定参数)
    wavData.append(contentsOf: "fmt ".utf8)
    wavData.append(intToByteArray(value: 16, length: 4)) // 子块大小
    wavData.append(intToByteArray(value: 1, length: 2)) // 音频格式(PCM=1)
    wavData.append(intToByteArray(value: channels, length: 2)) // 声道数
    wavData.append(intToByteArray(value: Int(sampleRate), length: 4)) // 采样率
    wavData.append(intToByteArray(value: byteRate, length: 4)) // 字节率
    wavData.append(intToByteArray(value: channels * (bitDepth / 8), length: 2)) // 块对齐
    wavData.append(intToByteArray(value: bitDepth, length: 2)) // 位深
    
    // data子块
    wavData.append(contentsOf: "data".utf8)
    wavData.append(intToByteArray(value: rawPCM.count, length: 4)) // PCM数据大小
    wavData.append(rawPCM)
    
    return wavData
}

// 辅助函数:Int转指定长度的小端字节数组
func intToByteArray(value: Int, length: Int) -> [UInt8] {
    var bytes = [UInt8](repeating: 0, count: length)
    for i in 0..<length {
        bytes[i] = UInt8((value >> (i * 8)) & 0xFF)
    }
    return bytes
}

// 播放逻辑
func playPCMAsWAV(audioData: Data) {
    // 替换成你的PCM实际参数
    let sampleRate = 44100.0
    let bitDepth = 16
    let channels = 1
    
    let wavData = makeWAVData(from: audioData, sampleRate: sampleRate, bitDepth: bitDepth, channels: channels)
    
    // 写入临时文件
    let tempDir = NSTemporaryDirectory()
    let tempFileURL = URL(fileURLWithPath: tempDir).appendingPathComponent("temp_audio.wav")
    
    do {
        try wavData.write(to: tempFileURL)
        let player = try AVAudioPlayer(contentsOf: tempFileURL)
        player.play()
    } catch {
        print("播放失败:\(error.localizedDescription)")
    }
}

调用示例:

playPCMAsWAV(audioData: audioData)

方案二:用AVAudioEngine + AVAudioPCMBuffer播放

这种方案不需要写入文件,直接在内存中处理,效率更高,适合实时播放场景。

实现代码

import AVFoundation

func playPCMWithAudioEngine(audioData: Data) {
    // 替换成你的PCM实际参数
    let sampleRate = 44100.0
    let bitDepth = 16
    let channels = 1
    
    // 创建对应格式的AVAudioFormat
    guard let format = AVAudioFormat(
        commonFormat: bitDepth == 16 ? .pcmFormatInt16 : .pcmFormatFloat32,
        sampleRate: sampleRate,
        channels: AVAudioChannelCount(channels),
        interleaved: true
    ) else {
        print("创建音频格式失败")
        return
    }
    
    // 计算帧数并创建AVAudioPCMBuffer
    let frameCount = UInt32(audioData.count / Int(format.streamDescription.pointee.mBytesPerFrame))
    guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCount) else {
        print("创建PCMBuffer失败")
        return
    }
    buffer.frameLength = frameCount
    
    // 将原始Data复制到Buffer中
    let bytes = audioData.withUnsafeBytes { $0.bindMemory(to: Int16.self).baseAddress }
    memcpy(buffer.int16ChannelData?[0], bytes, Int(frameCount) * Int(format.streamDescription.pointee.mBytesPerFrame))
    
    // 初始化音频引擎和播放节点
    let engine = AVAudioEngine()
    let playerNode = AVAudioPlayerNode()
    engine.attach(playerNode)
    engine.connect(playerNode, to: engine.mainMixerNode, format: format)
    
    do {
        try engine.start()
        playerNode.scheduleBuffer(buffer) {
            // 播放完成后的回调(可选)
            playerNode.stop()
            engine.stop()
        }
        playerNode.play()
    } catch {
        print("播放失败:\(error.localizedDescription)")
    }
}

调用示例:

playPCMWithAudioEngine(audioData: audioData)

注意点

  • 如果你的PCM是32位浮点格式,需要把commonFormat改成.pcmFormatFloat32,同时复制数据时用buffer.floatChannelData?[0]替代int16ChannelData。
  • AVAudioEngine和playerNode如果定义为函数局部变量,可能会被提前释放导致播放中断,建议将它们设为类的属性。

内容的提问来源于stack exchange,提问作者mrzaza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 12:58:01