Question

I am writing some silent PCM data on a file @16KBps. This file is of .wav format. For this I have the following code:

#define DEFAULT_BITRATE 16000

long LibGsmManaged:: addSilence ()
{
    char silenceBuf[DEFAULT_BITRATE];

    if (fout) {
        for (int i = 0; i < DEFAULT_BITRATE; i++) {
            silenceBuf[i] = '\0';
        }

        fwrite(silenceBuf, sizeof(silenceBuf), 1, fout);
    }

    return ftell(fout);
}

Updated: Here is how I write the header

void LibGsmManaged::write_wave_header( )
{
    if(fout) {
        fwrite("RIFF", 4, 1, fout);
        total_length_pos = ftell(fout);
        write_int32(0);     
        fwrite("WAVE", 4, 1, fout);
        fwrite("fmt ",4, 1, fout);
        write_int32(16);
        write_int16(1);
        write_int16(1);
        write_int32(8000);
        write_int32(16000);
        write_int16(2);
        write_int16(16);
        fwrite("data",4,1,fout);
        data_length_pos = ftell(fout);
        write_int32(0);
    }
    else {
        std::cout << "File pointer not correctly initialized";
    }
}

void LibGsmManaged::write_int32( int value)
{
    if(fout) {
        fwrite( (const char*)&value, sizeof(value), 1, fout);
    }
    else {
        std::cout << "File pointer not correctly initialized";
    }
}

I run this code on my iOS device using NSTimer with interval 1.0 sec. So AFAIK, if I run this for 60 sec, I should get a file.wav that when played should show 60 sec as its duration (again AFAIK). But in actual test it displays almost double duration i.e. 2 min. (approx). I have also tested that when I change the DEFAULT_BITRATE to 8000, then the file duration is almost correct.

I am unable to identify what is going on here. Am I missing something bad here? I hope my code is not wrong.

Was it helpful?

Solution

What you're trying to do (write your own WAV files) should be totally doable. That's the good news. However, I'm a bit confused about your exact parameters and constraints, as are many others in the comments, which is why they have been trying to flesh out the details.

You want to write raw, uncompressed, silent PCM to a WAV file. Okay. How wide does the PCM data need to be? You are creating an array of chars that you are writing to the file. A char is an 8-bit byte. Is that what you want? If so, then you need to use a silent center point of 0x80 (128). 8-bit PCM in WAV files is unsigned, i.e., 0..255, and 128 is silent.

If you intend to store silent 16-bit data, that will be signed data, so the center point (between -32768 and 32767) is 0. Also, it will be stored in little endian byte format. But since it's silence (all 0s), that doesn't matter.

The title of your question indicates (and the first sentence reiterates) that you want to write data at 16 kbps. Are you sure you want raw 16 kbps audio? That's 16 kiloBITs per second, or 16000 bits per second. Depending on whether you are writing 8- or 16-bit PCM samples, that only allows for 2000 or 1000 Hz audio, which is probably not what you want. Did you mean 16 kHz audio? 16 kHz audio translates to 16000 audio samples per second, which more closely aligns with your code. Then again, your code mentions GSM (LibGsmManaged), so maybe you are looking for 16 kbps audio. But I'll assume we're proceeding along the raw PCM route.

Do you know in advance how many seconds of audio you need to write? That makes this process really easy. As you may have noticed, the WAV header needs length information in a few spots. You either write it in advance (if you know the values) or fill it in later (if you are writing an indeterminate amount).

Let's assume you are writing 2 seconds of raw, monophonic, 16000 Hz, 16-bit PCM to a WAV file. The center point is 0x0000.

WAV writing process:

  1. Write 'RIFF'
  2. Write 32-bit file size, which will be 36 (header size - first 8 bytes) + 64000 (see step 12 about that number)
  3. Write 'WAVEfmt ' (with space)
  4. Write 32-bit format header size (16)
  5. Write 16-bit audio format (1 indicating raw PCM audio)
  6. Write 16-bit channel count (1 because it's monophonic)
  7. Write 32-bit sample rate (number of audio sample per second = 16000)
  8. Write 32-bit byte rate (number of bytes per second = 32000)
  9. Write 16-bit block alignment (2 bytes per sample * 1 channel = 2)
  10. Write 16-bit bits per sample (16)
  11. Write 'data'
  12. Write 32-bit length of audio payload data (16000 samples/second * 2 bytes/sample * 2 seconds = 64000 bytes)
  13. Write 64000 bytes, all 0 values

If you need to write a dynamic amount of audio data, leave the length field from steps 2 and 12 as 0, then seek back after you're done writing and fill those in. I'm not convinced that your original code was writing the length fields correctly. Some playback software might ignore those, others might not, so you could have gotten varying results.

Hope that helps! If you know Python, here's another question I answered which describes how to write a WAV file using Python's struct library (I referred to that code fragment a lot while writing the steps above).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top