WAV 音频文件格式解析

由于最近工作中使用到了多路音频采样数据的处理,故补充一波wav格式解析的知识,本文大部分资料来源于网络,我只做了收集归纳的工作。下方附有原文连接,若有侵权,请在下方留言告知。

概述

Waveform Audio File Format(WAVE,又或者是因为WAV后缀而被大众所知的),它采用RIFF(Resource Interchange File Format)文件格式结构。通常用来保存PCM格式的原始音频数据,所以通常被称为无损音频。但是严格意义上来讲,WAV也可以存储其它压缩格式的音频数据。

格式解析

WAV文件遵循RIFF规则,其内容以区块(chunk)为最小单位进行存储。WAV文件一般由3个区块组成:RIFF chunk、Format chunk和Data chunk。另外,文件中还可能包含一些可选的区块,如:Fact chunk、Cue points chunk、Playlist chunk、Associated data list chunk等。
本文将只介绍RIFF chunk、Format chunk和Data chunk。

RIFF区块

名称偏移地址字节数端序内容
ID0x004Byte大端‘RIFF’ (0x52494646)
Size0x044Byte小端fileSize - 8
Type0x084Byte大端‘WAVE’(0x57415645)
ID 以'RIFF'为标识
Size 是整个文件的长度减去 ID 和 Size 的长度
Type 是WAVE表示后面需要两个子块:Format 区块和 Data 区块

FORMAT区块

名称偏移地址字节数端序内容
ID0x004Byte大端‘fmt ‘ (0x666D7420)
Size0x044Byte小端16
AudioFormat0x082Byte小端音频格式
NumChannels0x0A2Byte小端声道数
SampleRate0x0C4Byte小端采样率
ByteRate0x104Byte小端每秒数据字节数
BlockAlign0x142Byte小端数据块对齐
BitsPerSample0x162Byte小端采样位数
ID 以'fmt '为标识
Size 表示该区块数据的长度(不包含 ID 和 Size 的长度)
AudioFormat 表示 Data 区块存储的音频数据的格式,PCM音频数据的值为1
NumChannels 表示音频数据的声道数,1:单声道,2:双声道
SampleRate 表示音频数据的采样率
ByteRate 每秒数据字节数 = SampleRate * NumChannels * BitsPerSample / 8
BlockAlign 每个采样所需的字节数 = NumChannels * BitsPerSample / 8
BitsPerSample 每个采样存储的bit数,8:8bit,16:16bit,32:32bit

DATA区块

名称偏移地址字节数端序内容
ID0x004Byte大端‘data’ (0x64617461)
Size0x044Byte小端N
Data0x08NByte小端音频数据
ID 以'data'为标识
Size 表示音频数据的长度,N = ByteRate * seconds
Data 音频数据

小端存储

所谓的大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
所谓的小端模式,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。

下面解释一下 PCM 数据在 WAV 文件中的 bit 位排列方式

PCM数据类型采样采样
8Bit 单声道声道0声道0
8Bit 双声道声道0声道1
16Bit 单声道声道0低位,声道0高位声道0低位,声道0高位
16Bit 双声道声道0低位,声道0高位声道1低位,声道1高位

示例代码 (C)

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

struct WAV_Format {
    // riff header
    uint32_t ChunkID;        // "RIFF"
    uint32_t ChunkSize;      // 36 + Subchunk2Size
    uint32_t Format;         // "WAVE"

    // sub-chunk "fmt"
    uint32_t Subchunk1ID;    // "fmt " */
    uint32_t Subchunk1Size;  // 16 for PCM */
    uint16_t AudioFormat;    // PCM = 1*/
    uint16_t NumChannels;    // Mono = 1, Stereo = 2, etc.
    uint32_t SampleRate;     // 8000, 44100, etc.
    uint32_t ByteRate;       // = SampleRate * NumChannels * BitsPerSample/8
    uint16_t BlockAlign;     // = NumChannels * BitsPerSample/8
    uint16_t BitsPerSample;  // 8bits, 16bits, etc.

    // sub-chunk "data"
    uint32_t Subchunk2ID;    // "data"
    uint32_t Subchunk2Size;  // data size
};

int main(void) {
    FILE *fp = NULL;
    struct WAV_Format wav;

    fp = fopen("output_1.wav", "rb");
    if (!fp) {
        printf("can't open audio file\n");
        exit(1);
    }

    fread(&wav, 1, sizeof(struct WAV_Format), fp);

    printf("ChunkID \t%x\n", wav.ChunkID);
    printf("ChunkSize \t%d\n", wav.ChunkSize);
    printf("Format \t\t%x\n", wav.Format);
    printf("Subchunk1ID \t%x\n", wav.Subchunk1ID);
    printf("Subchunk1Size \t%d\n", wav.Subchunk1Size);
    printf("AudioFormat \t%d\n", wav.AudioFormat);
    printf("NumChannels \t%d\n", wav.NumChannels);
    printf("SampleRate \t%d\n", wav.SampleRate);
    printf("ByteRate \t%d\n", wav.ByteRate);
    printf("BlockAlign \t%d\n", wav.BlockAlign);
    printf("BitsPerSample \t%d\n", wav.BitsPerSample);
    printf("Subchunk2ID \t%x\n", wav.Subchunk2ID);
    printf("Subchunk2Size \t%d\n", wav.Subchunk2Size);

    fclose(fp);
    return 0;
}

Reference

[1] WAV文件格式详解 - 简书


本文作者: Vincent0700
本文地址: https://vincentstudio.info/2019/05/15/029_WAV_Format_Parsing/
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!