2010-03-16 143 views
6

我正在处理一个wav文件的幅度和缩放一些十进制的因素。我试图围绕如何以有效的内存方式读取和重写文件,同时试图解决语言的细微差别(我是C的新手)。该文件可以是8位或16位格式。我认为这样做的方法是首先将header data读入一些预定义的结构中,然后在循环中处理实际数据,在该循环中我将读取一块数据到缓冲区中,执行所需的任何操作,以及然后将其写入输出。处理音频wav文件与C

#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, 1, sizeof(meta), outfile); 

     while (!feof(infile)) 
     { 
      if (is_8bit) 
      { 
       fread(inbuff8, 1, BUFSIZE, infile); 
      } else { 
       fread(inbuff16, 1, BUFSIZE, 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, 1, BUFSIZE, outfile); 
      } else { 
       fwrite(outbuff16, 1, BUFSIZE, 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; 
} 

我在最后得到不同的文件大小(以1K左右,对于一个40MB的文件),我怀疑这是由于这样的事实,我正在写一整个缓冲区输出,即使该文件在填充整个缓冲区大小之前可能已经终止。另外,输出文件会搞砸 - 不会播放或打开 - 所以我可能会做错整个事情。任何关于我搞砸的提示都会很棒。谢谢!

+1

当你说的输入和输出文件具有不同的大小,是输出文件大于还是小于输入? – bta 2010-03-16 19:51:21

+1

输出较大 – sa125 2010-03-16 20:07:35

回答

8

你正在读取字节,而不是16位的样品在这个else分支:

while (!feof(infile)) 
    { 
     if (is_8bit) 
     { 
      fread(inbuff8, 1, BUFSIZE, infile); 
     } else { 
      fread(inbuff16, 1, BUFSIZE, infile); // <-- should be BUFSIZE*2  
     } 

缩放时,您不饱和值,例如原来的16位采样= 32000和系数= 1.5将环绕整数值,而不是它夹到最高的32767

你不会在RIFF和其它头看看所有的。在WAV文件中,音频数据后面可能会出现一些信息页脚或其他标题。换句话说:您的header结构太静态。你也应该从文件中读取WAV格式,而不是有一个参数说它是8位样本。

这是不会发生的:

   outbuff16[i] = factor * inbuff16[i]; 
       if ((int)outbuff16[i] > MAX_16BIT_AMP) 

8位/ 16位的值永远不会大于三万二千七百六十八分之二百五十五除非您的计算机中插入一些魔术位到内存时的整数溢出:P

音频样本被签名,所以范围是-128; 127和-32768; 32767。在乘法表达式中必须进行溢出检查。您还正在对浮点到整数舍入模式进行假设,这是可配置的,应予以考虑。可能是类似if(roundf(factor * inbuff16[i]) > 32767 || roundf(factor * inbuff16[i]) < -32768)的东西。

您不存储fread的结果,因此您会向输出文件写入太多样本。

作为最后一点,您正在重新发明轮子。只要这是为了学习,没关系。否则你应该使用现有的库。

+1

我认为你抓住了大部分东西 - 我会补充说sizeof(元)是错误的时候写出头为元是一个指针 - 需要是sizeof(header)或sizeof(* meta)。 – Dipstick 2010-03-16 20:04:24

+0

这是很好的反馈,我会尝试这些事情 – sa125 2010-03-16 20:08:01

+0

回复:第3项,你也不能根据你最喜欢的波形编辑器的输出做出假设,因为它们都有怪癖。很显然,libsndfile中的大部分工作都是在各种波形编辑器中处理奇怪的问题。如果你对libsndfile许可证感到满意,所有这些都会减少到几个sf_read_floats和sf_write_floats,并引入了一些初始化的东西。 – kibibu 2010-03-16 23:49:58

1

我建议在十六进制编辑器中查看原始文件和输出文件,看看您是否正确地重新写入数据。如果生成的文件不能播放或打开,则输出文件的标题可能不正确。

另一种选择是删除音频处理逻辑,只需将源文件读入内部缓冲区并将其写入文件即可。如果您的代码可以以这种方式生成有效的工作输出文件,那么您可以将问题缩小到处理代码。

您可能还想从小于40Mb的文件开始。如果没有其他,请复制该输入文件并将其缩减为几秒钟的音频。较小的文件将更容易手动检查。请致电fread()fwrite()验证其返回值。这些函数返回读取或写入的元素数量,如果对任何一个函数的调用返回的值比预期的小,那么这可能是文件大小差异的根源。

此外,fread的第二个参数是以字节为单位。因此,如果您想读取填充整个缓冲区,则需要说些更像fread(inbuff16, sizeof(inbuff16[0]), BUFSIZE, infile);的内容。当前的代码只能读取BUFSIZE字节(它适用于8位情况,但为了清晰起见,我建议更改它)。

+0

你是对的文件大小,它绝对是值得收缩只是为了看看它是否工作 – sa125 2010-03-16 20:09:08

5

使用库来读取和写入声音文件要好得多。例如。 libsndfile。该网页有一个“其他类似项目”的列表,你也可以看看。 sndfile-tools可能是学习如何使用该库的很好的代码示例。

0

如果可能的话,您可能希望查看与C不同的语言,除非它专门用于C应用程序。

  • 例如python有一个很好的wav包,它可以很容易地读取ww文件&。
  • 对于更多专业或学术用途,第一个指导是MATLAB,它也很容易读取wav文件(直接导入然后作为单个表达式运行的矢量)。
1

也无需用于读取WAV头此以下行(使头48个字节长,而不是“标准” 44):

short int extra_param_size;