1 .wav格式说明

一. RIFF 概念

在 Windows 环境下,大部分的多媒体文件都依循着一种结构来存放信息,这种结构称为"资源互换文件格式"(Resources lnterchange File Format),简称 RIFF。例如声音的 WAV 文件、视频的 AV1 文件等等均是由此结构衍生出来的。RIFF 可以看做是一种树状结构,其基本构成单位为 chunk,犹如树状结构中的节点,每个 chunk 由"辨别码"、“数据大小"及"数据"所组

成。

c语言音频.wav读写示例_数据


辨别码由 4 个 ASCII 码所构成,数据大小则标示出紧跟其后数据的长度(单位为 Byte),而数据大小本身也用掉 4 个 Byte,所以事实上一个 chunk 的长度为数据大小加 8。一般而言,chunk 本身并不允许内部再包含 chunk,但有两种例外,分别为以"RIFF"及"L1ST"为辨别码的chunk。而针对此两种 chunk,RIFF 又从原先的"数据"中切出 4 个 Byte。 此 4 个 Byte 称为"格式辨别码”,然而 RIFF 又规定文件中仅能有一个以"RIFF"为辨别码的 chunk。

c语言音频.wav读写示例_音视频_02


只要依循此一结构的文件,我们均称之为 RIFF 档。此种结构提供了一种系统化的分类。如果和 MS 一 DOS 文件系统作比较,"RIFF"chunk 就好比是一台硬盘的根目录,其格式辨别码便是此硬盘的逻辑代码(C:或 D:),而"L1ST"chunk 即为其下的子目录,其他的 chunk 则为一般的文件。至于在 RIFF 文件的处理方面,微软提供了相关的函数。视窗下的各种多媒体文件格式就如同在磁盘机下规定仅能放怎样的目录,而在该目录下仅能放何种数据。

二. WAV 文件格式

WAVE 文件是非常简单的一种 RIFF 文件,它的格式类型为"WAVE"。RIFF 块包含两个子块,这两个子块的 ID 分别是"fmt"和"data",其中"fmt"子块由结构 PCMWAVEFORMAT 所组成,其子块的大小就是 sizeofof(PCMWAVEFORMAT),数据组成就是 PCMWAVEFORMAT 结构中的数据。

c语言音频.wav读写示例_数据_03


PCMWAVEFORMAT 结构定义如下:

typedef struct
{ 
 WAVEFORMAT wf; /波形格式; 
 WORD wBitsPerSample; //WAVE 文件的采样大小; 
} PCMWAVEFORMAT; 
//WAVEFORMAT 结构定义如下:
typedef struct
{ 
 WORD wFormatag; //编码格式,包括 WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM 等 
 WORD nChannls; //声道数,单声道为 1,双声道为 2; 
 DWORD nSamplesPerSec; //采样频率; 
 DWORD nAvgBytesperSec; //每秒的数据量; 
 WORD nBlockAlign; //块对齐; 
} WAVEFORMAT;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

"data"子块包含 WAVE 文件的数字化波形声音数据,其存放格式依赖于"fmt"子块中wFormatTag 成员指定的格式种类,在多声道 WAVE 文件中,样本是交替出现的。如 16bit 的单声道 WAVE 文件和双声道 WAVE 文件的数据采样格式分别如图四所示:

c语言音频.wav读写示例_数据_04


c语言音频.wav读写示例_#include_05

2 c语言读写.wav文件示例

下面是一个简单的C语言示例,用于读取和写入WAV音频文件。这段代码展示了如何打开WAV文件、读取其头部信息、读取音频数据以及写回一个新的WAV文件。

读取和写入WAV文件示例

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

#pragma pack(1)
typedef struct {
    char riff[4];        // "RIFF"
    unsigned int size;   // Size of the file
    char wave[4];        // "WAVE"
    char fmt[4];         // "fmt "
    unsigned int fmt_size;// Size of format
    unsigned short audio_format; // Audio format
    unsigned short num_channels;  // Number of channels
    unsigned int sample_rate;     // Sample rate
    unsigned int byte_rate;        // Byte rate
    unsigned short block_align;    // Block align
    unsigned short bits_per_sample; // Bits per sample
    char data[4];         // "data"
    unsigned int data_size; // Size of data
} WAVHeader;

void read_wav(const char *filename) {
    FILE *file = fopen(filename, "rb");
    if (!file) {
        perror("Unable to open file");
        return;
    }

    WAVHeader header;
    fread(&header, sizeof(WAVHeader), 1, file);

    printf("RIFF: %.4s\n", header.riff);
    printf("WAVE: %.4s\n", header.wave);
    printf("Format: %.4s\n", header.fmt);
    printf("Channels: %hu\n", header.num_channels);
    printf("Sample Rate: %u\n", header.sample_rate);
    printf("Bits per Sample: %hu\n", header.bits_per_sample);
    printf("Data Size: %u\n", header.data_size);

    // Read audio data
    short *data = malloc(header.data_size);
    fread(data, header.data_size, 1, file);
    fclose(file);

    // Example: Print first 10 samples
    for (int i = 0; i < 10 && i < header.data_size / 2; i++) {
        printf("Sample %d: %d\n", i, data[i]);
    }

    free(data);
}

void write_wav(const char *filename, short *data, unsigned int data_size, int sample_rate, int num_channels) {
    FILE *file = fopen(filename, "wb");
    if (!file) {
        perror("Unable to open file");
        return;
    }

    WAVHeader header;
    strncpy(header.riff, "RIFF", 4);
    header.size = 36 + data_size;
    strncpy(header.wave, "WAVE", 4);
    strncpy(header.fmt, "fmt ", 4);
    header.fmt_size = 16;
    header.audio_format = 1; // PCM
    header.num_channels = num_channels;
    header.sample_rate = sample_rate;
    header.byte_rate = sample_rate * num_channels * (16 / 8);
    header.block_align = num_channels * (16 / 8);
    header.bits_per_sample = 16;
    strncpy(header.data, "data", 4);
    header.data_size = data_size;

    fwrite(&header, sizeof(WAVHeader), 1, file);
    fwrite(data, data_size, 1, file);
    fclose(file);
}

int main() {
    const char *input_filename = "input.wav";
    const char *output_filename = "output.wav";

    read_wav(input_filename);

    // Here you would modify `data` as needed
    short sample_data[10] = {0}; // Example data
    write_wav(output_filename, (short *)sample_data, sizeof(sample_data), 44100, 1);

    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.

说明
WAV 文件头结构 (WAVHeader):定义了WAV文件的结构,包括文件格式、采样率、声道等信息。
读取 WAV 文件:read_wav 函数打开一个WAV文件,读取其头部信息和音频数据,并打印一些基本信息和前10个音频样本。
写入 WAV 文件:write_wav 函数根据提供的音频数据生成一个新的WAV文件。
主函数:在main函数中,读取input.wav文件并输出到output.wav文件。

使用注意
确保使用的WAV文件符合标准(如PCM编码)。
在实际应用中,可能需要对音频数据进行处理或操作。
编译命令
使用GCC编译:

gcc -o wav_example wav_example.c
  • 1.

c语言音频.wav读写示例_#include_06