c语言音频.wav读写示例

科技   2024-09-09 12:00   江苏  


1 .wav格式说明

一. RIFF 概念

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

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

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

二. WAV 文件格式

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

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;

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

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;
}

说明
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


本文内容仅代表作者观点,不代表平台观点。

如有任何异议,欢迎联系我们。

如有侵权,请联系删除。


往期精彩回顾





2021年的第一场雪!英特尔2020年Q4财报解读



利用硬件辅助验证工具加速功能仿真


博文:裸片尺寸和光罩难题——光刻扫描仪吞吐量的成本模型


博文速递:Race condition in digital circuits


IP与SoC设计
《IP 与SoC》依托无锡国家“芯火”平台,全面报道全球IP与SoC设计技术的发展和国内外应用经验,为中国IC设计行业搭建一个IP与SoC资讯交流、产业促进的平台,为中国IC设计行业提供IP与SoC专业知识及相关信息支持和服务。
 最新文章