jstrait / wavefile

A Ruby gem for reading and writing sound files in Wave format (*.wav)
https://wavefilegem.com
MIT License
209 stars 24 forks source link

UnsupportedFormatError #24

Open donv opened 7 years ago

donv commented 7 years ago

Hi!

Thank you for working on this excellent library!

I am getting an UnsupportedFormatError when reading the included file. Is this truly an unsupported file format, or am I maybe doing something wrong when reading it?

wav.zip

The exception I get does not include a message with any details of the format. That would be nice.

jstrait commented 7 years ago

@donv thanks!

It looks like the error message is correct, and this is a Wave file format that the gem doesn't support. The audio format code is 49, which is not a support format. (Typical PCM files have an audio format code of 1, and IEEE Float files have an audio format code of 3). I'm not sure what format code 49 means - do you know what wave file format this is in, or how the file was created?

If you are interested, check out examples/info.rb. It can be used to get metadata about a Wave file, including whether or not the Wave file can be read by this gem. When I run it on this file, I get:

> ruby examples/info.rb test.wav
Metadata for test.wav:
  Readable by this gem?  No
  Audio Format:          49
  Channels:              1
  Bits per sample:       0
  Samples per second:    8000
  Bytes per second:      1625
  Block align:           65
  Sample frame count:    131
  Play time:             00:00:00:016

Thanks for the suggestion about including more info in the error message about the why a Wave file isn't supported. That seems like a good idea, so I'll think about if there is a possible way to do that in the future.

CromonMS commented 4 years ago

I thought the best place to put this would be here, I am using your library for extracting certain metadata out of WAV files as they are uploaded.

However I've come across a strange one with a wav file that is of standard format, however your info script returns not supported, so I used FFMPEG to get more details:

Guessed Channel Layout for Input Stream #0.0 : stereo
Input #0, wav, from 'original-302dd761f827c3c0e9190b3db8a10823.wav':
  Metadata:
    encoded_by      : Pro Tools
    originator_reference: aayuK39YIhOk
    date            : 2018-11-27
    creation_time   : 12:10:36
    time_reference  : 0
  Duration: 00:04:14.00, bitrate: 1422 kb/s
    Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s16, 1411 kb/s

Could this be that wav's from Pro Tools are not a supported type? Anything we / I can do about this?

Thanks!

CromonMS commented 4 years ago

Just as a follow up I have noticed the mismatch in the bitrates on the Duration line not sure how this can be fixed :s

FYI this is the FFMPEG output of a file that worked fine.

Guessed Channel Layout for Input Stream #0.0 : stereo
Input #0, wav, from 'original-edb729b15a7ce6ad584c4494b9188690.wav':
  Metadata:
    encoded_by      : Logic Pro
    date            : 2014-06-10
    creation_time   : 15:03:09
    time_reference  : 158760000
    coding_history  : 
  Duration: 00:04:34.00, bitrate: 2116 kb/s
    Stream #0:0: Audio: pcm_s24le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s32 (24 bit), 2116 kb/s
At least one output file must be specified
jstrait commented 4 years ago

@CromonMS Thanks for the heads up! Unfortunately it's not clear to me from the output above what the problem with the file is. What is the output from the examples/info.rb script? That might give more info.

CromonMS commented 4 years ago

So I did a bit of messing around, although makes things a little tricky in the app I'm working on and seems like a strange quirk of Pro Tools, sadly I don't work on that DAW so I cannot really delve into that side of it.

Anyways here are some testing results:

So for the first example (Pro Tools File) I got:

$ ruby info.rb original-302dd761f827c3c0e9190b3db8a10823.wav
>> Not a valid Wave file!

The Second example (Logic File):

$ ruby info.rb original-edb729b15a7ce6ad584c4494b9188690.wav
Readable by this gem?  Yes
Audio Format:          1
Channels:              2
Bits per sample:       24
Samples per second:    44100
Bytes per second:      264600
Block align:           6
Sample frame count:    12083400
Speaker mapping:       Not defined
Play time:             00:04:34:000

I then ran a simple ffmpeg encode on the first file

ffmpeg -i original-302dd761f827c3c0e9190b3db8a10823.wav -codec copy 302-reencode.wav

Then I get the expected result

$ ruby info.rb 302-reencode.wav
Readable by this gem?  Yes
Audio Format:          1
Channels:              2
Bits per sample:       16
Samples per second:    44100
Bytes per second:      176400
Block align:           4
Sample frame count:    11201400
Speaker mapping:       Not defined
Play time:             00:04:14:000

The new ffmpeg output supports this too, with matching bitrates as you can see here:

Guessed Channel Layout for Input Stream #0.0 : stereo
Input #0, wav, from '302-reencode.wav':
  Metadata:
    date            : 2018-11-27
    encoder         : Lavf58.20.100
    encoded_by      : Pro Tools
  Duration: 00:04:14.00, bitrate: 1411 kb/s
    Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s16, 1411 kb/s

A very strange edge case indeed I think.

jstrait commented 4 years ago

@CromonMS Behind the scenes in the info.rb script, the Not a valid Wave file! message is caused by InvalidFormatError being raised. This error occurs when the gem thinks a file is not a Wave file for some reason. For example, this error would be raised if you tried to use the gem to read an *.mp3, *.txt, *.jpg, etc. file.

In the repo there's a tools/debug.rb script which displays some of the bytes in a file in a format for debugging purposes. You can use it like ruby tools/debug.rb name_of_file_goes_here. If you can run that script on the file and add the output here, it should help indicate why the gem thinks the file is not a Wave file.

CromonMS commented 4 years ago

Sure thing (Pro Tools file):

$ ruby debug.rb original-302dd761f827c3c0e9190b3db8a10823.wav

Riff Chunk Header
==================================================================================
Chunk ID:              RIFF       | RIFF       | ["R"] ["I"] ["F"] ["F"]
Chunk size:            int_32     | 45163042   | [34] [34] [177] [2]
----------------------------------+------------+----------------------------------
Format code:           WAVE       | WAVE       | ["W"] ["A"] ["V"] ["E"]

'JUNK' chunk of size 92, skipping.

'bext' chunk of size 602, skipping.

Format Chunk
==================================================================================
Chunk ID:              fmt        | fmt        | ["f"] ["m"] ["t"] [" "]
Chunk size:            int_32     | 40         | [40] [0] [0] [0]
----------------------------------+------------+----------------------------------
Audio format:          int_16     | 1          | [1] [0]
Channels:              int_16     | 2          | [2] [0]
Sample rate:           int_32     | 44100      | [68] [172] [0] [0]
Byte rate:             int_32     | 176400     | [16] [177] [2] [0]
Block align:           int_16     | 4          | [4] [0]
Bits per sample:       int_16     | 16         | [16] [0]
Extension size:        int_16     | 0          | [0] [0]

'' chunk of size 0, skipping.

'' chunk of size 0, skipping.

'' chunk of size 1768751104, skipping.

And for comparison (Logic File):

$ ruby debug.rb original-edb729b15a7ce6ad584c4494b9188690.wav

Riff Chunk Header
==================================================================================
Chunk ID:              RIFF       | RIFF       | ["R"] ["I"] ["F"] ["F"]
Chunk size:            int_32     | 72501048   | [56] [71] [82] [4]
----------------------------------+------------+----------------------------------
Format code:           WAVE       | WAVE       | ["W"] ["A"] ["V"] ["E"]

Format Chunk
==================================================================================
Chunk ID:              fmt        | fmt        | ["f"] ["m"] ["t"] [" "]
Chunk size:            int_32     | 16         | [16] [0] [0] [0]
----------------------------------+------------+----------------------------------
Audio format:          int_16     | 1          | [1] [0]
Channels:              int_16     | 2          | [2] [0]
Sample rate:           int_32     | 44100      | [68] [172] [0] [0]
Byte rate:             int_32     | 264600     | [152] [9] [4] [0]
Block align:           int_16     | 6          | [6] [0]
Bits per sample:       int_16     | 24         | [24] [0]
* NO EXTENSION *

Data Chunk
==================================================================================
Chunk ID:              data       | data       | ["d"] ["a"] ["t"] ["a"]
Chunk size:            int_32     | 72500400   | [176] [68] [82] [4]
----------------------------------+------------+----------------------------------
Data Start:            alpha_10   |  | ["\x00"] ["\x00"] ["\x00"] ["\x00"] ["\x00"] ["\x00"] ["\x00"] ["\x00"] ["\x00"] ["\x00"]

'bext' chunk of size 603, skipping.
jstrait commented 4 years ago

@CromonMS Thanks! Based on that I think I see the problem.

The Pro Tools file seems to be a in non-standard format. Specifically, the format chunk seems to be larger than expected. When a format chunk has an audio format code of 1, the chunk body should normally be 16 bytes, not 40 bytes. Instead of ignoring the extra data at the end of the chunk, the gem is raising an exception.

After looking into it and verifying with a test case, I think the gem can be changed to allow reading files like this without raising an error. I opened #33 to track the fix. Thanks for raising this as an issue!