c++11wavriff

Converting Bytes read to seconds read


I am reading data from a Riff wav fmt file, I have an array of the data chunk DataBuffer of the wav file, how can I convert the number of bytes read of the data to the number of seconds read from the wav file.

int size_buffer = (Subchunk2Size / (NumOfChan * bitsPerSample / 8));
FILE* WavResult = fopen(FileNom, "rb");
u8* DataBuffer = new u8[size_buffer];
size_t nRead = fread(DataBuffer, sizeof DataBuffer[0], size_buffer, WavResult);

Solution

  • I think you mixed up things a bit. Assuming the field names in the WAV header as described in http://soundfile.sapp.org/doc/WaveFormat :

    ChunkID - "RIFF"
    ChunkSize
        Format - "WAVE"
        Subchunk1ID - "fmt "
        Subchunk1Size
            AudioFormat
            NumChannels
            SampleRate
            ByteRate
            BlockAlign
            BitsPerSample
        Subchunk2ID - "data"
        Subchunk2Size
            data
    

    This line of yours:

    int size_buffer = (Subchunk2Size / (NumOfChan * bitsPerSample / 8));
    

    calculates a number of samples in a single channel. Or a number of blocks, where the block is a structure that contains one sample for each channel. If you use that for allocating memory for bytes from data chunk, then it will be enough only in case of 8-bit mono audio.

    If allocating memory for bytes is really what you want, then simply use Subchunk2Size as the size.

    If you want to allocate memory for samples, then it will differ depending if the audio is 8-bit or 16-bit (I'm ignoring other possibilities). For 8-bit:

    const uint32_t num_of_samples = Subchunk2Size / (BitsPerSample / 8);
    uint8_t *samples = new uint8_t[num_of_samples];
    

    and for 16-bit:

    const uint32_t num_of_samples = Subchunk2Size / (BitsPerSample / 8);
    int16_t *samples = new int16_t[num_of_samples];
    

    Personally, I'd rather use std::vector instead of c-arrays:

    const uint32_t num_of_samples = Subchunk2Size / (BitsPerSample / 8);
    std::vector<int16_t> samples;
    samples.resize(num_of_samples);    // could be done in the constructor, but I am afraid of vector constructors ;-)
    

    I also assume here that the audio is in the most popular encoding (I think), i.e., unsigned for 8-bit and signed for 16-bit. I'm also ignoring the issue of endianness.


    But back to the number of seconds. We can calculate that using the total number of blocks and SampleRate. SampleRate tells us how many samples (in a single channel) there are per second. Or in another words, how many blocks there are per second. So the number of seconds is:

    const double num_of_seconds = 1.0 * num_of_blocks / SampleRate;
    

    You can calculate the number of blocks using the formula from your first line:

    const uint32_t num_of_blocks = Subchunk2Size / (NumChannels * BitsPerSample / 8);
    

    or, as we already have num_of_samples, which is the total number of samples from all channels, we can just divide that by NumChannels:

    const uint32_t num_of_blocks = num_of_samples / NumChannels;
    

    And lastly, in case all you wanted was really just to get the number of seconds from the number of bytes, then there are 2 options. You can calculate the block size:

    const int block_size = NumChannels * BitsPerSample / 8;
    

    which should be essentially the same as BlockAlign, and then divide Subchunk2Size by it, to get the number of blocks, and again by SampleRate to get the number of seconds:

    const double num_of_seconds = 1.0 * Subchunk2Size / block_size / SampleRate;
    // or
    const double num_of_seconds = 1.0 * Subchunk2Size / BlockAlign / SampleRate;
    

    Or you can use ByteRate, which is the number of bytes per second:

    const double num_of_seconds = 1.0 * Subchunk2Size / ByteRate;