我正在处理wav文件的幅度并按一些小数因子缩放它.我试图以有效记忆的方式阅读和重写文件,同时也试图解决语言的细微差别(我是C的新手).该文件可以是8位或16位格式.我想这样做的方法是首先将
header data读入一些预定义的结构,然后在一个循环中处理实际数据,在那里我将一大块数据读入缓冲区,做任何需要它,以及然后将其写入输出.
#include <stdio.h> #include <stdlib.h> typedef struct header { char chunk_id[4]; int chunk_size; char format[4]; char subchunk1_id[4]; int subchunk1_size; short int audio_format; short int num_channels; int sample_rate; int byte_rate; short int block_align; short int bits_per_sample; short int extra_param_size; char subchunk2_id[4]; int subchunk2_size; } header; typedef struct header* header_p; void scale_wav_file(char * input,float factor,int is_8bit) { FILE * infile = fopen(input,"rb"); FILE * outfile = fopen("outfile.wav","wb"); int BUFSIZE = 4000,i,MAX_8BIT_AMP = 255,MAX_16BIT_AMP = 32678; // used for processing 8-bit file unsigned char inbuff8[BUFSIZE],outbuff8[BUFSIZE]; // used for processing 16-bit file short int inbuff16[BUFSIZE],outbuff16[BUFSIZE]; // header_p points to a header struct that contains the file's Metadata fields header_p Meta = (header_p)malloc(sizeof(header)); if (infile) { // read and write header data fread(Meta,1,sizeof(header),infile); fwrite(Meta,sizeof(Meta),outfile); while (!feof(infile)) { if (is_8bit) { fread(inbuff8,BUFSIZE,infile); } else { fread(inbuff16,infile); } // scale amplitude for 8/16 bits for (i=0; i < BUFSIZE; ++i) { if (is_8bit) { outbuff8[i] = factor * inbuff8[i]; if ((int)outbuff8[i] > MAX_8BIT_AMP) { outbuff8[i] = MAX_8BIT_AMP; } } else { outbuff16[i] = factor * inbuff16[i]; if ((int)outbuff16[i] > MAX_16BIT_AMP) { outbuff16[i] = MAX_16BIT_AMP; } else if ((int)outbuff16[i] < -MAX_16BIT_AMP) { outbuff16[i] = -MAX_16BIT_AMP; } } } // write to output file for 8/16 bit if (is_8bit) { fwrite(outbuff8,outfile); } else { fwrite(outbuff16,outfile); } } } // cleanup if (infile) { fclose(infile); } if (outfile) { fclose(outfile); } if (Meta) { free(Meta); } } int main (int argc,char const *argv[]) { char infile[] = "file.wav"; float factor = 0.5; scale_wav_file(infile,factor,0); return 0; }
我在最后得到不同的文件大小(对于40Mb文件大约1k左右),我怀疑这是因为我正在向输出写入整个缓冲区,即使文件可能已终止在填充整个缓冲区大小之前.此外,输出文件搞砸了 – 不会播放或打开 – 所以我可能做错了.关于我搞砸的地方的任何提示都会很棒.谢谢!
解决方法
1您正在读取其他分支中的字节而不是16位样本:
while (!feof(infile)) { if (is_8bit) { fread(inbuff8,infile); } else { fread(inbuff16,infile); // <-- should be BUFSIZE*2 }
2缩放时,您不会使值饱和,例如原始的16位样本= 32000和因子= 1.5将环绕整数值,而不是将其钳位到最大值32767.
3你根本不看RIFF和其他标题.在WAV文件中,音频数据可能后跟一些信息页脚或前面有其他标题.或者换句话说:您的标头结构太静态了.您还应该从文件中读取WAV格式,而不是让参数说明它是8位样本.
4这不会发生:
outbuff16[i] = factor * inbuff16[i]; if ((int)outbuff16[i] > MAX_16BIT_AMP)
8位/ 16位值永远不会大于255/32768,除非您的计算机在整数溢出时将一些魔术位插入内存中:P
音频样本已签名,因此范围为-128; 127和-32768; 32767.溢出检查必须在乘法表达式中进行.您还可以对浮点到整数舍入模式进行假设,这种模式是可配置的,应予以考虑.也许是if(roundf(factor * inbuff16 [i])> 32767 || roundf(factor * inbuff16 [i])< -32768)之类的东西. 5您不存储fread的结果,因此您将向输出文件中写入太多样本. 6作为最后一点,你正在重新发明轮子.只要这是学习,那没关系.否则,您应该使用现有的库.