Skip to content

Commit 3409b7e

Browse files
authored
More improvements on WAV parser (#246)
* More improvements on WAV parser * Calculate duration based on byte rate * Adjust test * Use sample_rate for determining duration * Adjust test * Bump version
1 parent b71b6f6 commit 3409b7e

4 files changed

Lines changed: 16 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 2.10.0
2+
* Improve WAV parser by focusing on performance rather than on attempting a best-effort when extracting metadata from files that do not strictly follow the format spec.
3+
14
## 2.9.0
25
* Improve WAV parser by performing a best-effort when extracting metadata from files that do not strictly follow the format spec.
36

lib/format_parser/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module FormatParser
2-
VERSION = '2.9.0'
2+
VERSION = '2.10.0'
33
end

lib/parsers/wav_parser.rb

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,27 @@ def call(io)
2121
# The specification does not require the Format chunk to be the first chunk
2222
# after the RIFF header.
2323
# https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
24+
fmt_processed = false
25+
data_processed = false
2426
fmt_data = {}
2527
data_size = 0
26-
total_sample_frames = nil
2728
loop do
2829
chunk_type, chunk_size = safe_read(io, 8).unpack('a4l')
2930
case chunk_type
3031
when 'fmt ' # watch out: the chunk ID of the format chunk ends with a space
3132
fmt_data = unpack_fmt_chunk(io, chunk_size)
33+
fmt_processed = true
3234
when 'data'
3335
data_size = chunk_size
34-
when 'fact'
35-
total_sample_frames = safe_read(io, 4).unpack('l').first
36-
safe_skip(io, chunk_size - 4)
36+
data_processed = true
3737
else
3838
# Skip this chunk until a known chunk is encountered
3939
safe_skip(io, chunk_size)
4040
end
41-
rescue FormatParser::IOUtils::InvalidRead
42-
# We've reached EOF, so it's time to make the most out of the metadata we
43-
# managed to parse
44-
break
41+
break if fmt_processed && data_processed
4542
end
4643

47-
file_info(fmt_data, data_size, total_sample_frames)
44+
file_info(fmt_data, data_size)
4845
end
4946

5047
def unpack_fmt_chunk(io, chunk_size)
@@ -70,10 +67,10 @@ def unpack_fmt_chunk(io, chunk_size)
7067
}
7168
end
7269

73-
def file_info(fmt_data, data_size, sample_frames)
70+
def file_info(fmt_data, data_size)
7471
# NOTE: Each sample includes information for each channel
75-
sample_frames ||= data_size / (fmt_data[:channels] * fmt_data[:bits_per_sample] / 8) if fmt_data[:channels] > 0 && fmt_data[:bits_per_sample] > 0
76-
duration_in_seconds = sample_frames / fmt_data[:sample_rate].to_f if fmt_data[:sample_rate] > 0
72+
sample_frames = data_size / (fmt_data[:channels] * fmt_data[:bits_per_sample] / 8) if fmt_data[:channels] > 0 && fmt_data[:bits_per_sample] > 0
73+
duration_in_seconds = sample_frames / fmt_data[:sample_rate].to_f if sample_frames && fmt_data[:byte_rate] > 0
7774
FormatParser::Audio.new(
7875
format: :wav,
7976
num_audio_channels: fmt_data[:channels],

spec/parsers/wav_parser_spec.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
expect(parse_result.format).to eq(:wav)
2121
expect(parse_result.num_audio_channels).to eq(1)
2222
expect(parse_result.audio_sample_rate_hz).to eq(8000)
23-
expect(parse_result.media_duration_frames).to eq(110488)
24-
expect(parse_result.media_duration_seconds).to be_within(0.01).of(13.81)
23+
# Fixture does not define bits_per_sample in the fmt chunk
24+
expect(parse_result.media_duration_frames).to be_nil
25+
expect(parse_result.media_duration_seconds).to be_nil
2526
end
2627

2728
it 'returns correct info about pcm files with more channels' do

0 commit comments

Comments
 (0)