我如何在C中编写一个wav文件?

huangapple go评论60阅读模式
英文:

How do I write a wav file in C?

问题

我试图在C中编写一个wav文件,但是audacity或者vlc无法识别该文件。

我知道一些头部数据是大端序的,但我不确定在C中编写二进制文件时如何确定这一点。我认为这可能是问题所在。

我参考了这些网站:
https://www.videoproc.com/resource/wav-file.htm
https://docs.fileformat.com/audio/wav/

当前代码没有任何错误,但创建的wav文件无法识别。

wav规格:
16位,
96kHz,
2通道

英文:

I'm trying to write a wav file in C but the file is not recognized by audacity or vlc.

I know that some of the header data is big endian and I'm not sure how to determine that when writing a binary file in C. I think this is probably the issue.

I'm going off of these sites:
https://www.videoproc.com/resource/wav-file.htm,
https://docs.fileformat.com/audio/wav/

The current code doesn't have any errors but the wav file it creates isn't recognized.

wav specs:
16bit,
96kHz,
2 channel

// 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);

答案1

得分: 2

这是您的代码的修改版本,用于生成一秒钟的白噪声:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main()
{
    uint32_t duration = 1;
    uint32_t rate = 96000;  // 采样率
    uint32_t frame_count = duration * rate;
    uint16_t chan_num = 2;  // 通道数
    uint16_t bits = 16;  // 位深度
    uint32_t length = frame_count * chan_num * bits / 8;
    int16_t byte;
    float sync_level = 1.0;
    // 将信号缩放以使用完整的16位分辨率
    float multiplier = 32767 / sync_level;

    float *channel1 = (float *) malloc(frame_count * sizeof(float));
    float *channel2 = (float *) malloc(frame_count * sizeof(float));
    for (uint32_t i=0; i < frame_count; i++) {
        channel1[i] = (float) (rand() - RAND_MAX / 2) / RAND_MAX;
        channel2[i] = (float) (rand() - RAND_MAX / 2) / RAND_MAX;
    }

    // 将数据写入WAV文件
    FILE *fp = fopen("video_signal.wav", "w");
    if (fp == NULL)
    {
        printf("无法打开输出文件!\n");
        return 4;
    }

    //// WAVE头数据
    fwrite("RIFF", 1, 4, fp);
    uint32_t chunk_size = length + 44 - 8;
    fwrite(&chunk_size, 4, 1, fp);
    fwrite("WAVE", 1, 4, fp);
    fwrite("fmt ", 1, 4, fp);  // 注意此处末尾有空格
    uint32_t subchunk1_size = 16;
    fwrite(&subchunk1_size, 4, 1, fp);
    uint16_t fmt_type = 1;  // 1 = PCM
    fwrite(&fmt_type, 2, 1, fp);
    fwrite(&chan_num, 2, 1, fp);
    fwrite(&rate, 4, 1, fp);
    // (采样率 * 每样本位数 * 通道数) / 8
    uint32_t byte_rate = rate * bits * chan_num / 8;
    fwrite(&byte_rate, 4, 1, fp);
    uint16_t block_align = chan_num * bits / 8;
    fwrite(&block_align, 2, 1, fp);
    fwrite(&bits, 2, 1, fp);

    // 标记数据的开始
    fwrite("data", 1, 4, fp);
    fwrite(&length, 4, 1, fp);  // 数据大小
    for (uint32_t i = 0; i < frame_count; i++)
    {
        byte = (channel1[i] * multiplier);
        fwrite(&byte, 2, 1, fp);
        byte = (channel2[i] * multiplier);
        fwrite(&byte, 2, 1, fp);
    }

    fclose(fp);
    free(channel1);
    free(channel2);
    return 0;
}

您的代码中存在一些问题:

  • 奇怪的数字没有除以8
  • "fmt "标记末尾的空格缺失
  • 长度计算与数据大小不符合
英文:

Here is a modified version of your code that generates one second of white noise:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdint.h&gt;

int main()
{
    uint32_t duration = 1;
    uint32_t rate = 96000;  // Sample rate
    uint32_t frame_count = duration * rate;
    uint16_t chan_num = 2;  // Number of channels
    uint16_t bits = 16;  // Bit depth
    uint32_t length = frame_count*chan_num*bits / 8;
    int16_t byte;
    float sync_level = 1.0;
    // scales signal to use full 16bit resolution
    float multiplier = 32767 / sync_level;


    float *channel1 = (float *) malloc(frame_count * sizeof(float));
    float *channel2 = (float *) malloc(frame_count * sizeof(float));
    for (uint32_t i=0; i &lt; frame_count; i++) {
        channel1[i] = (float) (rand() - RAND_MAX / 2) / RAND_MAX;
        channel2[i] = (float) (rand() - RAND_MAX / 2) / RAND_MAX;
    }

    // Writes data to wav file
    FILE *fp = fopen(&quot;video_signal.wav&quot;, &quot;w&quot;);
    if (fp == NULL)
    {
        printf(&quot;Output file couldn&#39;t be opened!\n&quot;);
        return 4;
    }

    //// WAVE Header Data
    fwrite(&quot;RIFF&quot;, 1, 4, fp);
    uint32_t chunk_size = length + 44 - 8;
    fwrite(&amp;chunk_size, 4, 1, fp);
    fwrite(&quot;WAVE&quot;, 1, 4, fp);
    fwrite(&quot;fmt &quot;, 1, 4, fp);
    uint32_t subchunk1_size = 16;
    fwrite(&amp;subchunk1_size, 4, 1, fp);
    uint16_t fmt_type = 1;  // 1 = PCM
    fwrite(&amp;fmt_type, 2, 1, fp);
    fwrite(&amp;chan_num, 2, 1, fp);
    fwrite(&amp;rate, 4, 1, fp);
    // (Sample Rate * BitsPerSample * Channels) / 8
    uint32_t byte_rate = rate * bits * chan_num / 8;
    fwrite(&amp;byte_rate, 4, 1, fp);
    uint16_t block_align = chan_num * bits / 8;
    fwrite(&amp;block_align, 2, 1, fp);
    fwrite(&amp;bits, 2, 1, fp);

    // Marks the start of the data
    fwrite(&quot;data&quot;, 1, 4, fp);
    fwrite(&amp;length, 4, 1, fp);  // Data size
    for (uint32_t i = 0; i &lt; frame_count; i++)
    {
        byte = (channel1[i] * multiplier);
        fwrite(&amp;byte, 2, 1, fp);
        byte = (channel2[i] * multiplier);
        fwrite(&amp;byte, 2, 1, fp);
    }

    fclose(fp);
    free(channel1);
    free(channel2);
    return 0;
}

There were a few problems in your code:

  • the funny number was missing dividing by 8
  • the "fmt " mark was missing the space at the end
  • the length calculation didn't correspond to the data size

huangapple
  • 本文由 发表于 2023年4月20日 09:49:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76059971.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定