C语言 用 C 处理音频 wav 文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2457482/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
processing an audio wav file with C
提问by sa125
I'm working on processing the amplitude of a wav file and scaling it by some decimal factor. I'm trying to wrap my head around how to read and re-write the file in a memory-efficient way while also trying to tackle the nuances of the language (I'm new to C). The file can be in either an 8- or 16-bit format. The way I thought of doing this is by first reading the header datainto some pre-defined struct, and then processing the actual data in a loop where I'll read a chunk of data into a buffer, do whatever is needed to it, and then write it to the output.
我正在处理 wav 文件的幅度并按一些小数因子对其进行缩放。我正在努力思考如何以节省内存的方式读取和重写文件,同时还试图解决语言的细微差别(我是 C 的新手)。该文件可以是 8 位或 16 位格式。我想这样做的方法是首先将标头数据读入一些预定义的结构,然后在循环中处理实际数据,在该循环中我将一大块数据读入缓冲区,执行任何需要的操作,然后将其写入输出。
#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;
}
I'm getting differing file sizes at the end (by 1k or so, for a 40Mb file), and I suspect this is due to the fact that I'm writing an entire buffer to the output, even though the file may have terminated before filling the entire buffer size. Also, the output file is messed up - won't play or open - so I'm probably doing the whole thing wrong. Any tips on where I'm messing up will be great. Thanks!
最后我得到了不同的文件大小(对于 40Mb 的文件,相差 1k 左右),我怀疑这是因为我正在将整个缓冲区写入输出,即使文件可能已终止在填充整个缓冲区大小之前。此外,输出文件搞砸了 - 无法播放或打开 - 所以我可能做错了整件事。关于我在哪里搞砸的任何提示都会很棒。谢谢!
回答by AndiDog
1You're reading bytes instead of 16-bit samples in this else branch:
1在这个 else 分支中,您正在读取字节而不是 16 位样本:
while (!feof(infile))
{
if (is_8bit)
{
fread(inbuff8, 1, BUFSIZE, infile);
} else {
fread(inbuff16, 1, BUFSIZE, infile); // <-- should be BUFSIZE*2
}
2You don't saturate the values when scaling, e.g. original 16-bit sample = 32000 and factor = 1.5 will wrap around the integer value instead of clamping it to the maximum of 32767.
2缩放时不要使值饱和,例如原始 16 位样本 = 32000 和因子 = 1.5 将环绕整数值而不是将其钳位到最大值 32767。
3You don't look at the RIFF and other headers at all. In WAV files, it is possible that the audio data is followed by some informational footers or preceded by additional headers. Or in other words: Your headerstruct is too static. You should also read the WAV format from the file instead of having a parameter saying it's 8 bit samples.
3你根本不看 RIFF 和其他标题。在 WAV 文件中,音频数据后面可能有一些信息性页脚或前面有额外的标题。或者换句话说:你的header结构太静态了。您还应该从文件中读取 WAV 格式,而不是让参数表示它是 8 位样本。
4This just won't happen:
4这不会发生:
outbuff16[i] = factor * inbuff16[i];
if ((int)outbuff16[i] > MAX_16BIT_AMP)
8-bit/16-bit values will never be greater than 255/32768 except if your computer inserts some magic bits into the memory when integers overflows :P
8 位/16 位值永远不会大于 255/32768,除非您的计算机在整数溢出时将一些魔术位插入内存:P
And audio samples are signed, so the ranges are -128;127 and -32768;32767. Overflow checking must occur in the multiplication expression. You're also making assumptions on the floating-point-to-integer rounding mode, which is configurable and should be considered. Something like if(roundf(factor * inbuff16[i]) > 32767 || roundf(factor * inbuff16[i]) < -32768), maybe.
并且音频样本是有符号的,所以范围是 -128;127 和 -32768;32767。溢出检查必须发生在乘法表达式中。您还对浮点到整数舍入模式进行了假设,该模式是可配置的,应该予以考虑。类似的东西if(roundf(factor * inbuff16[i]) > 32767 || roundf(factor * inbuff16[i]) < -32768),也许吧。
5You don't store the result of fread, so you will write too many samples to the output file.
5您不存储 的结果fread,因此您将向输出文件写入太多样本。
6And as a last point, you're reinventing the wheel. As long as this is for learning, it's okay. Else you should use existing libraries.
6最后一点,您正在重新发明轮子。只要是学习用的就行。否则你应该使用现有的库。
回答by Craig McQueen
It is much better to use libraries for reading and writing sound files. E.g. libsndfile. That web page has a list of "other similar projects" you can also look at. The sndfile-toolscould be good code examples to learn how to use the library.
使用库来读取和写入声音文件要好得多。例如libsndfile。该网页有一个“其他类似项目”的列表,您也可以查看。这sndfile-tools可能是学习如何使用库的好代码示例。
回答by bta
I would recommend looking at the original file and the output file in a hex editor to see if you are re-writing the data properly. If the resulting file won't play or open, chances are the header of the output file isn't correct.
我建议在十六进制编辑器中查看原始文件和输出文件,看看您是否正确地重写了数据。如果生成的文件无法播放或打开,则输出文件的标题可能不正确。
Another option is to remove your audio processing logic and simply read in the source file to your internal buffer and write it out to a file. If your code can generate a valid, working output file in this manner, then you can narrow down the problem to your processing code.
另一种选择是删除您的音频处理逻辑,只需将源文件读入内部缓冲区并将其写出到文件中。如果您的代码可以以这种方式生成有效的工作输出文件,那么您可以将问题缩小到您的处理代码。
You may also want to start with a smaller file than 40Mb. If nothing else, make a copy of that input file and trim it down to a couple of seconds of audio. A smaller file will be easier to manually inspect.
您可能还想从小于 40Mb 的文件开始。如果不出意外,请复制该输入文件并将其修剪为几秒钟的音频。较小的文件将更容易手动检查。
Edit:The calls to fread()and fwrite()need to have their return values verified. These functions return the number of elements read or written, and if a call to either function returns a value less than expected then this could be the source of your file size difference.
编辑:调用fread()并fwrite()需要验证其返回值。这些函数返回读取或写入的元素数,如果对任一函数的调用返回的值小于预期值,则这可能是文件大小差异的根源。
Also, the second parameter to freadis in bytes. Therefore, if you want to read-fill an entire buffer, you would need to say something more like fread(inbuff16, sizeof(inbuff16[0]), BUFSIZE, infile);. The current code will only read in BUFSIZEbytes (which works for the 8-bit case by coincidence, but I would recommend changing it too for clarity).
此外,第二个参数 tofread以字节为单位。因此,如果您想读取填充整个缓冲区,则需要说更像fread(inbuff16, sizeof(inbuff16[0]), BUFSIZE, infile);. 当前代码只能以BUFSIZE字节为单位读取(巧合的是它适用于 8 位情况,但为了清楚起见,我也建议更改它)。
回答by Okko
This following line is also not needed for reading WAV headers (makes the header 48 bytes long, instead of the "standard" 44):
读取 WAV 标头也不需要以下行(使标头长 48 个字节,而不是“标准”44 个字节):
short int extra_param_size;
回答by Jonathan Cline IEEE
If possible you may want to look at a different language than C unless it is specifically for a C application.
如果可能,您可能希望查看与 C 不同的语言,除非它专门用于 C 应用程序。
- For example python has a good wav package that reads & writes wav files easily.
- For more professional or academic use, the first go-to is MATLAB which also reads wav files very easily (directly into vectors which are then operated on as single expressions).
- 例如,python 有一个很好的 wav 包,可以轻松读取和写入 wav 文件。
- 对于更专业或学术用途,第一个选择是 MATLAB,它也很容易读取 wav 文件(直接转换为向量,然后作为单个表达式进行操作)。

