本篇记录了学习雷霄骅的pcm16le转wav代码后,为其添加了注释和自己的理解,方便以后自己阅读。
可能用到的基础知识:
pcm-------原始的音频数据,windows没法直接播放,因为不知道这是什么。
WAVE格式音频(扩展名为“.wav”)是Windows系统中最常见的一种音频。该格式的实质就是在PCM文件的前面加了一个 文件头。
//本程序的函数就可以通过在PCM16le文件前面加一个WAVE文件头从而封装为WAVE格式音频。
int simplest_pcm16le_to_wave(const char *pcmpath,int channels,int sample_rate,const char *wavepath)
{
typedef struct WAVE_HEADER{
char fccID[4]; //RIFF
//wav整个文件大小
unsigned long dwSize;
char fccType[4]; //WAVE
}WAVE_HEADER;
typedef struct WAVE_FMT{
char fccID[4]; //"fmt ",注意后面有一个空过渡字节‘ ’
//WAVE_FMT的字节字段长度减去fccID和dwSize的字节长度
unsigned long dwSize; //sizeof(WAVE_FMT)-sizeof(fccID)-sizeof(dwSize)
//音频格式
unsigned short audioFormat;//表示Data区块存储的音频数据的格式,PCM音频数据的值为1
//声道
unsigned short wChannels;
//采样率
unsigned long sampleRate;
//比特率
unsigned long byteRate;//存储比特率 SampleRate *NumChannels * BitsPerSample/8
//块对齐
unsigned short wBlockAlign; //每次采样所需的字节数 = NumChannels * BitsPerSample / 8
//每次采样的bit数-----位深度
unsigned short bitsPerSample;
}WAVE_FMT;
typedef struct WAVE_DATA{
char fccID[4];//data
// wave 数据大小,这个例子即使pcm的大小
unsigned long 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"));
//先跳过WAVE_HEADER的存储位置不写,因为还不知道WAVE_HEADER.dwSize的值(整个文件的大小)
fseek(fpout,sizeof(WAVE_HEADER),1);
//WAVE_FMT
pcmFMT.sampleRate=sample_rate;
pcmFMT.byteRate=pcmFMT.sampleRate*16*2/8;
pcmFMT.bitsPerSample=bits;
memcpy(pcmFMT.fccID,"fmt ",strlen("fmt "));
pcmFMT.dwSize=16;
pcmFMT.wBlockAlign = 16 * 2 / 8;
pcmFMT.wChannels=channels;
pcmFMT.audioFormat=1;
//写WAVE_FMT
fwrite(&pcmFMT,sizeof(WAVE_FMT),1,fpout);
//WAVE_DATA;
memcpy(pcmDATA.fccID,"data",strlen("data"));
pcmDATA.dwSize=0;
//跳过WAVE_DATA不写,等知道了WAVE_DATA.dwSize再写
fseek(fpout,sizeof(WAVE_DATA),SEEK_CUR);
fread(&m_pcmData,sizeof(unsigned short),1,fp);
while(!feof(fp)){
//最终获得pcm的大小
pcmDATA.dwSize+=2;
//向文件尾部写pcm数据
fwrite(&m_pcmData,sizeof(unsigned short),1,fpout);
fread(&m_pcmData,sizeof(unsigned short),1,fp);
}
//计算出整个文件的大小
pcmHEADER.dwSize = sizeof(WAVE_HEADER)+sizeof(WAVE_FMT)+sizeof(WAVE_DATA)+pcmDATA.dwSize;
//设置文件位置为给定流 stream 的文件的开头
rewind(fpout);
//写WAVE_HEADER
fwrite(&pcmHEADER,sizeof(WAVE_HEADER),1,fpout);
//WAVE_FMT已经写过了,跳过
fseek(fpout,sizeof(WAVE_FMT),SEEK_CUR);
//写WAVE_DATA
fwrite(&pcmDATA,sizeof(WAVE_DATA),1,fpout);
fclose(fp);
fclose(fpout);
system("pause");
return 0;
}
添加的头文件
byte[] header = new byte[44];
header[0] = 'R'; // RIFF
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
header[4] = (byte) (totalWavSize & 0xff);//数据大小
header[5] = (byte) ((totalWavSize >> 8) & 0xff);
header[6] = (byte) ((totalWavSize >> 16) & 0xff);
header[7] = (byte) ((totalWavSize >> 24) & 0xff);
header[8] = 'W';//WAVE
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
//FMT Chunk
header[12] = 'f'; // 'fmt '
header[13] = 'm';
header[14] = 't';
header[15] = ' ';//过渡字节
//数据大小
header[16] = 16; // 4 bytes: size of 'fmt ' chunk
header[17] = 0;
header[18] = 0;
header[19] = 0;
//编码方式 10H为PCM编码格式
header[20] = 1; // format = 1
header[21] = 0;
//通道数
header[22] = (byte) channels;
header[23] = 0;
//采样率,每个通道的播放速度
header[24] = (byte) (sampleRate & 0xff);
header[25] = (byte) ((sampleRate >> 8) & 0xff);
header[26] = (byte) ((sampleRate >> 16) & 0xff);
header[27] = (byte) ((sampleRate >> 24) & 0xff);
//音频数据传送速率,采样率*通道数*采样深度/8
header[28] = (byte) (byteRate & 0xff);
header[29] = (byte) ((byteRate >> 8) & 0xff);
header[30] = (byte) ((byteRate >> 16) & 0xff);
header[31] = (byte) ((byteRate >> 24) & 0xff);
// 确定系统一次要处理多少个这样字节的数据,确定缓冲区,通道数*采样位数
header[32] = (byte) (channels * 16 / 8);
header[33] = 0;
//每个样本的数据位数
header[34] = 16;
header[35] = 0;
//Data chunk
header[36] = 'd';//data
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
header[40] = (byte) (totalPcmSize & 0xff);
header[41] = (byte) ((totalPcmSize >> 8) & 0xff);
header[42] = (byte) ((totalPcmSize >> 16) & 0xff);
header[43] = (byte) ((totalPcmSize >> 24) & 0xff);
os.write(header, 0, 44);