Search code examples
fileaudiosdlwavduration

Is it possible to get length (in seconds) of a loaded WAV file in SDL library?


I am trying to find a way to get the duration of an WAV audio file loaded in SDL library.
I am using the latest official build of SDL for Windows, 64-bit, version 2.26.5.

SDL provides following data about the loaded file after a call to LoadWAV function.

SDL_AudioSpec* SDL_LoadWAV(const char*    file,
                           SDL_AudioSpec* spec,
                           Uint8**        audio_buf,
                           Uint32*        audio_len)

Here we have SDL_AudioSpec which I describe below, and audio_len which is simply the file size in bytes.

typedef struct SDL_AudioSpec
{
    int freq;
    SDL_AudioFormat format;
    Uint8 channels;
    Uint8 silence;
    Uint16 samples;
    Uint16 padding;
    Uint32 size;
    SDL_AudioCallback callback;
    void *userdata;
} SDL_AudioSpec;

The file which I load was taken from the system, it is located in C:\Windows\Media folder, so this file must be 100% correct.

After I load my WAV file, I see only two fields of SDL_AudioSpec filled in. These fields are: freq and format. All other fields are zeroed. File size in bytes is filled into another function argument, but it does not mean anything to me while it is set in bytes instead of time units like second of millisecond.

I have searched in documentation of SDL, but I do not see other ways to get the file length in seconds, or simply, file duration in seconds.

Is it really possible to get WAV file duration using only SDL library without any other third-party tools ?

UPDATE

I found the reason for zeroed values. I was using a 16-bit int size instead of 32-bit int. Forgive me, MS-DOS, the world has changed.


Solution

  • The audio_len output parameter is the length of the loaded audio buffer in bytes, not the size of the file. When dealing with audio buffers, SDL generally specifies the size in bytes.

    So the steps to convert to seconds are pretty simple: divide the buffer length by the sample size to get the number of samples across all channels. Then divide by the number of channels to get the length of the audio clip in samples. Then divide that by the frequency to get the length in seconds.

    Untested code:

    double howManySeconds(const char *filename) {
        SDL_AudioSpec spec;
        uint32_t audioLen;
        uint8_t *audioBuf;
        double seconds = 0.0;
    
        if(SDL_LoadWAV(filename, &spec, &audioBuf, &audioLen) != NULL) {
            // we aren't using the actual audio in this example
            SDL_FreeWAV(audioBuf);
            uint32_t sampleSize = SDL_AUDIO_BITSIZE(spec.format) / 8;
            uint32_t sampleCount = audioLen / sampleSize;
            // could do a sanity check and make sure (audioLen % sampleSize) is 0
            uint32_t sampleLen = 0;
            if(spec.channels) {
                sampleLen = sampleCount / spec.channels;
            } else {
                // spec.channels *should* be 1 or higher, but just in case
                sampleLen = sampleCount;
            }
            seconds = (double)sampleLen / (double)audioSpec.freq;
        } else {
            // uh-oh!
            fprintf(stderr, "ERROR: can't load: %s: %s\n", filename, SDL_GetError());
        }
    
        return seconds;
    }