😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍wav音频格式🍭
😎金句分享😎:🍭子曰:父母在,不远游,游必有方。 ——《论语·里仁篇》。意思是,父母还健在时,就不要远离他们,如果一定要出远门,也必须告知自己所去的地方。🍭
文章未经允许,不许转载 !!!
目录
- 🎄一、概述
- 🎄二、RIFF 规范
- ✨2.1 RIFF 介绍
- ✨2.2 RIFF 文件结构
- 🎈2.2.1 RIFF 块
- 🎈2.2.2 LIST 块
- ✨2.3 FourCC
- 🎄三、wav 文件详解
- ✨3.1 wav 文件结构
- ✨3.2 wav 文件的编码格式
- 🎄四、PCM 转 WAV 的C语言程序
- 🎄五、总结
🎄一、概述
WAV全称是
Waveform Audio File Format
,是一种常用的无损音频文件格式,它最初由微软和IBM于1991年共同开发,并成为Windows操作系统中音频文件的标准格式之一。从文件结构来讲,WAV文件格式是微软存储多媒体文件的RIFF
规范的子集。本文将详细介绍WAV格式文件的文件结构。
WAV格式文件相对于其他音频文件格式具有以下特点:
- 无损压缩:WAV文件采用无损压缩算法,不会丢失原始音频数据,能够保留音频的高质量。
- 高音质:由于无损压缩技术的使用,WAV文件通常具有较高的音质和更好的还原性能。
- 大文件大小:由于不进行任何压缩,WAV文件相对于其他压缩格式(如MP3)的文件大小较大,占用存储空间较多。
- 支持多种采样率和位深度:WAV文件支持多种采样率和位深度,可以根据需求选择合适的参数进行录制或处理。
- 广泛兼容性:WAV格式是一种通用的音频文件格式,几乎所有的音频软件和硬件设备都能够支持读取和播放WAV文件。
原文链接:https://blog.csdn.net/wkd_007/article/details/134125746
🎄二、RIFF 规范
WAV 文件采用RIFF
规范来存储音频数据和相关元信息。这小节我们先了解RIFF
规范。
✨2.1 RIFF 介绍
RIFF(Resource Interchange File Format)是一种通用的文件格式规范,最初由微软开发,用于在不同应用程序之间交换数据。它以分块的方式组织数据,每个块包含一个标识符和相应的数据内容。
能以RIFF格式存储的数据有:
.AVI
:音频视频交错格式数据.WAV
:波形格式数据.RDI
:位图数据格式.RMI
:MIDI格式数据.PAL
:调色板格式.RMN
:多媒体电影.ANI
:动画光标.BND
:其他的RIFF文件
✨2.2 RIFF 文件结构
RIFF文件都是由一个或多个块(chunk)组成的,且第一个块必须是RIFF
块。
常见的块有:RIFF
、LIST
、fmt
、data
,每个块都包含了Id(块标志)、Size(块大小)、data(块数据)。其中,RIFF
块、LIST
块可以包含其他子块。
🎈2.2.1 RIFF 块
RIFF规范的文件的第一个块必须是RIFF块,RIFF块前面12个字节是RIFF块描述,包含了块标志、块大小、块类型。接下去的数据就是子块(Subchunk),RIFF的块类型决定了有多少个子块,有哪些子块。
🎈2.2.2 LIST 块
LIST
块可能比较少见,这里大概了解一下,下图是包含LIST
块的RIFF文件,首先是RIFF文件必须的RIFF chunk
,其数据域又包含有两个subchunk
,其中一个subchunk
的类型为LIST
,该LIST chunk
又包含了两个subchunk
。
✨2.3 FourCC
FourCC 全称为Four-Character Codes
,是一个4字节32位的标识符,通常用来标识文件的数据格式。RIFF文件的块标志就是使用了 FourCC 。FourCC是4个ASCII字符,不足四个字符的则在最后补充空格(不是空字符)。比如,FourCC fmt,实际上是'f'、'm'、't'、' '
。
C语言中,可以用宏来生成FourCC:
#define MAKE_FOURCC(a,b,c,d) \
( ((uint32_t)d) | ( ((uint32_t)c) << 8 ) | ( ((uint32_t)b) << 16 ) | ( ((uint32_t)a) << 24 ) )
🎄三、wav 文件详解
✨3.1 wav 文件结构
WAV文件通常是一个
RIFF
文件,如果数据是没压缩的PCM,则整个文件可以看出44个字节的文件头
+音频数据
构成。如果是压缩的音频数据,接着看下面小节细说。
wav文件(PCM数据)分为三个部分,如下图:
RIFF
块描述(下图紫色部分);- 指定数据格式的子块——
fmt
块(下图绿色部分)、- 包含实际样本数据的子块——
data
块(下图砖红色部分)。
下面是各个块详细的解释,有些块在pcm数据中是用不到的:
RIFF
块描述:
- 1、ChunkID:包含ASCII格式的字母
RIFF
;
- 2、ChunkSize:这个数值
ChunkSize
后面所有数据的大小。可以是整个文件的大小减去8个字节
;也可以是36+SubChunk2Size
;还可以是4 + (8+SubChunk1Size) + (8+SubChunk2Size)
;
- 3、Format:包含字母
WAVE
fmt
块:
- Subchunk1ID:包含字母
fmt
,表示fmt
块;
- Subchunk1Size:这个数值是
Subchunk1Size
后所有fmt
块数据的大小,对于PCM数据来说,这个值固定为16;
- AudioFormat:如果音频数据是PCM,这个值为
1
。1
以外的值表示一些压缩形式;
- NumChannels:声道数,Mono = 1, Stereo = 2 等等;
- SampleRate:采样率,8000,44100,48000 等;
- ByteRate:每秒的字节数,
采样率 * 声道数 * 样本位数 / 8
;
- BlockAlign:每个声道取一个样本的字节数之和,
声道数 * 样本位数 / 8
;
- BitsPerSample:样本位数,每个样本占用的bit位个数。8bit、16bit 等等。
- ExtraParamSize:拓展参数大小,如果是PCM,则不存在;
- ExtraParams:拓展参数数据;
fact
块 (可选),如果是没压缩的PCM,则没有这个块
- id:FOURCC 值为
'f' 'a' 'c' 't'
,4个字节
- size:数据域的长度,4个字节(最小值为4)
- Data:采样总数 4字节
data
块:
- Subchunk2ID:包含字母
data
,表示data
块;
- Subchunk2Size:这个数值是
Subchunk2Size
后所有数据的字节数,也就是实际音频数据的总字节数。
- Data:实际的音频数据;
✨3.2 wav 文件的编码格式
大部分的wav文件的编码格式都是PCM的,但也存在其他编码格式,不同的编码格式,其文件结构会有区别,下表列出了常见编码格式和wav文件结构的区别:
格式编码 | 格式名称 | fmt块长度 | fact 块 |
---|---|---|---|
0x01 | PCM / 非压缩格式 | 16 | |
0x02 | Microsoft ADPCM | 18 | √ |
0x03 | IEEE float | 18 | √ |
0x06 | ITU G.711 a-law | 18 | √ |
0x07 | ITU G.711 μ-law | 18 | √ |
0x031 | GSM 6.10 | 20 | √ |
0x040 | ITU G.721 ADPCM | √ | |
0xFFFE | 见子格式块中的编码格式 | 40 |
🎄四、PCM 转 WAV 的C语言程序
// pcm2wac.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* Convert PCM16LE raw data to WAVE format
* @param pcmpath Input PCM file.
* @param channels Channel number of PCM file.
* @param sample_rate Sample rate of PCM file.
* @param wavepath Output WAVE file.
*/
int simplest_pcm16le_to_wave(const char *pcmpath,int channels,int sample_rate,const char *wavepath)
{
typedef struct WAVE_HEADER{
char fccID[4];
unsigned int dwSize;
char fccType[4];
}WAVE_HEADER;
typedef struct WAVE_FMT{
char fccID[4];
unsigned int dwSize;
unsigned short wFormatTag;
unsigned short wChannels;
unsigned int dwSamplesPerSec;
unsigned int dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short uiBitsPerSample;
}WAVE_FMT;
typedef struct WAVE_DATA{
char fccID[4];
unsigned int dwSize;
}WAVE_DATA;
if(channels==0||sample_rate==0){
channels = 2;
sample_rate = 44100;
}
int bits = 16;
WAVE_HEADER pcmHEADER;
WAVE_FMT pcmFMT;
WAVE_DATA pcmDATA;
unsigned short m_pcmData;
FILE *fp,*fpout;
fp=fopen(pcmpath, "rb");
if(fp == NULL) {
printf("open pcm file error\n");
return -1;
}
fpout=fopen(wavepath, "wb+");
if(fpout == NULL) {
printf("create wav file error\n");
return -1;
}
//WAVE_HEADER
memcpy(pcmHEADER.fccID,"RIFF",strlen("RIFF"));
memcpy(pcmHEADER.fccType,"WAVE",strlen("WAVE"));
fseek(fpout,sizeof(WAVE_HEADER),1);
//WAVE_FMT
pcmFMT.dwSamplesPerSec=sample_rate;
pcmFMT.dwAvgBytesPerSec=pcmFMT.dwSamplesPerSec*sizeof(m_pcmData);
pcmFMT.uiBitsPerSample=bits;
memcpy(pcmFMT.fccID,"fmt ",strlen("fmt "));
pcmFMT.dwSize=16;
pcmFMT.wBlockAlign=2;
pcmFMT.wChannels=channels;
pcmFMT.wFormatTag=1;
fwrite(&pcmFMT,sizeof(WAVE_FMT),1,fpout);
//WAVE_DATA;
memcpy(pcmDATA.fccID,"data",strlen("data"));
pcmDATA.dwSize=0;
fseek(fpout,sizeof(WAVE_DATA),SEEK_CUR);
fread(&m_pcmData,sizeof(unsigned short),1,fp);
while(!feof(fp)){
pcmDATA.dwSize+=2;
fwrite(&m_pcmData,sizeof(unsigned short),1,fpout);
fread(&m_pcmData,sizeof(unsigned short),1,fp);
}
pcmHEADER.dwSize=44+pcmDATA.dwSize;
rewind(fpout);
fwrite(&pcmHEADER,sizeof(WAVE_HEADER),1,fpout);
fseek(fpout,sizeof(WAVE_FMT),SEEK_CUR);
fwrite(&pcmDATA,sizeof(WAVE_DATA),1,fpout);
fclose(fp);
fclose(fpout);
return 0;
}
int main()
{
simplest_pcm16le_to_wave("48000Hz-s16le-2ch-ChengDu.pcm",2,48000,"output_nocturne.wav");
return 0;
}
代码来自:https://blog.csdn.net/leixiaohua1020/article/details/50534316
🎄五、总结
本文详细介绍wav音频文件的结构,包含RIFF规范、完整的各个块解析、以及提供了pcm转wav的C语言代码。
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁
参考资料:
WAVE PCM soundfile format
视音频数据处理入门:PCM音频采样数据处理
wav头文件解析
RIFF和WAVE音频文件格式
音频——WAV 格式详解
https://blog.csdn.net/jackailson/article/details/105183413