wavefile 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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