Swift 中 PCM 转换为 WAV 格式

在处理音频数据时,PCM(脉冲编码调制)是一个常见的格式。然而,PCM 文件不够灵活,不能包含音乐元数据,而 WAV(波形音频文件格式)则可以对录音进行更好的存储和管理。本文将介绍如何在 Swift 中将 PCM 数据转换为 WAV 格式,并附带相关代码示例。

PCM 和 WAV 格式简介

PCM 格式

PCM 是一种无压缩的音频格式,主要用作音频的原始数据。它以线性方式存储音频信号的样本,提供了高保真的音频质量。PCM 数据本身不包含文件头信息,因此在数据传输时,我们需要知道数据的格式和采样率等参数。

WAV 格式

WAV 是一种包含 PCM 数据的文件格式,带有文件头,可以提供关于文件的元数据(如采样率、通道等)的信息。这使得 WAV 格式在实际应用中更为灵活和通用。

PCM 转 WAV 的基本步骤

转换 PCM 数据到 WAV 格式主要包括以下几个步骤:

  1. 读取 PCM 数据。
  2. 创建 WAV 文件头。
  3. 将 PCM 数据与 WAV 文件头结合,写入新文件。

关系图

为了更清晰地展示这些步骤,可以使用关系图表示各个组件之间的关系:

erDiagram
    PCMData {
        string data
        int sampleRate
        int channels
    }
    WAVHeader {
        int chunkID
        int chunkSize
        int format
        int subChunk1ID
        int subChunk1Size
        int audioFormat
        int numberOfChannels
        int sampleRate
        int byteRate
        int blockAlign
        int bitsPerSample
        int subChunk2ID
        int subChunk2Size
    }
    WAVFile {
        WAVHeader
        PCMData
    }

Swift 代码示例

下面是一个简单的 Swift 示例,演示如何将 PCM 数据转换为 WAV 格式。

import Foundation

func writeWAVFile(pcmData: Data, sampleRate: Int, channels: Int, outputURL: URL) {
    // 1. 定义 WAV 文件头
    var wavHeader = WAVHeader(
        chunkID: "RIFF".data(using: .utf8)!.withUnsafeBytes { $0.load(as: UInt32.self) },
        chunkSize: UInt32(pcmData.count + 36),
        format: "WAVE".data(using: .utf8)!.withUnsafeBytes { $0.load(as: UInt32.self) },
        subChunk1ID: "fmt ".data(using: .utf8)!.withUnsafeBytes { $0.load(as: UInt32.self) },
        subChunk1Size: 16,
        audioFormat: 1,
        numberOfChannels: Int32(channels),
        sampleRate: Int32(sampleRate),
        byteRate: Int32(sampleRate * channels * 2), // 16-bit audio
        blockAlign: Int32(channels * 2),
        bitsPerSample: 16,
        subChunk2ID: "data".data(using: .utf8)!.withUnsafeBytes { $0.load(as: UInt32.self) },
        subChunk2Size: UInt32(pcmData.count)
    )
    
    // 2. 创建 WAV 文件
    var wavData = Data()
    wavData.append(UnsafeRawBufferPointer(start: &wavHeader, count: MemoryLayout<WAVHeader>.size))
    wavData.append(pcmData)
    
    // 3. 写入文件
    do {
        try wavData.write(to: outputURL)
        print("WAV file written successfully to \(outputURL.path)")
    } catch {
        print("Error writing WAV file: \(error)")
    }
}

// WAV 文件头结构体
struct WAVHeader {
    var chunkID: UInt32
    var chunkSize: UInt32
    var format: UInt32
    var subChunk1ID: UInt32
    var subChunk1Size: UInt32
    var audioFormat: UInt16
    var numberOfChannels: UInt16
    var sampleRate: UInt32
    var byteRate: UInt32
    var blockAlign: UInt16
    var bitsPerSample: UInt16
    var subChunk2ID: UInt32
    var subChunk2Size: UInt32
}

代码解析

  1. WAVHeader 结构体:定义了 WAV 文件头的各个参数,包括格式、采样率、通道数等。
  2. writeWAVFile 函数:接收 PCM 数据及相关参数,并生成 WAV 文件。它首先创建 WAV 文件头,并将 PCM 数据与头信息结合,最终将结果写入指定的输出 URL。

流程图

以下是整体流程的示意图:

flowchart TD
    A[读取 PCM 数据] --> B[创建 WAV 文件头]
    B --> C[将 PCM 数据与 WAV 文件头结合]
    C --> D[写入 WAV 文件]
    D --> E[完成转换]

结论

通过上述步骤,我们可以方便地将 PCM 音频数据转换为 WAV 格式。在许多音频处理应用中,这种转换非常常见。从 PCM 到 WAV 的转换,不仅提高了数据的可读性,还为音频的编辑与播放提供了更好的框架。希望本文能帮助您理解 PCM 与 WAV 的差异以及如何通过 Swift 实现这项转换。通过不断探索音频处理技术,您会发现更多有趣的应用和技巧。