1

I'm working with .wav files and I need to get their duration in seconds. So far I've been determining it with:

File size / byte_rate

Byte_rate being (Sample Rate * BitsPerSample * Channels) / 8. And it works, with smaller files, when I try to parse bigger files, I get more seconds than the actual duration.

Example:

Size(bytes): 45207622 Byte_rate: 176400 Duration: 256 (45207622 / 176400)

but the actual duration is 250...

FYI: I've double checked the size and byte_rate, they are correct.

Community
  • 1
  • 1
misticniVrac
  • 11
  • 1
  • 2
  • And in what programming language you need it: [here](https://stackoverflow.com/questions/7833807/get-wav-file-length-or-duration) is a python solution. – YesThatIsMyName Jul 08 '19 at 13:45
  • Thanks for your answer, i use C. in your link it says frames/frame_rate however it is unclear to me what frames and frame_rate are,or how should i get them. – misticniVrac Jul 08 '19 at 14:04

2 Answers2

2

Without a sample RIFF header or your code, it would be difficult to answer the specifics in your question. (i.e. Why your math isn't coming to your expected result.)

However, since you've specified that you're working in C in the comments, might I suggest using the sox library instead of parsing the headers with newly written code? In addition to catching a fair number of edge cases, this allows you to support any format sox supports reading without having to write any of the reading code yourself. (Though anyone inclined to do so should probably take a look at Can someone explain .wav(WAVE) file headers? and RIFF WAVE format specifications. The process should be roughly the method described in the question, at least in most cases. [Edit: That is chunk data length divided by the header's byte rate.])

Example code:

#include <sox.h>
#include <stdio.h>

int main(int argc, char **argv) {
  sox_format_t *fmt;
  if(argc < 2) {
    printf("Please provide audio file.\n");
    return 1;
  }
  fmt = sox_open_read(argv[1], NULL, NULL, NULL);
  __uint64_t ws = fmt->signal.length / fmt->signal.channels;
  if(fmt->signal.length) {
    printf("%0.2f seconds long\n", (double)ws / fmt->signal.rate);
  } else {
    printf("Cannot determine duration from header.\n");
  }
}

For anyone curious, I largely derived this from the sox command line tool's source code.

EPB
  • 3,939
  • 1
  • 24
  • 26
0

Thank you EPR for giving me the fix to timing in my program. I'm not using libsox, I've set up a struct trying to match the original at http://www.lightlink.com/tjweber/StripWav/Canon.html This is NOT the correct way to do it but it works for simple files. Another useful reference is at http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html

Anyway I assume the header is 44 bytes and read() it into memory at the location of the struct. Then I can access fields of the struct, malloc space for the pcm data and read() it into the pcm space from where the file pointer was left. I'm just writing an audiogram program so it needs to be close to correct for the WAV files I generate with arecord, sox, Audacity. Always 2 channels, 44100 sample rate. My struct:

struct wavhdr { // defined by Microsoft, needs to match
  char riff[4];  // should be "RIFF"
  uint32_t len8; // file length - 8
  char wave[4];  // should be "WAVE"
  char fmt[4];   // should be "fmt "
  uint32_t fdatalen; // should be 16 (0x10)
  uint16_t ftag;     // format tag, 1 = pcm
  uint16_t channels; // 2 for stereo
  uint32_t sps;      // samples/sec
  uint32_t srate;    // sample rate in bytes/sec (block align)
  uint16_t chan8;    // channels * bits/sample / 8 
  uint16_t bps;      // bits/sample
  char data[4];      // should be "data"
  uint32_t datlen;   // length of data block
  // pcm data follows this  
} hdr;

I was trying to use the measured file size - header length / samples/sec, that didn't work, I was off by a factor of 6.

Alan Corey
  • 577
  • 6
  • 10