wavefile 0.5.0 → 0.6.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.
@@ -1,20 +1,33 @@
1
1
  module WaveFile
2
- # Error that is raised when trying to read from a file that is either not a wave file,
2
+ # Error that is raised when trying to read from a file that is either not a wave file,
3
3
  # or that is not valid according to the wave file spec.
4
4
  class InvalidFormatError < StandardError; end
5
5
 
6
- # Error that is raised when trying to read from a valid wave file that has its sample data
6
+ # Error that is raised when trying to read from a valid wave file that has its sample data
7
7
  # stored in a format that Reader doesn't understand.
8
8
  class UnsupportedFormatError < StandardError; end
9
9
 
10
10
 
11
- # Provides the ability to read sample data out of a wave file, as well as query a
11
+ # Provides the ability to read sample data out of a wave file, as well as query a
12
12
  # wave file about its metadata (e.g. number of channels, sample rate, etc).
13
+ #
14
+ # When constructing a Reader a block can be given. All data should be read inside this
15
+ # block, and when the block exits the Reader will automatically be closed.
16
+ #
17
+ # Reader.new("my_file.wav") do |reader|
18
+ # # Read sample data here
19
+ # end
20
+ #
21
+ # Alternately, if a block isn't given you should make sure to call close when finished reading.
22
+ #
23
+ # reader = Reader.new("my_file.wav")
24
+ # # Read sample data here
25
+ # reader.close
13
26
  class Reader
14
- # Returns a Reader object that is ready to start reading the specified file's sample data.
27
+ # Returns a Reader object that is ready to start reading the specified file's sample data.
15
28
  #
16
29
  # file_name - The name of the wave file to read from.
17
- # format - The format that read sample data should be returned in
30
+ # format - The format that read sample data should be returned in
18
31
  # (default: the wave file's internal format).
19
32
  #
20
33
  # Returns a Reader object that is ready to start reading the specified file's sample data.
@@ -50,9 +63,9 @@ module WaveFile
50
63
  end
51
64
 
52
65
 
53
- # Reads metadata from the specified wave file and returns an Info object with the results.
54
- # Metadata includes things like the number of channels, bits per sample, number of sample
55
- # frames, sample encoding format (i.e. PCM, IEEE float, uLaw etc). See the Info object for
66
+ # Reads metadata from the specified wave file and returns an Info object with the results.
67
+ # Metadata includes things like the number of channels, bits per sample, number of sample
68
+ # frames, sample encoding format (i.e. PCM, IEEE float, uLaw etc). See the Info object for
56
69
  # more detail on exactly what metadata is available.
57
70
  #
58
71
  # file_name - The name of the wave file to read from
@@ -74,17 +87,17 @@ module WaveFile
74
87
  end
75
88
 
76
89
 
77
- # Reads sample data of the into successive Buffers of the specified size, until there is no more
78
- # sample data to be read. When all sample data has been read, the Reader is automatically closed.
90
+ # Reads sample data of the into successive Buffers of the specified size, until there is no more
91
+ # sample data to be read. When all sample data has been read, the Reader is automatically closed.
79
92
  # Each Buffer is passed to the given block.
80
93
  #
81
- # Note that sample_frame_count indicates the number of sample frames to read, not number of samples.
82
- # A sample frame include one sample for each channel. For example, if sample_frame_count is 1024, then
83
- # for a stereo file 1024 samples will be read from the left channel, and 1024 samples will be read from
94
+ # Note that sample_frame_count indicates the number of sample frames to read, not number of samples.
95
+ # A sample frame include one sample for each channel. For example, if sample_frame_count is 1024, then
96
+ # for a stereo file 1024 samples will be read from the left channel, and 1024 samples will be read from
84
97
  # the right channel.
85
98
  #
86
- # sample_frame_count - The number of sample frames to read into each Buffer from each channel. The number
87
- # of sample frames read into the final Buffer could be less than this size, if there
99
+ # sample_frame_count - The number of sample frames to read into each Buffer from each channel. The number
100
+ # of sample frames read into the final Buffer could be less than this size, if there
88
101
  # are not enough remaining.
89
102
  #
90
103
  # Returns nothing.
@@ -99,10 +112,10 @@ module WaveFile
99
112
  end
100
113
 
101
114
 
102
- # Reads the specified number of sample frames from the wave file into a Buffer. Note that the Buffer will have
115
+ # Reads the specified number of sample frames from the wave file into a Buffer. Note that the Buffer will have
103
116
  # at most sample_frame_count sample frames, but could have less if the file doesn't have enough remaining.
104
117
  #
105
- # sample_frame_count - The number of sample frames to read. Note that each sample frame includes a sample for
118
+ # sample_frame_count - The number of sample frames to read. Note that each sample frame includes a sample for
106
119
  # each channel.
107
120
  #
108
121
  # Returns a Buffer containing sample_frame_count sample frames
@@ -118,24 +131,18 @@ module WaveFile
118
131
  samples = @file.sysread(sample_frame_count * @native_format.block_align).unpack(@pack_code)
119
132
  @current_sample_frame += sample_frame_count
120
133
 
121
- if @native_format.channels > 1
122
- num_multichannel_samples = samples.length / @native_format.channels
123
- multichannel_data = Array.new(num_multichannel_samples)
124
-
125
- if(@native_format.channels == 2)
126
- # Files with more than 2 channels are expected to be less common, so if there are 2 channels
127
- # using a faster specific algorithm instead of a general one.
128
- num_multichannel_samples.times {|i| multichannel_data[i] = [samples.pop, samples.pop].reverse! }
129
- else
130
- # General algorithm that works for any number of channels, 2 or greater.
131
- num_multichannel_samples.times do |i|
132
- sample = Array.new(@native_format.channels)
133
- @native_format.channels.times {|j| sample[j] = samples.pop }
134
- multichannel_data[i] = sample.reverse!
135
- end
136
- end
134
+ if @native_format.bits_per_sample == 24
135
+ # Since the sample data is little endian, the 3 bytes will go from least->most significant
136
+ samples = samples.each_slice(3).map {|least_significant_byte, middle_byte, most_significant_byte|
137
+ # Convert the byte read as "C" to one read as "c"
138
+ most_significant_byte = [most_significant_byte].pack("c").unpack("c").first
139
+
140
+ (most_significant_byte << 16) | (middle_byte << 8) | least_significant_byte
141
+ }
142
+ end
137
143
 
138
- samples = multichannel_data.reverse!
144
+ if @native_format.channels > 1
145
+ samples = samples.each_slice(@native_format.channels).to_a
139
146
  end
140
147
 
141
148
  buffer = Buffer.new(samples, @native_format)
@@ -165,19 +172,19 @@ module WaveFile
165
172
  # Returns the name of the Wave file that is being read
166
173
  attr_reader :file_name
167
174
 
168
- # Returns a Format object describing how sample data is being read from the Wave file (number of
169
- # channels, sample format and bits per sample, etc). Note that this might be different from the
175
+ # Returns a Format object describing how sample data is being read from the Wave file (number of
176
+ # channels, sample format and bits per sample, etc). Note that this might be different from the
170
177
  # underlying format of the Wave file on disk.
171
178
  attr_reader :format
172
179
 
173
- # Returns the index of the sample frame which is "cued up" for reading. I.e., the index
174
- # of the next sample frame that will be read. A sample frame contains a single sample
175
- # for each channel. So if there are 1,000 sample frames in a stereo file, this means
180
+ # Returns the index of the sample frame which is "cued up" for reading. I.e., the index
181
+ # of the next sample frame that will be read. A sample frame contains a single sample
182
+ # for each channel. So if there are 1,000 sample frames in a stereo file, this means
176
183
  # there are 1,000 left-channel samples and 1,000 right-channel samples.
177
184
  attr_reader :current_sample_frame
178
185
 
179
- # Returns the total number of sample frames in the file. A sample frame contains a single
180
- # sample for each channel. So if there are 1,000 sample frames in a stereo file, this means
186
+ # Returns the total number of sample frames in the file. A sample frame contains a single
187
+ # sample for each channel. So if there are 1,000 sample frames in a stereo file, this means
181
188
  # there are 1,000 left-channel samples and 1,000 right-channel samples.
182
189
  attr_reader :total_sample_frames
183
190
 
@@ -218,7 +225,7 @@ module WaveFile
218
225
  # Used to read the RIFF chunks in a wave file up until the data chunk. Thus is can be used
219
226
  # to open a wave file and "queue it up" to the start of the actual sample data, as well as
220
227
  # extract information out of pre-data chunks, such as the format chunk.
221
- class HeaderReader
228
+ class HeaderReader # :nodoc:
222
229
  RIFF_CHUNK_HEADER_SIZE = 12
223
230
  FORMAT_CHUNK_MINIMUM_SIZE = 16
224
231
 
@@ -237,6 +244,14 @@ module WaveFile
237
244
  if chunk_id == CHUNK_IDS[:format]
238
245
  format_chunk = read_format_chunk(chunk_id, chunk_size)
239
246
  else
247
+ # The RIFF specification requires that each chunk be aligned to an even number of bytes,
248
+ # even if the byte count is an odd number.
249
+ #
250
+ # See http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf, page 11.
251
+ if chunk_size.odd?
252
+ chunk_size += 1
253
+ end
254
+
240
255
  # Other chunk types besides the format chunk are ignored. This may change in the future.
241
256
  @file.sysread(chunk_size)
242
257
  end
@@ -1,15 +1,29 @@
1
1
  module WaveFile
2
2
  # Provides the ability to write data to a wave file.
3
+ #
4
+ # When a Writer is constructed it can be given a block. All samples should be written inside this
5
+ # block, and when the block exits the file will automatically be closed:
6
+ #
7
+ # Writer.new("my_file.wav", Format.new(:mono, :pcm_16, 44100)) do |writer|
8
+ # # Write sample data here
9
+ # end
10
+ #
11
+ # If no block is given, you'll need to manually close the Writer when done. The underlaying
12
+ # file will not be valid or playable until close is called.
13
+ #
14
+ # writer = Writer.new("my_file.wav", Format.new(:mono, :pcm_16, 44100))
15
+ # # Write sample data here
16
+ # writer.close
3
17
  class Writer
4
18
 
5
- # Padding value written to the end of chunks whose payload is an odd number of bytes. The RIFF
6
- # specification requires that each chunk be aligned to an even number of bytes, even if the byte
19
+ # Padding value written to the end of chunks whose payload is an odd number of bytes. The RIFF
20
+ # specification requires that each chunk be aligned to an even number of bytes, even if the byte
7
21
  # count is an odd number.
8
22
  #
9
23
  # See http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf, page 11.
10
- EMPTY_BYTE = "\000"
24
+ EMPTY_BYTE = "\000" # :nodoc:
11
25
 
12
- # The number of bytes at the beginning of a wave file before the sample data in the data chunk
26
+ # The number of bytes at the beginning of a wave file before the sample data in the data chunk
13
27
  # starts, assuming this canonical format:
14
28
  #
15
29
  # RIFF Chunk Header (12 bytes)
@@ -18,16 +32,16 @@ module WaveFile
18
32
  # Data Chunk Header (8 bytes)
19
33
  #
20
34
  # All wave files written by Writer use this canonical format.
21
- CANONICAL_HEADER_BYTE_LENGTH = {:pcm => 36, :float => 50}
35
+ CANONICAL_HEADER_BYTE_LENGTH = {:pcm => 36, :float => 50} # :nodoc:
22
36
 
23
37
 
24
- # Returns a constructed Writer object which is available for writing sample data to the specified
25
- # file (via the write method). When all sample data has been written, the Writer should be closed.
26
- # Note that the wave file being written to will NOT be valid (and playable in other programs) until
38
+ # Returns a constructed Writer object which is available for writing sample data to the specified
39
+ # file (via the write method). When all sample data has been written, the Writer should be closed.
40
+ # Note that the wave file being written to will NOT be valid (and playable in other programs) until
27
41
  # the Writer has been closed.
28
42
  #
29
- # If a block is given to this method, sample data can be written inside the given block. When the
30
- # block terminates, the Writer will be automatically closed (and no more sample data can be written).
43
+ # If a block is given to this method, sample data can be written inside the given block. When the
44
+ # block terminates, the Writer will be automatically closed (and no more sample data can be written).
31
45
  #
32
46
  # If no block is given, then sample data can be written until the close method is called.
33
47
  def initialize(file_name, format)
@@ -60,7 +74,14 @@ module WaveFile
60
74
  def write(buffer)
61
75
  samples = buffer.convert(@format).samples
62
76
 
63
- @file.syswrite(samples.flatten.pack(@pack_code))
77
+ if @format.bits_per_sample == 24 && @format.sample_format == :pcm
78
+ samples.flatten.each do |sample|
79
+ @file.syswrite([sample].pack("lX"))
80
+ end
81
+ else
82
+ @file.syswrite(samples.flatten.pack(@pack_code))
83
+ end
84
+
64
85
  @total_sample_frames += samples.length
65
86
  end
66
87
 
@@ -73,10 +94,10 @@ module WaveFile
73
94
 
74
95
  # Closes the Writer. After a Writer is closed, no more sample data can be written to it.
75
96
  #
76
- # Note that the wave file will NOT be valid until this method is called. The wave file
77
- # format requires certain information about the amount of sample data, and this can't be
78
- # determined until all samples have been written. (This method doesn't need to be called
79
- # when passing a block to Writer.new, as this method will automatically be called when
97
+ # Note that the wave file will NOT be valid until this method is called. The wave file
98
+ # format requires certain information about the amount of sample data, and this can't be
99
+ # determined until all samples have been written. (This method doesn't need to be called
100
+ # when passing a block to Writer.new, as this method will automatically be called when
80
101
  # the block exits).
81
102
  #
82
103
  # Returns nothing.
@@ -109,12 +130,12 @@ module WaveFile
109
130
  # Returns the name of the Wave file that is being written to
110
131
  attr_reader :file_name
111
132
 
112
- # Returns a Format object describing the Wave file being written (number of channels, sample
133
+ # Returns a Format object describing the Wave file being written (number of channels, sample
113
134
  # format and bits per sample, sample rate, etc.)
114
135
  attr_reader :format
115
136
 
116
- # Returns the number of samples (per channel) that have been written to the file so far.
117
- # For example, if 1000 "left" samples and 1000 "right" samples have been written to a stereo file,
137
+ # Returns the number of samples (per channel) that have been written to the file so far.
138
+ # For example, if 1000 "left" samples and 1000 "right" samples have been written to a stereo file,
118
139
  # this will return 1000.
119
140
  attr_reader :total_sample_frames
120
141
 
data/test/buffer_test.rb CHANGED
@@ -106,6 +106,22 @@ class BufferTest < Test::Unit::TestCase
106
106
  b.samples)
107
107
  end
108
108
 
109
+ def test_convert_buffer_bits_per_sample_8_to_24
110
+ # Mono
111
+ b = Buffer.new([0, 32, 64, 96, 128, 160, 192, 223, 255], Format.new(:mono, :pcm_8, 44100))
112
+ b.convert!(Format.new(:mono, :pcm_24, 44100))
113
+ assert_equal([-8388608, -6291456, -4194304, -2097152, 0, 2097152, 4194304, 6225920, 8323072], b.samples)
114
+
115
+ # Stereo
116
+ b = Buffer.new([[0, 255], [32, 223], [64, 192], [96, 160], [128, 128],
117
+ [160, 96], [192, 64], [223, 32], [255, 0]],
118
+ Format.new(:stereo, :pcm_8, 44100))
119
+ b.convert!(Format.new(:stereo, :pcm_24, 44100))
120
+ assert_equal([[-8388608, 8323072], [-6291456, 6225920], [-4194304, 4194304], [-2097152, 2097152], [0, 0],
121
+ [2097152, -2097152], [4194304, -4194304], [6225920, -6291456], [8323072, -8388608]],
122
+ b.samples)
123
+ end
124
+
109
125
  def test_convert_buffer_bits_per_sample_8_to_32
110
126
  # Mono
111
127
  b = Buffer.new([0, 32, 64, 96, 128, 160, 192, 223, 255], Format.new(:mono, :pcm_8, 44100))
@@ -158,6 +174,22 @@ class BufferTest < Test::Unit::TestCase
158
174
  b.samples)
159
175
  end
160
176
 
177
+ def test_convert_buffer_bits_per_sample_16_to_24
178
+ # Mono
179
+ b = Buffer.new([-32768, -24576, -16384, -8192, 0, 8192, 16384, 24575, 32767], Format.new(:mono, :pcm_16, 44100))
180
+ b.convert!(Format.new(:mono, :pcm_24, 44100))
181
+ assert_equal([-8388608, -6291456, -4194304, -2097152, 0, 2097152, 4194304, 6291200, 8388352], b.samples)
182
+
183
+ # Stereo
184
+ b = Buffer.new([[-32768, 32767], [-24576, 24575], [-16384, 16384], [-8192, 8192], [0, 0],
185
+ [8192, -8192], [16384, -16384], [24575, -24576], [32767, -32768]],
186
+ Format.new(:stereo, :pcm_16, 44100))
187
+ b.convert!(Format.new(:stereo, :pcm_24, 44100))
188
+ assert_equal([[-8388608, 8388352], [-6291456, 6291200], [-4194304, 4194304], [-2097152, 2097152], [0, 0],
189
+ [2097152, -2097152], [4194304, -4194304], [6291200, -6291456], [8388352, -8388608]],
190
+ b.samples)
191
+ end
192
+
161
193
  def test_convert_buffer_bits_per_sample_16_to_32
162
194
  # Mono
163
195
  b = Buffer.new([-32768, -24576, -16384, -8192, 0, 8192, 16384, 24575, 32767], Format.new(:mono, :pcm_16, 44100))
@@ -194,6 +226,78 @@ class BufferTest < Test::Unit::TestCase
194
226
  end
195
227
  end
196
228
 
229
+ def test_convert_buffer_bits_per_sample_24_to_8
230
+ # Mono
231
+ b = Buffer.new([-8388608, -6291456, -4194304, -2097152, 0, 2097152, 4194304, 6291455, 8388607],
232
+ Format.new(:mono, :pcm_24, 44100))
233
+ b.convert!(Format.new(:mono, :pcm_8, 44100))
234
+ assert_equal([0, 32, 64, 96, 128, 160, 192, 223, 255], b.samples)
235
+
236
+ # Stereo
237
+ b = Buffer.new([[-8388608, 8388607], [-6291456, 6291455], [-4194304, 4194304], [-2097152, 2097152], [0, 0],
238
+ [2097152, -2097152], [4194304, -4194304], [6291455, -6291456], [8388607, -8388608]],
239
+ Format.new(:stereo, :pcm_24, 44100))
240
+ b.convert!(Format.new(:stereo, :pcm_8, 44100))
241
+ assert_equal([[0, 255], [32, 223], [64, 192], [96, 160], [128, 128],
242
+ [160, 96], [192, 64], [223, 32], [255, 0]],
243
+ b.samples)
244
+ end
245
+
246
+ def test_convert_buffer_bits_per_sample_24_to_16
247
+ # Mono
248
+ b = Buffer.new([-8388608, -6291456, -4194304, -2097152, 0, 2097152, 4194304, 6291455, 8388607],
249
+ Format.new(:mono, :pcm_24, 44100))
250
+ b.convert!(Format.new(:mono, :pcm_16, 44100))
251
+ assert_equal([-32768, -24576, -16384, -8192, 0, 8192, 16384, 24575, 32767], b.samples)
252
+
253
+ # Stereo
254
+ b = Buffer.new([[-8388608, 8388607], [-6291456, 6291455], [-4194304, 4194304], [-2097152, 2097152], [0, 0],
255
+ [2097152, -2097152], [4194304, -4194304], [6291455, -6291456], [8388607, -8388608]],
256
+ Format.new(:stereo, :pcm_24, 44100))
257
+ b.convert!(Format.new(:stereo, :pcm_16, 44100))
258
+ assert_equal([[-32768, 32767], [-24576, 24575], [-16384, 16384], [-8192, 8192], [0, 0],
259
+ [8192, -8192], [16384, -16384], [24575, -24576], [32767, -32768]],
260
+ b.samples)
261
+ end
262
+
263
+ def test_convert_buffer_bits_per_sample_24_to_32
264
+ # Mono
265
+ b = Buffer.new([-8388608, -6291456, -4194304, -2097152, 0, 2097152, 4194304, 6291455, 8388607],
266
+ Format.new(:mono, :pcm_24, 44100))
267
+ b.convert!(Format.new(:mono, :pcm_32, 44100))
268
+ assert_equal([-2147483648, -1610612736, -1073741824, -536870912, 0, 536870912, 1073741824, 1610612480, 2147483392], b.samples)
269
+
270
+ # Stereo
271
+ b = Buffer.new([[-8388608, 8388607], [-6291456, 6291455], [-4194304, 4194304], [-2097152, 2097152], [0, 0],
272
+ [2097152, -2097152], [4194304, -4194304], [6291455, -6291456], [8388607, -8388608]],
273
+ Format.new(:stereo, :pcm_24, 44100))
274
+ b.convert!(Format.new(:stereo, :pcm_32, 44100))
275
+ assert_equal([[-2147483648, 2147483392], [-1610612736, 1610612480], [-1073741824, 1073741824], [-536870912, 536870912], [0, 0],
276
+ [536870912, -536870912], [1073741824, -1073741824], [1610612480, -1610612736], [2147483392, -2147483648]],
277
+ b.samples)
278
+ end
279
+
280
+ def test_convert_buffer_bits_per_sample_24_to_float
281
+ Format::SUPPORTED_BITS_PER_SAMPLE[:float].each do |bits_per_sample|
282
+ float_format = "float_#{bits_per_sample}".to_sym
283
+
284
+ # Mono
285
+ b = Buffer.new([-8388608, -6291456, -4194304, -2097152, 0, 2097152, 4194304, 6291455, 8388607],
286
+ Format.new(:mono, :pcm_24, 44100))
287
+ b.convert!(Format.new(:mono, float_format, 44100))
288
+ assert_equal([-1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.7499998807907104, 0.9999998807907104], b.samples)
289
+
290
+ # Stereo
291
+ b = Buffer.new([[-8388608, 8388607], [-6291456, 6291455], [-4194304, 4194304], [-2097152, 2097152], [0, 0],
292
+ [2097152, -2097152], [4194304, -4194304], [6291455, -6291456], [8388607, -8388608]],
293
+ Format.new(:stereo, :pcm_24, 44100))
294
+ b.convert!(Format.new(:stereo, float_format, 44100))
295
+ assert_equal([[-1.0, 0.9999998807907104], [-0.75, 0.7499998807907104 ], [-0.5, 0.5], [-0.25, 0.25], [0.0, 0.0],
296
+ [0.25, -0.25], [0.5, -0.5], [0.7499998807907104 , -0.75], [0.9999998807907104, -1.0]],
297
+ b.samples)
298
+ end
299
+ end
300
+
197
301
  def test_convert_buffer_bits_per_sample_32_to_8
198
302
  # Mono
199
303
  b = Buffer.new([-2147483648, -1610612736, -1073741824, -536870912, 0, 536870912, 1073741824, 1610612735, 2147483647],
@@ -228,6 +332,23 @@ class BufferTest < Test::Unit::TestCase
228
332
  b.samples)
229
333
  end
230
334
 
335
+ def test_convert_buffer_bits_per_sample_32_to_24
336
+ # Mono
337
+ b = Buffer.new([-2147483648, -1610612736, -1073741824, -536870912, 0, 536870912, 1073741824, 1610612735, 2147483647],
338
+ Format.new(:mono, :pcm_32, 44100))
339
+ b.convert!(Format.new(:mono, :pcm_24, 44100))
340
+ assert_equal([-8388608, -6291456, -4194304, -2097152, 0, 2097152, 4194304, 6291455, 8388607], b.samples)
341
+
342
+ # Stereo
343
+ b = Buffer.new([[-2147483648, 2147483647], [-1610612736, 1610612735], [-1073741824, 1073741824], [-536870912, 536870912], [0, 0],
344
+ [536870912, -536870912], [1073741824, -1073741824], [1610612735, -1610612736], [2147483647, -2147483648]],
345
+ Format.new(:stereo, :pcm_32, 44100))
346
+ b.convert!(Format.new(:stereo, :pcm_24, 44100))
347
+ assert_equal([[-8388608, 8388607], [-6291456, 6291455], [-4194304, 4194304], [-2097152, 2097152], [0, 0],
348
+ [2097152, -2097152], [4194304, -4194304], [6291455, -6291456], [8388607, -8388608]],
349
+ b.samples)
350
+ end
351
+
231
352
  def test_convert_buffer_bits_per_sample_32_to_float
232
353
  Format::SUPPORTED_BITS_PER_SAMPLE[:float].each do |bits_per_sample|
233
354
  float_format = "float_#{bits_per_sample}".to_sym
@@ -289,6 +410,26 @@ class BufferTest < Test::Unit::TestCase
289
410
  end
290
411
  end
291
412
 
413
+ def test_convert_buffer_bits_per_sample_float_to_24
414
+ Format::SUPPORTED_BITS_PER_SAMPLE[:float].each do |bits_per_sample|
415
+ float_format = "float_#{bits_per_sample}".to_sym
416
+
417
+ # Mono
418
+ b = Buffer.new([-1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0], Format.new(:mono, float_format, 44100))
419
+ b.convert!(Format.new(:mono, :pcm_24, 44100))
420
+ assert_equal([-8388607, -6291455, -4194304, -2097152, 0, 2097152, 4194304, 6291455, 8388607], b.samples)
421
+
422
+ # Stereo
423
+ b = Buffer.new([[-1.0, 1.0], [-0.75, 0.75], [-0.5, 0.5], [-0.25, 0.25], [0.0, 0.0],
424
+ [0.25, -0.25], [0.5, -0.5], [0.75, -0.75], [1.0, -1.0]],
425
+ Format.new(:stereo, float_format, 44100))
426
+ b.convert!(Format.new(:stereo, :pcm_24, 44100))
427
+ assert_equal([[-8388607, 8388607], [-6291455, 6291455], [-4194304, 4194304], [-2097152, 2097152], [0, 0],
428
+ [2097152, -2097152], [4194304, -4194304], [6291455, -6291455], [8388607, -8388607]],
429
+ b.samples)
430
+ end
431
+ end
432
+
292
433
  def test_convert_buffer_bits_per_sample_float_to_32
293
434
  Format::SUPPORTED_BITS_PER_SAMPLE[:float].each do |bits_per_sample|
294
435
  float_format = "float_#{bits_per_sample}".to_sym