You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修复说明

  1. 二进制模式写入:"wb"确保所有字节按原始二进制形式写入,不会被文本模式修改,彻底解决波形解析异常问题。
  2. 堆内存分配:malloc分配的内存来自堆空间,远大于栈空间,避免大数组导致的溢出问题,同时使用free释放内存,避免泄漏。
  3. 动态计算头部长度:用sizeof(h)代替硬编码的44字节,保证结构体长度变化时仍能正确写入头部。
  4. 优化正弦波计算:预计算step,减少循环内的浮点运算,提升效率同时避免累积精度误差。

内容的提问来源于stack exchange,提问作者Vinayak Deshmukh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 20:52:32