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

如何用C语言编写可被识别的WAV文件?字节序问题排查

问题描述

我尝试用C语言编写WAV文件,但生成的文件无法被Audacity或VLC识别。我了解WAV部分头数据采用大端字节序,但不确定在C语言中写入二进制文件时如何处理,推测这可能是问题根源。当前代码无报错,但生成的WAV文件不被识别。WAV参数:16bit、96kHz、双声道。代码如下:

// Writes data to wav file
FILE *fp = fopen("output/video_signal.wav", "w");
if (fp == NULL)
{
    printf("Output file couldn't be opened!\n");
    return 4;
}
int16_t byte;
uint32_t length = 160+(4800*frame_count);
float multiplier = 32767 / sync_level; // scales signal to use full 16bit resolution

//// WAVE Header Data

unsigned char riff[] = {'R', 'I', 'F', 'F'};
fwrite(&riff, 1, 4, fp);

uint32_t file_size = length + 44;
fwrite(&file_size, 4, 1, fp); // Overall file size

unsigned char wave[] = {'W', 'A', 'V', 'E'};
fwrite(&wave, 1, 4, fp);

unsigned char fmt[] = {'f', 'm', 't'}; // ?
fwrite(&fmt, 1, 4, fp);

uint32_t fmt_length = 16;  // ?
fwrite(&fmt_length, 4, 1, fp);

uint16_t fmt_type = 1; // 1 = PCM
fwrite(&fmt_type, 2, 1, fp);

uint16_t chan_num = 2; // Number of channels
fwrite(&chan_num, 2, 1, fp);

uint32_t rate = 96000; // Sample rate
fwrite(&rate, 4, 1, fp);

uint32_t funny_number = rate * 16 * chan_num; // (Sample Rate * BitsPerSample * Channels) / 8
fwrite(&funny_number, 4, 1, fp);

uint16_t another_format = 4; // 4 = 16bit stereo
fwrite(&another_format, 2, 1, fp);

uint16_t bits = 16; // Bit depth / Bits per sample
fwrite(&bits, 2, 1, fp);

unsigned char data_start[] = {'d', 'a', 't', 'a'}; // Marks the start of the data
fwrite(&data_start, 1, 4, fp);

fwrite(&length, 4, 1, fp); // Data size

for (int i = 0; i < length; i++)
{
    byte = (channel1[i] * multiplier);
    fwrite(&byte, 2, 1, fp);
    byte = (channel2[i] * multiplier);
    fwrite(&byte, 2, 1, fp);
}

fclose(fp);
问题排查与修复方案

你的代码里存在多个导致WAV文件无法被识别的问题,以下是逐一修复的要点:

1. 文件打开模式错误

用"w"模式打开文件是文本模式,写入二进制数据时会自动转换特殊字符(比如Windows下的换行符),破坏WAV的二进制结构。必须改用二进制写入模式:

FILE *fp = fopen("output/video_signal.wav", "wb");

2. 字节序处理错误

WAV文件的所有多字节数值(如文件大小、采样率等)要求是小端字节序,不是大端。当前代码直接写入主机字节序,若你的机器是大端架构,头数据会完全错误。需要用字节序转换函数处理:

  • 16位整数:用htole16()或__builtin_bswap16()(根据编译器调整)
  • 32位整数:用htole32()或__builtin_bswap32()

示例:

uint32_t file_size = htole32(length + 44);
fwrite(&file_size, 4, 1, fp);

3. "fmt"块标识不完整

WAV的格式块标识是{'f','m','t',' '}(最后有一个空格),你定义的数组少了这个空格,导致播放器无法识别格式块:

unsigned char fmt[] = {'f', 'm', 't', ' '};
fwrite(fmt, 1, 4, fp);

4. 字节速率计算错误

你注释里的公式是(Sample Rate * BitsPerSample * Channels) / 8,但代码里漏掉了除以8的步骤,正确计算应为:

uint32_t byte_rate = htole32((rate * bits * chan_num) / 8);
fwrite(&byte_rate, 4, 1, fp);

5. 数据长度与实际写入不匹配

你定义的length是样本数,但WAV的data块长度需要是字节数。双声道16位情况下,每个样本对应4字节(2通道×2字节),所以实际数据字节数应为length * 4,同时文件总大小的计算也要对应调整:

uint32_t sample_count = 160 + (4800 * frame_count);
uint32_t data_bytes = sample_count * 2 * sizeof(int16_t);
uint32_t file_size = htole32(data_bytes + 44);

循环部分也要遍历样本数,而非字节数:

for (int i = 0; i < sample_count; i++)
{
    int16_t sample1 = htole16((int16_t)(channel1[i] * multiplier));
    fwrite(&sample1, 2, 1, fp);
    int16_t sample2 = htole16((int16_t)(channel2[i] * multiplier));
    fwrite(&sample2, 2, 1, fp);
}

修复后的关键代码片段

FILE *fp = fopen("output/video_signal.wav", "wb");
if (fp == NULL) {
    printf("Output file couldn't be opened!\n");
    return 4;
}

uint32_t sample_count = 160 + (4800 * frame_count);
float multiplier = 32767.0f / sync_level;
uint32_t data_bytes = sample_count * 2 * sizeof(int16_t);
uint32_t file_size = htole32(data_bytes + 44);

// RIFF chunk
unsigned char riff[] = {'R','I','F','F'};
fwrite(riff, 1, 4, fp);
fwrite(&file_size, 4, 1, fp);

// WAVE identifier
unsigned char wave[] = {'W','A','V','E'};
fwrite(wave, 1, 4, fp);

// fmt chunk
unsigned char fmt[] = {'f','m','t',' '};
fwrite(fmt, 1, 4, fp);
uint32_t fmt_len = htole32(16);
fwrite(&fmt_len, 4, 1, fp);

uint16_t fmt_type = htole16(1); // PCM
fwrite(&fmt_type, 2, 1, fp);

uint16_t chan_num = htole16(2);
fwrite(&chan_num, 2, 1, fp);

uint32_t rate = htole32(96000);
fwrite(&rate, 4, 1, fp);

uint16_t bits = htole16(16);
uint32_t byte_rate = htole32((96000 * 16 * 2) / 8);
fwrite(&byte_rate, 4, 1, fp);

uint16_t block_align = htole16((16 * 2) / 8);
fwrite(&block_align, 2, 1, fp);
fwrite(&bits, 2, 1, fp);

// data chunk
unsigned char data_start[] = {'d','a','t','a'};
fwrite(data_start, 1, 4, fp);
uint32_t data_len = htole32(data_bytes);
fwrite(&data_len, 4, 1, fp);

// Write samples
for (int i = 0; i < sample_count; i++) {
    int16_t s1 = htole16((int16_t)(channel1[i] * multiplier));
    int16_t s2 = htole16((int16_t)(channel2[i] * multiplier));
    fwrite(&s1, 2, 1, fp);
    fwrite(&s2, 2, 1, fp);
}

fclose(fp);

内容的提问来源于stack exchange,提问作者Jake A

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 12:37:53