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
相关产品推荐
相关产品推荐

