C语言基础WAV生成程序:非440倍数采样率波形异常排查
问题:生成单音WAV文件时,非频率倍数采样率下波形异常
我正在编写一个生成WAV文件的小型C程序(单音440Hz,采样率44100样本/秒),程序代码如下:
#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <math.h> #include <string.h> int main() { FILE *fp; struct header { char riff[4]; int fsize; char wave[4]; char fmt[4]; int chunk_size; short int format_tag; short int num_channel; int sample_rate; int bytes_per_second; short int bytes_per_sample; short int bits_per_sample; char ddh[4]; int size_dc; } h; char filename[100]; float freq; printf("\n\tEnter a file name..\n\t"); scanf("%s", filename); strcat (filename,".WAV"); printf("\n\n\tEnter sampling rate...\n\t"); scanf("%d", &h.sample_rate); printf("\n\n\tEnter frequency...\n\t"); scanf("%f", &freq); int i, tsec = 10; strcpy(h.riff,"RIFF"); strcpy(h.wave, "WAVE"); strcpy(h.fmt, "fmt "); h.chunk_size = 16; h.format_tag = 1; h.num_channel = 1; h.bits_per_sample = 16; h.bytes_per_sample = h.bits_per_sample * h.num_channel / 8; h.bytes_per_second = h.sample_rate * h.bits_per_sample * h.num_channel/8; strcpy(h.ddh, "data"); h.size_dc = h.bytes_per_second * tsec; h.fsize = h.size_dc + 44; long long int samples; samples = h.sample_rate * tsec; short int audio_data[samples]; for (i=0; i<samples; i++) audio_data[i] = 32760 * sin((2 * M_PI * freq * i)/h.sample_rate); fp = fopen (filename, "w"); fwrite(&h,44,1,fp); printf("%d", sizeof(audio_data)); fwrite(&audio_data,sizeof(audio_data),1,fp); fclose (fp); printf ("\n\tFile creation completed ...."); getch(); return 0; }
该程序在采样率为8800样本/秒或44000样本/秒(均为440的倍数)时运行正常,但当使用44100样本/秒等非440倍数的采样率时,生成的WAV文件波形出现异常。在Audacity中打开这类文件时,波形会突然混乱后又恢复正常,而在Excel中绘制相同正弦函数却显示清晰波形。怀疑可能是数据类型超出限制,恳请指出代码中的错误。
问题分析与修复方案
核心错误点
- 文件打开模式错误:使用
"w"文本模式写入二进制WAV数据,会导致系统自动转换换行符(如Windows下0x0A转为0x0D0A),破坏文件的二进制结构,这是波形异常的主要原因。必须使用二进制写入模式"wb"。 - 栈内存溢出风险:用变长数组
short int audio_data[samples];在栈上分配内存,当采样率44100、时长10秒时,数组大小达882KB,可能超出栈空间上限,引发未定义行为。 - 硬编码头部长度:
fwrite(&h,44,1,fp)硬编码44字节,若结构体因内存对齐变化长度改变,会导致头部写入错误,应改用sizeof(h)。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <math.h> #include <string.h> int main() { FILE *fp; struct header { char riff[4]; int fsize; char wave[4]; char fmt[4]; int chunk_size; short int format_tag; short int num_channel; int sample_rate; int bytes_per_second; short int bytes_per_sample; short int bits_per_sample; char ddh[4]; int size_dc; } h; char filename[100]; float freq; printf("\n\tEnter a file name..\n\t"); scanf("%s", filename); strcat(filename,".WAV"); printf("\n\n\tEnter sampling rate...\n\t"); scanf("%d", &h.sample_rate); printf("\n\n\tEnter frequency...\n\t"); scanf("%f", &freq); int i, tsec = 10; strcpy(h.riff,"RIFF"); strcpy(h.wave, "WAVE"); strcpy(h.fmt, "fmt "); h.chunk_size = 16; h.format_tag = 1; h.num_channel = 1; h.bits_per_sample = 16; h.bytes_per_sample = h.bits_per_sample * h.num_channel / 8; h.bytes_per_second = h.sample_rate * h.bits_per_sample * h.num_channel / 8; strcpy(h.ddh, "data"); h.size_dc = h.bytes_per_second * tsec; h.fsize = h.size_dc + sizeof(h); // 改用sizeof(h)计算总文件大小 long long int samples = (long long)h.sample_rate * tsec; // 用堆内存分配音频数据,避免栈溢出 short int *audio_data = malloc(samples * sizeof(short int)); if (!audio_data) { printf("\n\tMemory allocation failed!"); getch(); return 1; } // 预计算正弦波步长,减少循环内运算量 float step = 2 * M_PI * freq / h.sample_rate; for (i = 0; i < samples; i++) { audio_data[i] = (short)(32760 * sin(step * i)); } // 以二进制模式打开文件 fp = fopen(filename, "wb"); if (!fp) { printf("\n\tFailed to open file!"); free(audio_data); getch(); return 1; } // 写入WAV头部和音频数据 fwrite(&h, sizeof(h), 1, fp); fwrite(audio_data, sizeof(short int), samples, fp); fclose(fp); free(audio_data); // 释放堆内存 printf("\n\tFile creation completed ...."); getch(); return 0; }
关键修复说明
- 二进制模式写入:
"wb"确保所有字节按原始二进制形式写入,不会被文本模式修改,彻底解决波形解析异常问题。 - 堆内存分配:
malloc分配的内存来自堆空间,远大于栈空间,避免大数组导致的溢出问题,同时使用free释放内存,避免泄漏。 - 动态计算头部长度:用
sizeof(h)代替硬编码的44字节,保证结构体长度变化时仍能正确写入头部。 - 优化正弦波计算:预计算
step,减少循环内的浮点运算,提升效率同时避免累积精度误差。
内容的提问来源于stack exchange,提问作者Vinayak Deshmukh
相关产品推荐
相关产品推荐

