wavefile 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +2 -2
- data/README.markdown +67 -47
- data/Rakefile +23 -0
- data/lib/wavefile.rb +4 -2
- data/lib/wavefile/buffer.rb +40 -25
- data/lib/wavefile/chunk_readers.rb +7 -1
- data/lib/wavefile/chunk_readers/base_chunk_reader.rb +10 -0
- data/lib/wavefile/chunk_readers/data_chunk_reader.rb +77 -0
- data/lib/wavefile/chunk_readers/format_chunk_reader.rb +59 -0
- data/lib/wavefile/chunk_readers/generic_chunk_reader.rb +15 -0
- data/lib/wavefile/chunk_readers/riff_chunk_reader.rb +19 -0
- data/lib/wavefile/chunk_readers/riff_reader.rb +67 -0
- data/lib/wavefile/duration.rb +47 -12
- data/lib/wavefile/format.rb +44 -23
- data/lib/wavefile/reader.rb +101 -111
- data/lib/wavefile/unvalidated_format.rb +36 -6
- data/lib/wavefile/writer.rb +138 -40
- data/test/buffer_test.rb +21 -17
- data/test/chunk_readers/format_chunk_reader_test.rb +130 -0
- data/test/duration_test.rb +42 -1
- data/test/fixtures/actual_output/total_duration_mono_float_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_mono_float_64_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_mono_pcm_16_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_mono_pcm_24_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_mono_pcm_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_mono_pcm_8_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_float_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_float_64_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_pcm_16_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_pcm_24_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_pcm_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_pcm_8_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_float_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_float_64_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_pcm_16_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_pcm_24_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_pcm_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_pcm_8_44100.wav +0 -0
- data/test/fixtures/unsupported/README.markdown +1 -1
- data/test/fixtures/unsupported/bad_channel_count.wav +0 -0
- data/test/fixtures/unsupported/extensible_container_size_bigger_than_sample_size.wav +0 -0
- data/test/fixtures/unsupported/extensible_unsupported_subformat_guid.wav +0 -0
- data/test/fixtures/valid/valid_extensible_mono_float_32_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_mono_float_64_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_mono_pcm_16_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_mono_pcm_24_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_mono_pcm_32_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_mono_pcm_8_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_stereo_float_32_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_stereo_float_64_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_stereo_pcm_16_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_stereo_pcm_24_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_stereo_pcm_32_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_stereo_pcm_8_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_tri_float_32_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_tri_float_64_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_tri_pcm_16_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_tri_pcm_24_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_tri_pcm_32_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_tri_pcm_8_44100.wav +0 -0
- data/test/format_test.rb +22 -48
- data/test/reader_test.rb +188 -93
- data/test/unvalidated_format_test.rb +130 -4
- data/test/wavefile_io_test_helper.rb +6 -4
- data/test/writer_test.rb +118 -95
- metadata +47 -4
- data/lib/wavefile/chunk_readers/header_reader.rb +0 -163
- data/test/fixtures/actual_output/no_samples.wav +0 -0
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wavefile
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Strait
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: You can use this gem to create Ruby programs that work with audio, by
|
14
14
|
reading and writing Wave sound files (*.wav). Since it is written in pure Ruby (as
|
@@ -25,15 +25,38 @@ files:
|
|
25
25
|
- lib/wavefile.rb
|
26
26
|
- lib/wavefile/buffer.rb
|
27
27
|
- lib/wavefile/chunk_readers.rb
|
28
|
-
- lib/wavefile/chunk_readers/
|
28
|
+
- lib/wavefile/chunk_readers/base_chunk_reader.rb
|
29
|
+
- lib/wavefile/chunk_readers/data_chunk_reader.rb
|
30
|
+
- lib/wavefile/chunk_readers/format_chunk_reader.rb
|
31
|
+
- lib/wavefile/chunk_readers/generic_chunk_reader.rb
|
32
|
+
- lib/wavefile/chunk_readers/riff_chunk_reader.rb
|
33
|
+
- lib/wavefile/chunk_readers/riff_reader.rb
|
29
34
|
- lib/wavefile/duration.rb
|
30
35
|
- lib/wavefile/format.rb
|
31
36
|
- lib/wavefile/reader.rb
|
32
37
|
- lib/wavefile/unvalidated_format.rb
|
33
38
|
- lib/wavefile/writer.rb
|
34
39
|
- test/buffer_test.rb
|
40
|
+
- test/chunk_readers/format_chunk_reader_test.rb
|
35
41
|
- test/duration_test.rb
|
36
|
-
- test/fixtures/actual_output/
|
42
|
+
- test/fixtures/actual_output/total_duration_mono_float_32_44100.wav
|
43
|
+
- test/fixtures/actual_output/total_duration_mono_float_64_44100.wav
|
44
|
+
- test/fixtures/actual_output/total_duration_mono_pcm_16_44100.wav
|
45
|
+
- test/fixtures/actual_output/total_duration_mono_pcm_24_44100.wav
|
46
|
+
- test/fixtures/actual_output/total_duration_mono_pcm_32_44100.wav
|
47
|
+
- test/fixtures/actual_output/total_duration_mono_pcm_8_44100.wav
|
48
|
+
- test/fixtures/actual_output/total_duration_stereo_float_32_44100.wav
|
49
|
+
- test/fixtures/actual_output/total_duration_stereo_float_64_44100.wav
|
50
|
+
- test/fixtures/actual_output/total_duration_stereo_pcm_16_44100.wav
|
51
|
+
- test/fixtures/actual_output/total_duration_stereo_pcm_24_44100.wav
|
52
|
+
- test/fixtures/actual_output/total_duration_stereo_pcm_32_44100.wav
|
53
|
+
- test/fixtures/actual_output/total_duration_stereo_pcm_8_44100.wav
|
54
|
+
- test/fixtures/actual_output/total_duration_tri_float_32_44100.wav
|
55
|
+
- test/fixtures/actual_output/total_duration_tri_float_64_44100.wav
|
56
|
+
- test/fixtures/actual_output/total_duration_tri_pcm_16_44100.wav
|
57
|
+
- test/fixtures/actual_output/total_duration_tri_pcm_24_44100.wav
|
58
|
+
- test/fixtures/actual_output/total_duration_tri_pcm_32_44100.wav
|
59
|
+
- test/fixtures/actual_output/total_duration_tri_pcm_8_44100.wav
|
37
60
|
- test/fixtures/invalid/README.markdown
|
38
61
|
- test/fixtures/invalid/bad_riff_header.wav
|
39
62
|
- test/fixtures/invalid/bad_wavefile_format.wav
|
@@ -47,10 +70,30 @@ files:
|
|
47
70
|
- test/fixtures/unsupported/bad_audio_format.wav
|
48
71
|
- test/fixtures/unsupported/bad_channel_count.wav
|
49
72
|
- test/fixtures/unsupported/bad_sample_rate.wav
|
73
|
+
- test/fixtures/unsupported/extensible_container_size_bigger_than_sample_size.wav
|
74
|
+
- test/fixtures/unsupported/extensible_unsupported_subformat_guid.wav
|
50
75
|
- test/fixtures/unsupported/unsupported_audio_format.wav
|
51
76
|
- test/fixtures/unsupported/unsupported_bits_per_sample.wav
|
52
77
|
- test/fixtures/valid/README.markdown
|
53
78
|
- test/fixtures/valid/no_samples.wav
|
79
|
+
- test/fixtures/valid/valid_extensible_mono_float_32_44100.wav
|
80
|
+
- test/fixtures/valid/valid_extensible_mono_float_64_44100.wav
|
81
|
+
- test/fixtures/valid/valid_extensible_mono_pcm_16_44100.wav
|
82
|
+
- test/fixtures/valid/valid_extensible_mono_pcm_24_44100.wav
|
83
|
+
- test/fixtures/valid/valid_extensible_mono_pcm_32_44100.wav
|
84
|
+
- test/fixtures/valid/valid_extensible_mono_pcm_8_44100.wav
|
85
|
+
- test/fixtures/valid/valid_extensible_stereo_float_32_44100.wav
|
86
|
+
- test/fixtures/valid/valid_extensible_stereo_float_64_44100.wav
|
87
|
+
- test/fixtures/valid/valid_extensible_stereo_pcm_16_44100.wav
|
88
|
+
- test/fixtures/valid/valid_extensible_stereo_pcm_24_44100.wav
|
89
|
+
- test/fixtures/valid/valid_extensible_stereo_pcm_32_44100.wav
|
90
|
+
- test/fixtures/valid/valid_extensible_stereo_pcm_8_44100.wav
|
91
|
+
- test/fixtures/valid/valid_extensible_tri_float_32_44100.wav
|
92
|
+
- test/fixtures/valid/valid_extensible_tri_float_64_44100.wav
|
93
|
+
- test/fixtures/valid/valid_extensible_tri_pcm_16_44100.wav
|
94
|
+
- test/fixtures/valid/valid_extensible_tri_pcm_24_44100.wav
|
95
|
+
- test/fixtures/valid/valid_extensible_tri_pcm_32_44100.wav
|
96
|
+
- test/fixtures/valid/valid_extensible_tri_pcm_8_44100.wav
|
54
97
|
- test/fixtures/valid/valid_mono_float_32_44100.wav
|
55
98
|
- test/fixtures/valid/valid_mono_float_64_44100.wav
|
56
99
|
- test/fixtures/valid/valid_mono_pcm_16_44100.wav
|
@@ -1,163 +0,0 @@
|
|
1
|
-
module WaveFile
|
2
|
-
module ChunkReaders
|
3
|
-
# Used to read the RIFF chunks in a wave file up until the data chunk. Thus is can be used
|
4
|
-
# to open a wave file and "queue it up" to the start of the actual sample data, as well as
|
5
|
-
# extract information out of pre-data chunks, such as the format chunk.
|
6
|
-
class RiffReader # :nodoc:
|
7
|
-
def initialize(file, file_name)
|
8
|
-
@file = file
|
9
|
-
@file_name = file_name
|
10
|
-
|
11
|
-
read_until_data_chunk
|
12
|
-
end
|
13
|
-
|
14
|
-
attr_reader :native_format, :data_chunk_reader
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def read_until_data_chunk
|
19
|
-
begin
|
20
|
-
chunk_id = @file.sysread(4)
|
21
|
-
unless chunk_id == CHUNK_IDS[:riff]
|
22
|
-
raise_error InvalidFormatError, "Expected chunk ID '#{CHUNK_IDS[:riff]}', but was '#{chunk_id}'"
|
23
|
-
end
|
24
|
-
RiffChunkReader.new(@file).read
|
25
|
-
|
26
|
-
chunk_id = @file.sysread(4)
|
27
|
-
while chunk_id != CHUNK_IDS[:data]
|
28
|
-
if chunk_id == CHUNK_IDS[:format]
|
29
|
-
@native_format = FormatChunkReader.new(@file).read
|
30
|
-
else
|
31
|
-
# Other chunk types besides the format chunk are ignored. This may change in the future.
|
32
|
-
GenericChunkReader.new(@file).read
|
33
|
-
end
|
34
|
-
|
35
|
-
chunk_id = @file.sysread(4)
|
36
|
-
end
|
37
|
-
rescue EOFError
|
38
|
-
raise_error InvalidFormatError, "It doesn't have a data chunk."
|
39
|
-
end
|
40
|
-
|
41
|
-
if @native_format == nil
|
42
|
-
raise_error InvalidFormatError, "The format chunk is either missing, or it comes after the data chunk."
|
43
|
-
end
|
44
|
-
|
45
|
-
@data_chunk_reader = DataChunkReader.new(@file, @native_format)
|
46
|
-
end
|
47
|
-
|
48
|
-
class BaseChunkReader # :nodoc:
|
49
|
-
def read_chunk_size
|
50
|
-
chunk_size = @file.sysread(4).unpack(UNSIGNED_INT_32).first || 0
|
51
|
-
|
52
|
-
# The RIFF specification requires that each chunk be aligned to an even number of bytes,
|
53
|
-
# even if the byte count is an odd number.
|
54
|
-
#
|
55
|
-
# See http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf, page 11.
|
56
|
-
if chunk_size.odd?
|
57
|
-
chunk_size += 1
|
58
|
-
end
|
59
|
-
|
60
|
-
chunk_size
|
61
|
-
end
|
62
|
-
|
63
|
-
def raise_error(exception_class, message)
|
64
|
-
raise exception_class, "File '#{@file_name}' is not a supported wave file. #{message}"
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
class GenericChunkReader < BaseChunkReader # :nodoc:
|
69
|
-
def initialize(file)
|
70
|
-
@file = file
|
71
|
-
end
|
72
|
-
|
73
|
-
def read
|
74
|
-
chunk_size = read_chunk_size
|
75
|
-
@file.sysread(chunk_size)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
class RiffChunkReader < BaseChunkReader # :nodoc:
|
80
|
-
def initialize(file)
|
81
|
-
@file = file
|
82
|
-
end
|
83
|
-
|
84
|
-
def read
|
85
|
-
chunk_size = read_chunk_size
|
86
|
-
riff_format = @file.sysread(4)
|
87
|
-
|
88
|
-
unless riff_format == WAVEFILE_FORMAT_CODE
|
89
|
-
raise_error InvalidFormatError, "Expected RIFF format of '#{WAVEFILE_FORMAT_CODE}', but was '#{riff_format}'"
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
class DataChunkReader < BaseChunkReader # :nodoc:
|
95
|
-
def initialize(file, native_format)
|
96
|
-
@file = file
|
97
|
-
@native_format = native_format
|
98
|
-
|
99
|
-
data_chunk_size = @file.sysread(4).unpack(UNSIGNED_INT_32).first
|
100
|
-
@sample_frame_count = data_chunk_size / @native_format.block_align
|
101
|
-
end
|
102
|
-
|
103
|
-
attr_reader :sample_frame_count
|
104
|
-
end
|
105
|
-
|
106
|
-
class FormatChunkReader < BaseChunkReader # :nodoc:
|
107
|
-
def initialize(file)
|
108
|
-
@file = file
|
109
|
-
end
|
110
|
-
|
111
|
-
def read
|
112
|
-
chunk_size = read_chunk_size
|
113
|
-
|
114
|
-
if chunk_size < MINIMUM_CHUNK_SIZE
|
115
|
-
raise_error InvalidFormatError, "The format chunk is incomplete."
|
116
|
-
end
|
117
|
-
|
118
|
-
raw_bytes = read_chunk_body(CHUNK_IDS[:format], chunk_size)
|
119
|
-
|
120
|
-
format_chunk = {}
|
121
|
-
format_chunk[:audio_format],
|
122
|
-
format_chunk[:channels],
|
123
|
-
format_chunk[:sample_rate],
|
124
|
-
format_chunk[:byte_rate],
|
125
|
-
format_chunk[:block_align],
|
126
|
-
format_chunk[:bits_per_sample] = raw_bytes.slice!(0...MINIMUM_CHUNK_SIZE).unpack("vvVVvv")
|
127
|
-
|
128
|
-
if chunk_size > MINIMUM_CHUNK_SIZE
|
129
|
-
format_chunk[:extension_size] = raw_bytes.slice!(0...2).unpack(UNSIGNED_INT_16).first
|
130
|
-
|
131
|
-
if format_chunk[:extension_size] == nil
|
132
|
-
raise_error InvalidFormatError, "The format chunk is missing an expected extension."
|
133
|
-
end
|
134
|
-
|
135
|
-
if format_chunk[:extension_size] != raw_bytes.length
|
136
|
-
raise_error InvalidFormatError, "The format chunk extension is shorter than expected."
|
137
|
-
end
|
138
|
-
|
139
|
-
# TODO: Parse the extension
|
140
|
-
end
|
141
|
-
|
142
|
-
UnvalidatedFormat.new(format_chunk)
|
143
|
-
end
|
144
|
-
|
145
|
-
private
|
146
|
-
|
147
|
-
MINIMUM_CHUNK_SIZE = 16
|
148
|
-
|
149
|
-
def read_chunk_body(chunk_id, chunk_size)
|
150
|
-
begin
|
151
|
-
return @file.sysread(chunk_size)
|
152
|
-
rescue EOFError
|
153
|
-
raise_error InvalidFormatError, "The #{chunk_id} chunk has incomplete data."
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def raise_error(exception_class, message)
|
159
|
-
raise exception_class, "File '#{@file_name}' is not a supported wave file. #{message}"
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
Binary file
|