参考资料
视音频数据处理入门:PCM音频采样数据处理: https://blog.csdn.net/leixiaohua1020/article/details/50534316
WAV文件格式详解: https://blog.csdn.net/imxiangzi/article/details/80265978
在看雷神的pcm采样数据处理的文章时,看到了最后一个章节pcm转wav,测试了下,但是最后生成的wav文件没法用windowsplayer打开。
下载了一个正常的wav文件,发现可以播放,可以肯定的是代码里封装的格式有问题。
用ultraedit比对了下,发现了有几处不正常的地方,就顺便给改了。
代码如下:
#include <stdio.h> /** * Split Left and Right channel of 16LE PCM file. * @param url Location of PCM file. * */ int simplest_pcm16le_split(char *url){ FILE *fp=fopen(url,"rb+"); FILE *fp1=fopen("output_l.pcm","wb+"); FILE *fp2=fopen("output_r.pcm","wb+"); unsigned char *sample=(unsigned char *)malloc(4); while(!feof(fp)){ fread(sample,1,4,fp); //L fwrite(sample,1,2,fp1); //R fwrite(sample+2,1,2,fp2); } free(sample); fclose(fp); fclose(fp1); fclose(fp2); return 0; } /** * Halve volume of Left channel of 16LE PCM file * @param url Location of PCM file. */ int simplest_pcm16le_halfvolumeleft(char *url){ FILE *fp=fopen(url,"rb+"); FILE *fp1=fopen("output_halfleft.pcm","wb+"); int cnt=0; unsigned char *sample=(unsigned char *)malloc(4); while(!feof(fp)){ short *samplenum=NULL; // short 占用2字节 fread(sample,1,4,fp); samplenum=(short *)sample; *samplenum=*samplenum/2; //L fwrite(sample,1,2,fp1); //R fwrite(sample+2,1,2,fp1); cnt++; } printf("Sample Cnt:%d\n",cnt); free(sample); fclose(fp); fclose(fp1); return 0; } /** * Re-sample to double the speed of 16LE PCM file * @param url Location of PCM file. */ int simplest_pcm16le_doublespeed(char *url){ FILE *fp=fopen(url,"rb+"); FILE *fp1=fopen("output_doublespeed.pcm","wb+"); int cnt=0; unsigned char *sample=(unsigned char *)malloc(4); while(!feof(fp)){ fread(sample,1,4,fp); if(cnt%2!=0){ //L fwrite(sample,1,2,fp1); //R fwrite(sample+2,1,2,fp1); } cnt++; } printf("Sample Cnt:%d\n",cnt); free(sample); fclose(fp); fclose(fp1); return 0; } /** * Convert PCM-16 data to PCM-8 data. * @param url Location of PCM file. */ int simplest_pcm16le_to_pcm8(char *url){ FILE *fp=fopen(url,"rb+"); FILE *fp1=fopen("output_8.pcm","wb+"); int cnt=0; unsigned char *sample=(unsigned char *)malloc(4); while(!feof(fp)){ short *samplenum16=NULL; char samplenum8=0; unsigned char samplenum8_u=0; fread(sample,1,4,fp); //(-32768-32767) samplenum16=(short *)sample; samplenum8=(*samplenum16)>>8; //(0-255) samplenum8_u=samplenum8+128; //L fwrite(&samplenum8_u,1,1,fp1); samplenum16=(short *)(sample+2); samplenum8=(*samplenum16)>>8; samplenum8_u=samplenum8+128; //R fwrite(&samplenum8_u,1,1,fp1); cnt++; } printf("Sample Cnt:%d\n",cnt); free(sample); fclose(fp); fclose(fp1); return 0; } /** * Cut a 16LE PCM single channel file. * @param url Location of PCM file. * @param start_num start point * @param dur_num how much point to cut */ int simplest_pcm16le_cut_singlechannel(char *url,int start_num,int dur_num){ FILE *fp=fopen(url,"rb+"); FILE *fp1=fopen("output_cut.pcm","wb+"); FILE *fp_stat=fopen("output_cut.txt","wb+"); unsigned char *sample=(unsigned char *)malloc(2); int cnt=0; while(!feof(fp)){ fread(sample,1,2,fp); if(cnt>start_num&&cnt<=(start_num+dur_num)){ fwrite(sample,1,2,fp1); short samplenum=sample[1]; samplenum=samplenum*256; samplenum=samplenum+sample[0]; fprintf(fp_stat,"%6d,",samplenum); if(cnt%10==0) fprintf(fp_stat,"\n",samplenum); } cnt++; } free(sample); fclose(fp); fclose(fp1); fclose(fp_stat); return 0; } /** * 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,int bits, const char *wavepath) { //int bits = 16; // 每个channel每次采样的精度,占多少位 BitsPerSample typedef unsigned int uint32; typedef unsigned short uint16; typedef struct WAVE_HEADER{ char fccID[4]; // "RIFF" uint32 dwSize; // 44 + WAVE_DATA->dwSize 小端模式 char fccType[4]; // "WAVE" }WAVE_HEADER; // 24 typedef struct WAVE_FMT{ char fccID[4]; // "fmt " uint32 dwSize; // 16 小端模式 表示该区块数据的长度(不包含ID和Size的长度) uint16 wFormatTag; // AudioFormat表示Data区块存储的音频数据的格式,PCM音频数据的值为1 小端模式 uint16 wChannels; // NumChannels表示音频数据的声道数,1:单声道,2:双声道 小端模式 uint32 dwSamplesPerSec; // 采样率 小端模式 uint32 dwAvgBytesPerSec; // 小端模式 uint16 wBlockAlign; // BlockAlign每个采样所需的字节数 = NumChannels * BitsPerSample / 8 小端模式 uint16 uiBitsPerSample; //BitsPerSample每个采样存储的bit数,8:8bit,16:16bit,32:32bit 小端模式 }WAVE_FMT; // 8B typedef struct WAVE_DATA{ char fccID[4]; // "data" uint32 dwSize; // Size表示音频数据的长度,N = ByteRate * seconds 小端模式 }WAVE_DATA; if(channels==0||sample_rate==0){ channels = 2; sample_rate = 44100; } 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; // 比特率 = SampleRate * NumChannels * BitsPerSample / 8 pcmFMT.dwAvgBytesPerSec=pcmFMT.dwSamplesPerSec*channels*bits/8; pcmFMT.uiBitsPerSample=bits; memcpy(pcmFMT.fccID,"fmt ",strlen("fmt ")); pcmFMT.dwSize=16; // BlockAlign每个采样所需的字节数 = NumChannels * BitsPerSample / 8 pcmFMT.wBlockAlign=channels * bits / 8; 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=sizeof(WAVE_FMT) + sizeof(WAVE_DATA) + 4 +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_split("NocturneNo2inEflat_44.1k_s16le.pcm"); simplest_pcm16le_halfvolumeleft("NocturneNo2inEflat_44.1k_s16le.pcm"); simplest_pcm16le_doublespeed("NocturneNo2inEflat_44.1k_s16le.pcm"); simplest_pcm16le_to_pcm8("NocturneNo2inEflat_44.1k_s16le.pcm"); //simplest_pcm16le_cut_singlechannel("drum.pcm",2360,120); simplest_pcm16le_to_wave("NocturneNo2inEflat_44.1k_s16le.pcm",2, 44100, 16, "output_nocturne.wav"); return 0; }