Search code examples
caudiowav

WAV file creation - basic C program


I am trying to make a small program for writing a wav file (single note 440Hz, sampling rate 44100 samples / sec). The program is as follows:

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

The above program works well for a frequency of 440Hz and sampling rate of 8800 samples/sec or even 44000 samples/sec, but things get messed up when I use a sampling rate of 44100samples/sec, or any other sampling rate that is not a multiple of 440. When I open the WAV file (created with 440Hz and 44100 samples/sec) in audacity, the vizualisation is as shown in picture below.

enter image description here

I can't figure out why the sine function suddenly gets messed up and then comes back on track again. If I plot the same sine function in excel, the output is a clear clean sine wave as expected. Perhaps I am exceeding the limits of some of the data types used here. Please point out the mistake here. Thank you all in advance.


Solution

  • You need to open the file in binary mode like fopen (filename, "wb") (note the b in "wb". Otherwise on some platforms the file is opened in text mode and LFs (linefeed characters, ASCII code 10) will be replaced by CRLFs (carriage return and linefeed, ASCII code 13 and 10).

    I could reproduce the problem under Windows with Visual Studio 2022. With the "wb" mode in fopen it worked fine with any sampling rate.

    With a sample rate of 44000 the original code worked only by chance because the sequence of bytes in the output didn't contain any LFs.

    You can check if the generated sequence of bytes contains LF bytes by adding this code after the for loop:

      char* b = audio_data;
    
      for (i = 0; i < samples * 2; i++)
      {
        assert(b[i] != 10);
      }
    

    You need to add #include <assert.h> for assert to compile.