wav的文件格式如下图所示,这张图在很多地方都有,这张图完整的说明了wav文件的数据组成情况
按照图上的描述,一个wav文件的数据组成,应该如下所示
chunk名称 | 偏移地址 | 数据长度(byte) | 字段名称 | 字段说明 |
---|---|---|---|---|
RIFF | 00H | 4 | ChunkID | 固定为大写字符串"RIFF" |
RIFF | 04H | 4 | Chunk Size | 从下一个字段首地址开始到文件末尾的总字节数。该字段的数值加 8 为当前文件的实际长度 |
RIFF | 08H | 4 | Format | 所有 WAV 格式的文件此处为字符串"WAVE",标明该文件是 WAV 格式文件 |
fmt | 0CH | 4 | Subchank ID | 固定为小写字符串"fmt " |
fmt | 10H | 4 | Subchank Size | fmt这个chunk的长度 |
fmt | 14H | 2 | AudioFormat | 这个文件中的音频编码格式见后面的表 |
fmt | 16H | 2 | NumChannels | 声道数量 |
fmt | 18H | 4 | SampleRate | 采样率例如48000,44100等 |
fmt | 1CH | 4 | ByteRate | 数据传输速率,播放这个文件时缓冲区的最小大小 = NumChannels * BitsPerSample/8 |
fmt | 20H | 2 | BlockAlign | 每个采样所需的字节数 = NumChannels * BitsPerSample / 8 |
fmt | 22H | 2 | BitsPerSample | 采样位数,8bit,16bit,32bit等 |
RIFF | 24H | 4 | Subchank2 ID | 固定为字符串data |
RIFF | 28H | 4 | Subchank2 Size | 整个datachunk的长度 |
RIFF | 2CH | 4 | data | 原始的PCM数据 |
对于AudioFormat,一般有以下几种,不同的AudioFormat, fmt的长度是不一样的
enum Encod_format_PCM{ //fmt lenth
PCM = 0x01, //16
Microsoft_ADPCM, //18
IEEE_float, //18
ITU_G_711_a_law = 0x06, //18
ITU_G_711_u_law, //18
GSM_6_10 = 0x31, //20
ITU_G_721_ADPCM = 0x40, //
};
PCM音频格式就是没有经过压缩的格式,这种格式解析起来比较方便,但占用存储空间较大.
在PCM编码的wav文件中,PCM数据就是存储在data里面的,以小端方式存储,数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中.
PCM数据类型
8Bit 单声道
低地址---------------------------------->高地址
声道1数据0 声道1数据1 声道1数据2 声道1数据3
8Bit 双声道
声道1数据0 声道2数据0 声道1数据1 声道2数据1
16Bit 单声道
低地址---------------------------------->高地址
声道0低位,声道0高位 声道0低位,声道0高位
16Bit 双声道
低地址---------------------------------->高地址
声道0低位,声道0高位 声道1低位,声道1高位
wav_parse.h
头文件,包含了数据结构的定义
#ifndef WAV_TEST
#define WAV_TEST
#include "string"
using namespace std;
class Wav_File{
//RIFF chunk
public:
Wav_File(char *paths);
//int Save_wav_file(char *paths, WAV_file *wav_file);
//int Modify_Volume(WAV_file *wav_file, int change_val);
private:
string paths;
/* chunk "riff" */
char Wav_RIFF_ChunkID[4]; /* "RIFF" */
/* sub-chunk-size */
unsigned int Wav_RIFF_ChunkSize; /* 36 + Subchunk2Size */
/* sub-chunk-data */
char Wav_RIFF_Type[4]; /* "WAVE" */
/* sub-chunk "fmt" */
char Wav_Fmt_ID[4]; /* "fmt " */
/* sub-chunk-size */
unsigned int Wav_Fmt_ChunkSize; /* 16 for PCM */
/* sub-chunk-data */
unsigned int Wav_Fmt_AudioFormat; /* PCM = 1*/
unsigned int Wav_Fmt_NumChannels; /* Mono = 1, Stereo = 2, etc. */
unsigned int Wav_Fmt_SampleRate; /* 8000, 44100, 48000, etc. */
unsigned int Wav_Fmt_ByteRate; /* = SampleRate * NumChannels * BitsPerSample/8 */
short Wav_Fmt_BlockAlign; /* = NumChannels * BitsPerSample/8 */
unsigned int Wav_Fmt_BitsPerSample; /* 8bits, 16bits, 32bits, etc. */
/* sub-chunk "data" */
char Wav_Data_ID[4]; /* "data" */
/* sub-chunk-size */
unsigned int Wav_Data_Size; /* data size */
/* sub-chunk-data */
// Data_block_t block;
char *Wav_Data_RawData;
};
#endif // WAV_TEST
wav_parse.cpp
真正的解析代码,可以解析出wav的数据头,以及获取到data的指针,可以对data直接进行操作.
#include "stdio.h"
#include "stdlib.h"
#include "wav_parse.h"
#include "string.h"
#include "math.h"
static int Find_str(const char *str_orig,const char *obj_str)
{
int i = 0;
for(i = 0;i >= 0;i++)
{
if( (*(str_orig+i)) == (*obj_str))
{
{
if((*(str_orig+i+1)) == (*(obj_str+1)))
break;
}
}
}
return i;
}
Wav_File::Wav_File(char *paths)
{
FILE *wav_file = NULL;
unsigned int current_file_pos = 0;
unsigned int file_size;
char *orig_buffer;
int data_pos = 0;
int fmt_pos = 0;
this->paths = paths;
fprintf(stderr,"zrh_test\n");
wav_file = fopen(paths,"rb");
if (wav_file == NULL)
{
//
fprintf(stderr,"%s open failed\n",paths);
goto out2;
}
//get file size
current_file_pos = ftell(wav_file);
fseek(wav_file,0,SEEK_END);
file_size = ftell(wav_file);
fseek( wav_file,current_file_pos,SEEK_SET ); //back to 0 pos
fprintf(stderr,"file_size = %d\n",file_size);
orig_buffer = (char *)malloc(file_size);
fread(orig_buffer,file_size,1,wav_file);
data_pos = Find_str(orig_buffer,"data");
fmt_pos = Find_str(orig_buffer,"fmt");
fprintf(stderr, "Find fmt pos i = %d\n",fmt_pos);
fprintf(stderr, "Find data pos i = %d\n",data_pos);
//get riff chunk data 全是魔鬼数字,根据前面的长度定义,取出各个数据
memcpy(Wav_RIFF_ChunkID ,orig_buffer ,4); //ID
Wav_RIFF_ChunkSize = *((unsigned int*)(orig_buffer+4)); //SIZE
fprintf(stderr, "Wav_RIFF_ChunkSize = %d\n",Wav_RIFF_ChunkSize); //TYPE
memcpy(Wav_RIFF_Type ,orig_buffer+8 ,4);
//get format chunk data
memcpy(Wav_Fmt_ID,orig_buffer+fmt_pos ,4); //ID
Wav_Fmt_ChunkSize = *((unsigned int*)(orig_buffer+fmt_pos+4)); //Size
Wav_Fmt_AudioFormat = *((short*)(orig_buffer+fmt_pos+4+4)); //AudioFormat
Wav_Fmt_NumChannels = *((short*)(orig_buffer+fmt_pos+4+4+2)); //NumChannels
Wav_Fmt_SampleRate = *((unsigned int*)(orig_buffer+fmt_pos+4+4+2+2)); //SampleRate
Wav_Fmt_BlockAlign = *((short*)(orig_buffer+fmt_pos+4+4+2+2+4+4)); //BlockAlign
Wav_Fmt_ByteRate = *((unsigned int*)(orig_buffer+fmt_pos+4+4+2+2+4)); //ByteRate
Wav_Fmt_BitsPerSample = *((short*)(orig_buffer+fmt_pos+4+4+2+2+4+4+2)); //BitsPerSample
//get data chunk data
memcpy(Wav_Data_ID,orig_buffer+data_pos ,4); //ID
fprintf(stderr,"Wav_Fmt_ID = %x\n",*((unsigned int*)(orig_buffer+data_pos)));
Wav_Data_Size = *((unsigned int*)(orig_buffer+data_pos+4));//Size
Wav_Data_RawData = (char *)malloc(Wav_Data_Size); //给data分配内存,太大的wav文件会失败
if(Wav_Data_RawData == NULL)
{
fprintf(stderr,"Alloc Wav_Data_RawData failed\n");
goto out1;
}
memcpy(Wav_Data_RawData, orig_buffer+data_pos+4+4,Wav_Data_Size); //将data的数据全部拷贝到结构体中
//printf Wav file message
fprintf(stderr,"Wav_Fmt_AudioFormat = %d\n",Wav_Fmt_AudioFormat);
fprintf(stderr,"Wav_Fmt_NumChannels = %d\n",Wav_Fmt_NumChannels);
fprintf(stderr,"Wav_Fmt_SampleRate = %d\n",Wav_Fmt_SampleRate);
fprintf(stderr,"Wav_Fmt_ByteRate = %d\n",Wav_Fmt_ByteRate);
fprintf(stderr,"Wav_Fmt_BlockAlign = %d\n",Wav_Fmt_BlockAlign);
fprintf(stderr,"Wav_Fmt_BitsPerSample = %d\n",Wav_Fmt_BitsPerSample);
fprintf(stderr,"Wav_Data_Size = %x\n",Wav_Data_Size);
fclose(wav_file);
free(orig_buffer);
return;
out1:
fclose(wav_file);
fprintf(stderr, "zrh_test:exit -1");
out2:
fprintf(stderr, "zrh_test:exit -2");
}
/*将读取到的数据重新保存为一个wav,可以在对data进行一系列操作之后,再重新另存为文件
int Save_wav_file(char *paths, WAV_file *wav_file)
{
FILE *save_wav_file = NULL;
int i,j;
unsigned int current_file_pos = 0;
unsigned int file_size;
char *orig_buffer = NULL;
printf("%s:wav_file addr = %p\n",__func__,wav_file);
fprintf(stderr,"%s:enter\n",__func__);
fprintf(stderr,"%s:file_size = %d\n",__func__, (wav_file->riff.ChunkSize+8));
orig_buffer = malloc((wav_file->riff.ChunkSize)+8);
if(orig_buffer == NULL)
{
fprintf(stderr,"%s:failed to malloc orig_buffer!!!\n",__func__);
return -1;
}
fprintf(stderr, "%s:Ready to open file:\n",__func__);
//restore data from WAV_file
//riff
memcpy(orig_buffer, wav_file->riff.ChunkID,4); //ID
*((unsigned int*)(orig_buffer+4)) = wav_file->riff.ChunkSize ; //SIZE
memcpy(orig_buffer+8 ,wav_file->riff.Type ,4);
//get format chunk data
memcpy(orig_buffer+fmt_pos,wav_file->fmt.ID ,4); //ID
*((unsigned int*)(orig_buffer+fmt_pos+4)) = wav_file->fmt.ChunkSize ; //Size
*((short*)(orig_buffer+fmt_pos+4+4)) = wav_file->fmt.AudioFormat; //AudioFormat
*((short*)(orig_buffer+fmt_pos+4+4+2)) = wav_file->fmt.NumChannels; //NumChannels
*((unsigned int*)(orig_buffer+fmt_pos+4+4+2+2)) = wav_file->fmt.SampleRate; //SampleRate
*((short*)(orig_buffer+fmt_pos+4+4+2+2+4+4)) = wav_file->fmt.BlockAlign; //BlockAlign
*((unsigned int*)(orig_buffer+fmt_pos+4+4+2+2+4)) = wav_file->fmt.ByteRate; //ByteRate
*((short*)(orig_buffer+fmt_pos+4+4+2+2+4+4+2)) = wav_file->fmt.BitsPerSample; //BitsPerSample
memcpy(orig_buffer+data_pos,wav_file->data.ID,4); //ID
*((unsigned int*)(orig_buffer+data_pos+4)) = wav_file->data.Size;//Size
int t_val = 0;
t_val = memcpy(orig_buffer+data_pos+4+4, wav_file->data.RawData,wav_file->data.Size);
fprintf(stderr, "%s:wave data size = %d,malloc succeed in %d\n",__func__,wav_file->data.Size, t_val);
fprintf(stderr, "%s:start to write file:\n",__func__);
//restore end
save_wav_file = fopen(paths,"wb+");
float max = 100,tmp = 0;
for(i = 0;i < wav_file->data.Size; )
{
//fprintf(stderr, "%s:zrh_test:before = %x\n",__func__, *((int*)(orig_buffer+data_pos+4+4+i)));
//fprintf(stderr, "%s:zrh_test:before = %d\n",__func__, Get_Volume (*((int*)(orig_buffer+data_pos+4+4+i))));
//Get_Volume (*((int*)(orig_buffer+data_pos+4+4+i)));
for(j=0;j<20;j++)
{
if(*((short*)(orig_buffer+data_pos+4+4+i)) == 0)
{
i = i+2;
continue;
}
tmp = Get_Volume(*((short*)(orig_buffer+data_pos+4+4+i)));
i = i+2;
}
if(max > (tmp/20))
max = tmp;
tmp=0;
//fprintf(stderr, "%s:zrh_test:before = %d\n",__func__, (*(( int*)(orig_buffer+data_pos+4+4+i)))); //int
//*((float*)(orig_buffer+data_pos+4+4+i)) = ((*((float*)(orig_buffer+data_pos+4+4+i))) * 8); //195553 383999
//fprintf(stderr, "%s:zrh_test:after = %d\n",__func__, *((int*)(orig_buffer+data_pos+4+4+(i/4))));
}
fprintf(stderr, "Max volume = %f\n", max);
fwrite(orig_buffer, (wav_file->riff.ChunkSize)+8, 1, save_wav_file);
fclose(save_wav_file);
return 0;
}
*/
/*
float Get_Volume( short value)
{
if(value == 0)
return 0;
float db = 0;
value = abs(value);
db = 20.0*(log10(32767) - log10(value));
//fprintf(stderr, "%s:zrh_test:value = %d\n",__func__, value);
//fprintf(stderr, "%s:zrh_test:value = %f\n",__func__, db); //190820720
return db;
}
*/
还有就是main函数
#include
#include
#include "wav_parse.h"
#include
#include
#include
// 256
// 768
int main()
{
Wav_File *z1 = new Wav_File("wave.wav");
//fprintf(stderr,"%s:orig_wav_file addr = %p\n",__func__,orig_wav_file);
return 0;
}