/rust/registry/src/index.crates.io-6f17d22bba15001f/hound-3.5.0/src/lib.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Hound -- A wav encoding and decoding library in Rust |
2 | | // Copyright (C) 2015 Ruud van Asseldonk |
3 | | // |
4 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | // you may not use this file except in compliance with the License. |
6 | | // A copy of the License has been included in the root of the repository. |
7 | | // Unless required by applicable law or agreed to in writing, software |
8 | | // distributed under the License is distributed on an "AS IS" BASIS, |
9 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
10 | | // See the License for the specific language governing permissions and |
11 | | // limitations under the License. |
12 | | |
13 | | //! Hound, a wav encoding and decoding library. |
14 | | //! |
15 | | //! Examples |
16 | | //! ======== |
17 | | //! |
18 | | //! The following example renders a 440 Hz sine wave, and stores it as as a |
19 | | //! mono wav file with a sample rate of 44.1 kHz and 16 bits per sample. |
20 | | //! |
21 | | //! ``` |
22 | | //! use std::f32::consts::PI; |
23 | | //! use std::i16; |
24 | | //! use hound; |
25 | | //! |
26 | | //! let spec = hound::WavSpec { |
27 | | //! channels: 1, |
28 | | //! sample_rate: 44100, |
29 | | //! bits_per_sample: 16, |
30 | | //! sample_format: hound::SampleFormat::Int, |
31 | | //! }; |
32 | | //! let mut writer = hound::WavWriter::create("sine.wav", spec).unwrap(); |
33 | | //! for t in (0 .. 44100).map(|x| x as f32 / 44100.0) { |
34 | | //! let sample = (t * 440.0 * 2.0 * PI).sin(); |
35 | | //! let amplitude = i16::MAX as f32; |
36 | | //! writer.write_sample((sample * amplitude) as i16).unwrap(); |
37 | | //! } |
38 | | //! writer.finalize().unwrap(); |
39 | | //! ``` |
40 | | //! |
41 | | //! The following example computes the root mean square (RMS) of an audio file |
42 | | //! with at most 16 bits per sample. |
43 | | //! |
44 | | //! ``` |
45 | | //! use hound; |
46 | | //! |
47 | | //! let mut reader = hound::WavReader::open("testsamples/pop.wav").unwrap(); |
48 | | //! let sqr_sum = reader.samples::<i16>() |
49 | | //! .fold(0.0, |sqr_sum, s| { |
50 | | //! let sample = s.unwrap() as f64; |
51 | | //! sqr_sum + sample * sample |
52 | | //! }); |
53 | | //! println!("RMS is {}", (sqr_sum / reader.len() as f64).sqrt()); |
54 | | //! ``` |
55 | | |
56 | | #![warn(missing_docs)] |
57 | | |
58 | | use std::error; |
59 | | use std::fmt; |
60 | | use std::io; |
61 | | use std::result; |
62 | | use read::ReadExt; |
63 | | use write::WriteExt; |
64 | | |
65 | | mod read; |
66 | | mod write; |
67 | | |
68 | | pub use read::{WavReader, WavIntoSamples, WavSamples, read_wave_header}; |
69 | | pub use write::{SampleWriter16, WavWriter}; |
70 | | |
71 | | /// A type that can be used to represent audio samples. |
72 | | /// |
73 | | /// Via this trait, decoding can be generic over `i8`, `i16`, `i32` and `f32`. |
74 | | /// |
75 | | /// All integer formats with bit depths up to 32 bits per sample can be decoded |
76 | | /// into `i32`, but it takes up more memory. If you know beforehand that you |
77 | | /// will be reading a file with 16 bits per sample, then decoding into an `i16` |
78 | | /// will be sufficient. |
79 | | pub trait Sample: Sized { |
80 | | /// Writes the audio sample to the WAVE data chunk. |
81 | | fn write<W: io::Write>(self, writer: &mut W, bits: u16) -> Result<()>; |
82 | | |
83 | | /// Writes the audio sample to the WAVE data chunk, zero padding the size of |
84 | | /// the written sample out to `byte_width`. |
85 | | fn write_padded<W: io::Write>(self, writer: &mut W, bits: u16, byte_width: u16) -> Result<()>; |
86 | | |
87 | | /// Reads the audio sample from the WAVE data chunk. |
88 | | fn read<R: io::Read>(reader: &mut R, SampleFormat, bytes: u16, bits: u16) -> Result<Self>; |
89 | | |
90 | | /// Cast the sample to a 16-bit sample. |
91 | | /// |
92 | | /// This does not change the value of the sample, it only casts it. The |
93 | | /// value is assumed to fit within the range. This is not verified, |
94 | | /// truncation may occur. |
95 | | fn as_i16(self) -> i16; |
96 | | } |
97 | | |
98 | | /// Converts an unsigned integer in the range 0-255 to a signed one in the range -128-127. |
99 | | /// |
100 | | /// Presumably, the designers of the WAVE format did not like consistency. For |
101 | | /// all bit depths except 8, samples are stored as little-endian _signed_ |
102 | | /// integers. However, an 8-bit sample is instead stored as an _unsigned_ |
103 | | /// integer. Hound abstracts away this idiosyncrasy by providing only signed |
104 | | /// sample types. |
105 | 0 | fn signed_from_u8(x: u8) -> i8 { |
106 | 0 | (x as i16 - 128) as i8 |
107 | 0 | } |
108 | | |
109 | | /// Converts a signed integer in the range -128-127 to an unsigned one in the range 0-255. |
110 | 0 | fn u8_from_signed(x: i8) -> u8 { |
111 | 0 | (x as i16 + 128) as u8 |
112 | 0 | } |
113 | | |
114 | | #[test] |
115 | | fn u8_sign_conversion_is_bijective() { |
116 | | for x in 0..255 { |
117 | | assert_eq!(x, u8_from_signed(signed_from_u8(x))); |
118 | | } |
119 | | for x in -128..127 { |
120 | | assert_eq!(x, signed_from_u8(u8_from_signed(x))); |
121 | | } |
122 | | } |
123 | | |
124 | | /// Tries to cast the sample to an 8-bit signed integer, returning an error on overflow. |
125 | | #[inline(always)] |
126 | 0 | fn narrow_to_i8(x: i32) -> Result<i8> { |
127 | | use std::i8; |
128 | 0 | if x < i8::MIN as i32 || x > i8::MAX as i32 { |
129 | 0 | Err(Error::TooWide) |
130 | | } else { |
131 | 0 | Ok(x as i8) |
132 | | } |
133 | 0 | } |
134 | | |
135 | | #[test] |
136 | | fn verify_narrow_to_i8() { |
137 | | assert!(narrow_to_i8(127).is_ok()); |
138 | | assert!(narrow_to_i8(128).is_err()); |
139 | | assert!(narrow_to_i8(-128).is_ok()); |
140 | | assert!(narrow_to_i8(-129).is_err()); |
141 | | } |
142 | | |
143 | | /// Tries to cast the sample to a 16-bit signed integer, returning an error on overflow. |
144 | | #[inline(always)] |
145 | 0 | fn narrow_to_i16(x: i32) -> Result<i16> { |
146 | | use std::i16; |
147 | 0 | if x < i16::MIN as i32 || x > i16::MAX as i32 { |
148 | 0 | Err(Error::TooWide) |
149 | | } else { |
150 | 0 | Ok(x as i16) |
151 | | } |
152 | 0 | } |
153 | | |
154 | | #[test] |
155 | | fn verify_narrow_to_i16() { |
156 | | assert!(narrow_to_i16(32767).is_ok()); |
157 | | assert!(narrow_to_i16(32768).is_err()); |
158 | | assert!(narrow_to_i16(-32768).is_ok()); |
159 | | assert!(narrow_to_i16(-32769).is_err()); |
160 | | } |
161 | | |
162 | | /// Tries to cast the sample to a 24-bit signed integer, returning an error on overflow. |
163 | | #[inline(always)] |
164 | 0 | fn narrow_to_i24(x: i32) -> Result<i32> { |
165 | 0 | if x < -(1 << 23) || x > (1 << 23) - 1 { |
166 | 0 | Err(Error::TooWide) |
167 | | } else { |
168 | 0 | Ok(x) |
169 | | } |
170 | 0 | } |
171 | | |
172 | | #[test] |
173 | | fn verify_narrow_to_i24() { |
174 | | assert!(narrow_to_i24(8_388_607).is_ok()); |
175 | | assert!(narrow_to_i24(8_388_608).is_err()); |
176 | | assert!(narrow_to_i24(-8_388_608).is_ok()); |
177 | | assert!(narrow_to_i24(-8_388_609).is_err()); |
178 | | } |
179 | | |
180 | | impl Sample for i8 { |
181 | 0 | fn write<W: io::Write>(self, writer: &mut W, bits: u16) -> Result<()> { |
182 | 0 | self.write_padded(writer, bits, bits / 8) |
183 | 0 | } |
184 | | |
185 | 0 | fn write_padded<W: io::Write>(self, writer: &mut W, bits: u16, byte_width: u16) -> Result<()> { |
186 | 0 | match (bits, byte_width) { |
187 | 0 | (8, 1) => Ok(try!(writer.write_u8(u8_from_signed(self)))), |
188 | 0 | (16, 2) => Ok(try!(writer.write_le_i16(self as i16))), |
189 | 0 | (24, 3) => Ok(try!(writer.write_le_i24(self as i32))), |
190 | 0 | (24, 4) => Ok(try!(writer.write_le_i24_4(self as i32))), |
191 | 0 | (32, 4) => Ok(try!(writer.write_le_i32(self as i32))), |
192 | 0 | _ => Err(Error::Unsupported), |
193 | | } |
194 | 0 | } |
195 | | |
196 | | #[inline(always)] |
197 | 0 | fn as_i16(self) -> i16 { |
198 | 0 | self as i16 |
199 | 0 | } |
200 | | |
201 | 0 | fn read<R: io::Read>(reader: &mut R, fmt: SampleFormat, bytes: u16, bits: u16) -> Result<i8> { |
202 | 0 | if fmt != SampleFormat::Int { |
203 | 0 | return Err(Error::InvalidSampleFormat); |
204 | 0 | } |
205 | 0 | match (bytes, bits) { |
206 | 0 | (1, 8) => Ok(try!(reader.read_u8().map(signed_from_u8))), |
207 | 0 | (n, _) if n > 1 => Err(Error::TooWide), |
208 | | // TODO: add a genric decoder for any bit depth. |
209 | 0 | _ => Err(Error::Unsupported), |
210 | | } |
211 | 0 | } |
212 | | } |
213 | | |
214 | | impl Sample for i16 { |
215 | 0 | fn write<W: io::Write>(self, writer: &mut W, bits: u16) -> Result<()> { |
216 | 0 | self.write_padded(writer, bits, bits / 8) |
217 | 0 | } |
218 | | |
219 | 0 | fn write_padded<W: io::Write>(self, writer: &mut W, bits: u16, byte_width: u16) -> Result<()> { |
220 | 0 | match (bits, byte_width) { |
221 | 0 | (8, 1) => Ok(try!( |
222 | 0 | writer.write_u8(u8_from_signed(try!(narrow_to_i8(self as i32)))) |
223 | | )), |
224 | 0 | (16, 2) => Ok(try!(writer.write_le_i16(self))), |
225 | 0 | (24, 3) => Ok(try!(writer.write_le_i24(self as i32))), |
226 | 0 | (24, 4) => Ok(try!(writer.write_le_i24_4(self as i32))), |
227 | 0 | (32, 4) => Ok(try!(writer.write_le_i32(self as i32))), |
228 | 0 | _ => Err(Error::Unsupported), |
229 | | } |
230 | 0 | } |
231 | | |
232 | | #[inline(always)] |
233 | 0 | fn as_i16(self) -> i16 { |
234 | 0 | self |
235 | 0 | } |
236 | | |
237 | 0 | fn read<R: io::Read>(reader: &mut R, fmt: SampleFormat, bytes: u16, bits: u16) -> Result<i16> { |
238 | 0 | if fmt != SampleFormat::Int { |
239 | 0 | return Err(Error::InvalidSampleFormat); |
240 | 0 | } |
241 | 0 | match (bytes, bits) { |
242 | 0 | (1, 8) => Ok(try!(reader.read_u8().map(signed_from_u8).map(|x| x as i16))), |
243 | 0 | (2, 16) => Ok(try!(reader.read_le_i16())), |
244 | 0 | (n, _) if n > 2 => Err(Error::TooWide), |
245 | | // TODO: add a generic decoder for any bit depth. |
246 | 0 | _ => Err(Error::Unsupported), |
247 | | } |
248 | 0 | } |
249 | | } |
250 | | |
251 | | impl Sample for i32 { |
252 | 0 | fn write<W: io::Write>(self, writer: &mut W, bits: u16) -> Result<()> { |
253 | 0 | self.write_padded(writer, bits, bits / 8) |
254 | 0 | } |
255 | | |
256 | 0 | fn write_padded<W: io::Write>(self, writer: &mut W, bits: u16, byte_width: u16) -> Result<()> { |
257 | 0 | match (bits, byte_width) { |
258 | 0 | (8, 1) => Ok(try!( |
259 | 0 | writer.write_u8(u8_from_signed(try!(narrow_to_i8(self)))) |
260 | | )), |
261 | 0 | (16, 2) => Ok(try!(writer.write_le_i16(try!(narrow_to_i16(self))))), |
262 | 0 | (24, 3) => Ok(try!(writer.write_le_i24(try!(narrow_to_i24(self))))), |
263 | 0 | (24, 4) => Ok(try!(writer.write_le_i24_4(try!(narrow_to_i24(self))))), |
264 | 0 | (32, 4) => Ok(try!(writer.write_le_i32(self))), |
265 | 0 | _ => Err(Error::Unsupported), |
266 | | } |
267 | 0 | } |
268 | | |
269 | | #[inline(always)] |
270 | 0 | fn as_i16(self) -> i16 { |
271 | 0 | self as i16 |
272 | 0 | } |
273 | | |
274 | 0 | fn read<R: io::Read>(reader: &mut R, fmt: SampleFormat, bytes: u16, bits: u16) -> Result<i32> { |
275 | 0 | if fmt != SampleFormat::Int { |
276 | 0 | return Err(Error::InvalidSampleFormat); |
277 | 0 | } |
278 | 0 | match (bytes, bits) { |
279 | 0 | (1, 8) => Ok(try!(reader.read_u8().map(signed_from_u8).map(|x| x as i32))), |
280 | 0 | (2, 16) => Ok(try!(reader.read_le_i16().map(|x| x as i32))), |
281 | 0 | (3, 24) => Ok(try!(reader.read_le_i24())), |
282 | 0 | (4, 24) => Ok(try!(reader.read_le_i24_4())), |
283 | 0 | (4, 32) => Ok(try!(reader.read_le_i32())), |
284 | 0 | (n, _) if n > 4 => Err(Error::TooWide), |
285 | | // TODO: add a generic decoder for any bit depth. |
286 | 0 | _ => Err(Error::Unsupported), |
287 | | } |
288 | 0 | } |
289 | | } |
290 | | |
291 | | impl Sample for f32 { |
292 | 0 | fn write<W: io::Write>(self, writer: &mut W, bits: u16) -> Result<()> { |
293 | 0 | self.write_padded(writer, bits, bits / 8) |
294 | 0 | } |
295 | | |
296 | 0 | fn write_padded<W: io::Write>(self, writer: &mut W, bits: u16, byte_width: u16) -> Result<()> { |
297 | 0 | match (bits, byte_width) { |
298 | 0 | (32, 4) => Ok(try!(writer.write_le_f32(self))), |
299 | 0 | _ => Err(Error::Unsupported), |
300 | | } |
301 | 0 | } Unexecuted instantiation: <f32 as hound::Sample>::write_padded::<std::io::buffered::bufwriter::BufWriter<std::fs::File>> Unexecuted instantiation: <f32 as hound::Sample>::write_padded::<_> |
302 | | |
303 | 0 | fn as_i16(self) -> i16 { |
304 | 0 | panic!("Calling as_i16 with an f32 is invalid."); |
305 | | } |
306 | | |
307 | 0 | fn read<R: io::Read>(reader: &mut R, fmt: SampleFormat, bytes: u16, bits: u16) -> Result<Self> { |
308 | 0 | if fmt != SampleFormat::Float { |
309 | 0 | return Err(Error::InvalidSampleFormat); |
310 | 0 | } |
311 | 0 | match (bytes, bits) { |
312 | 0 | (4, 32) => Ok(try!(reader.read_le_f32())), |
313 | 0 | (n, _) if n > 4 => Err(Error::TooWide), |
314 | 0 | _ => Err(Error::Unsupported), |
315 | | } |
316 | 0 | } |
317 | | } |
318 | | |
319 | | /// Specifies whether a sample is stored as an "IEEE Float" or an integer. |
320 | | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
321 | | pub enum SampleFormat { |
322 | | /// Wave files with the `WAVE_FORMAT_IEEE_FLOAT` format tag store samples as floating point |
323 | | /// values. |
324 | | /// |
325 | | /// Values are normally in the range [-1.0, 1.0]. |
326 | | Float, |
327 | | /// Wave files with the `WAVE_FORMAT_PCM` format tag store samples as integer values. |
328 | | Int, |
329 | | } |
330 | | |
331 | | /// Specifies properties of the audio data. |
332 | | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
333 | | pub struct WavSpec { |
334 | | /// The number of channels. |
335 | | pub channels: u16, |
336 | | |
337 | | /// The number of samples per second. |
338 | | /// |
339 | | /// A common value is 44100, this is 44.1 kHz which is used for CD audio. |
340 | | pub sample_rate: u32, |
341 | | |
342 | | /// The number of bits per sample. |
343 | | /// |
344 | | /// A common value is 16 bits per sample, which is used for CD audio. |
345 | | pub bits_per_sample: u16, |
346 | | |
347 | | /// Whether the wav's samples are float or integer values. |
348 | | pub sample_format: SampleFormat, |
349 | | } |
350 | | |
351 | | /// Specifies properties of the audio data, as well as the layout of the stream. |
352 | | #[derive(Clone, Copy)] |
353 | | pub struct WavSpecEx { |
354 | | /// The normal information about the audio data. |
355 | | /// |
356 | | /// Bits per sample here is the number of _used_ bits per sample, not the |
357 | | /// number of bits used to _store_ a sample. |
358 | | pub spec: WavSpec, |
359 | | |
360 | | /// The number of bytes used to store a sample. |
361 | | pub bytes_per_sample: u16, |
362 | | } |
363 | | |
364 | | /// The error type for operations on `WavReader` and `WavWriter`. |
365 | | #[derive(Debug)] |
366 | | pub enum Error { |
367 | | /// An IO error occured in the underlying reader or writer. |
368 | | IoError(io::Error), |
369 | | /// Ill-formed WAVE data was encountered. |
370 | | FormatError(&'static str), |
371 | | /// The sample has more bits than the destination type. |
372 | | /// |
373 | | /// When iterating using the `samples` iterator, this means that the |
374 | | /// destination type (produced by the iterator) is not wide enough to hold |
375 | | /// the sample. When writing, this means that the sample cannot be written, |
376 | | /// because it requires more bits than the bits per sample specified. |
377 | | TooWide, |
378 | | /// The number of samples written is not a multiple of the number of channels. |
379 | | UnfinishedSample, |
380 | | /// The format is not supported. |
381 | | Unsupported, |
382 | | /// The sample format is different than the destination format. |
383 | | /// |
384 | | /// When iterating using the `samples` iterator, this means the destination |
385 | | /// type (produced by the iterator) has a different sample format than the |
386 | | /// samples in the wav file. |
387 | | /// |
388 | | /// For example, this will occur if the user attempts to produce `i32` |
389 | | /// samples (which have a `SampleFormat::Int`) from a wav file that |
390 | | /// contains floating point data (`SampleFormat::Float`). |
391 | | InvalidSampleFormat, |
392 | | } |
393 | | |
394 | | impl fmt::Display for Error { |
395 | 0 | fn fmt(&self, formatter: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { |
396 | 0 | match *self { |
397 | 0 | Error::IoError(ref err) => err.fmt(formatter), |
398 | 0 | Error::FormatError(reason) => { |
399 | 0 | try!(formatter.write_str("Ill-formed WAVE file: ")); |
400 | 0 | formatter.write_str(reason) |
401 | | } |
402 | | Error::TooWide => { |
403 | 0 | formatter.write_str("The sample has more bits than the destination type.") |
404 | | } |
405 | | Error::UnfinishedSample => { |
406 | 0 | formatter.write_str( |
407 | 0 | "The number of samples written is not a multiple of the number of channels.") |
408 | | } |
409 | | Error::Unsupported => { |
410 | 0 | formatter.write_str("The wave format of the file is not supported.") |
411 | | } |
412 | | Error::InvalidSampleFormat => { |
413 | 0 | formatter.write_str("The sample format differs from the destination format.") |
414 | | } |
415 | | } |
416 | 0 | } |
417 | | } |
418 | | |
419 | | impl error::Error for Error { |
420 | 0 | fn description(&self) -> &str { |
421 | 0 | match *self { |
422 | 0 | Error::IoError(ref err) => err.description(), |
423 | 0 | Error::FormatError(reason) => reason, |
424 | 0 | Error::TooWide => "the sample has more bits than the destination type", |
425 | 0 | Error::UnfinishedSample => "the number of samples written is not a multiple of the number of channels", |
426 | 0 | Error::Unsupported => "the wave format of the file is not supported", |
427 | 0 | Error::InvalidSampleFormat => "the sample format differs from the destination format", |
428 | | } |
429 | 0 | } |
430 | | |
431 | 0 | fn cause(&self) -> Option<&error::Error> { |
432 | 0 | match *self { |
433 | 0 | Error::IoError(ref err) => Some(err), |
434 | 0 | Error::FormatError(_) => None, |
435 | 0 | Error::TooWide => None, |
436 | 0 | Error::UnfinishedSample => None, |
437 | 0 | Error::Unsupported => None, |
438 | 0 | Error::InvalidSampleFormat => None, |
439 | | } |
440 | 0 | } |
441 | | } |
442 | | |
443 | | impl From<io::Error> for Error { |
444 | 0 | fn from(err: io::Error) -> Error { |
445 | 0 | Error::IoError(err) |
446 | 0 | } |
447 | | } |
448 | | |
449 | | /// A type for results generated by Hound where the error type is hard-wired. |
450 | | pub type Result<T> = result::Result<T, Error>; |
451 | | |
452 | | // The WAVEFORMATEXTENSIBLE struct can contain several subformats. |
453 | | // These are identified by a GUID. The various GUIDS can be found in the file |
454 | | // mmreg.h that is part of the Windows SDK. The following GUIDS are defined: |
455 | | // - PCM: 00000001-0000-0010-8000-00aa00389b71 |
456 | | // - IEEE_FLOAT: 00000003-0000-0010-8000-00aa00389b71 |
457 | | // When written to a wav file, the byte order of a GUID is native for the first |
458 | | // three sections, which is assumed to be little endian, and big endian for the |
459 | | // last 8-byte section (which does contain a hyphen, for reasons unknown to me). |
460 | | |
461 | | /// Subformat type for PCM audio with integer samples. |
462 | | const KSDATAFORMAT_SUBTYPE_PCM: [u8; 16] = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, |
463 | | 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71]; |
464 | | |
465 | | /// Subformat type for IEEE_FLOAT audio with float samples. |
466 | | const KSDATAFORMAT_SUBTYPE_IEEE_FLOAT: [u8; 16] = [0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, |
467 | | 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71]; |
468 | | |
469 | | |
470 | | impl WavSpec { |
471 | | /// Get "stand-alone" wav header representing infinite or unknown size wav file. |
472 | | /// Use this if you need to write audio data to non-seekable sinks (like stdout). |
473 | | /// |
474 | | /// Actual samples are supposed to be written using low-level [`Sample::write`] call. |
475 | | /// |
476 | | /// Such wav files are produced e.g. by FFmpeg and have `0xFFFFFFFF` instead of chunk sizes. |
477 | | /// |
478 | | /// Note that such files may be non-standard. Consider using [`WavWriter`] for better API. |
479 | | /// |
480 | | /// Example: |
481 | | /// |
482 | | /// ```no_run |
483 | | /// extern crate hound; |
484 | | /// use std::io::Write; |
485 | | /// |
486 | | /// let spec = hound::WavSpec { |
487 | | /// bits_per_sample: 16, |
488 | | /// channels: 1, |
489 | | /// sample_format: hound::SampleFormat::Int, |
490 | | /// sample_rate: 16000, |
491 | | /// }; |
492 | | /// |
493 | | /// let v = spec.into_header_for_infinite_file(); |
494 | | /// |
495 | | /// let so = std::io::stdout(); |
496 | | /// let mut so = so.lock(); |
497 | | /// so.write_all(&v[..]).unwrap(); |
498 | | /// |
499 | | /// loop { |
500 | | /// for i in 0..126 { |
501 | | /// let x : i16 = (i * 256) as i16; |
502 | | /// hound::Sample::write(x, &mut so, 16).unwrap(); |
503 | | /// } |
504 | | /// } |
505 | | /// ``` |
506 | 0 | pub fn into_header_for_infinite_file(self) -> Vec<u8> { |
507 | 0 | let mut c = std::io::Cursor::new(Vec::with_capacity(0x44)); |
508 | 0 | { |
509 | 0 | let w = WavWriter::new(&mut c, self); |
510 | 0 | drop(w); |
511 | 0 | } |
512 | 0 | let mut v = c.into_inner(); |
513 | 0 |
|
514 | 0 | // Set WAVE chunk size to a special signal value |
515 | 0 | v[4] = 0xFF; v[5] = 0xFF; v[6] = 0xFF; v[7] = 0xFF; |
516 | 0 |
|
517 | 0 | // Detect fmt size, get offset of data chunk's size and set it to signal value |
518 | 0 | if v[16] == 0x10 { |
519 | 0 | // pcm wave |
520 | 0 | v[0x28] = 0xFF; v[0x29] = 0xFF; v[0x2A] = 0xFF; v[0x2B] = 0xFF; |
521 | 0 | } else if v[16] == 0x28 { |
522 | 0 | // extensible |
523 | 0 | v[0x40] = 0xFF; v[0x41] = 0xFF; v[0x42] = 0xFF; v[0x43] = 0xFF; |
524 | 0 | } else { |
525 | 0 | unreachable!() |
526 | | } |
527 | | |
528 | 0 | v |
529 | 0 | } |
530 | | } |
531 | | |
532 | | #[test] |
533 | | fn write_read_i16_is_lossless() { |
534 | | let mut buffer = io::Cursor::new(Vec::new()); |
535 | | let write_spec = WavSpec { |
536 | | channels: 2, |
537 | | sample_rate: 44100, |
538 | | bits_per_sample: 16, |
539 | | sample_format: SampleFormat::Int, |
540 | | }; |
541 | | |
542 | | { |
543 | | let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap(); |
544 | | for s in -1024_i16..1024 { |
545 | | writer.write_sample(s).unwrap(); |
546 | | } |
547 | | writer.finalize().unwrap(); |
548 | | } |
549 | | |
550 | | { |
551 | | buffer.set_position(0); |
552 | | let mut reader = WavReader::new(&mut buffer).unwrap(); |
553 | | assert_eq!(write_spec, reader.spec()); |
554 | | assert_eq!(reader.len(), 2048); |
555 | | for (expected, read) in (-1024_i16..1024).zip(reader.samples()) { |
556 | | assert_eq!(expected, read.unwrap()); |
557 | | } |
558 | | } |
559 | | } |
560 | | |
561 | | #[test] |
562 | | fn write_read_i16_via_sample_writer_is_lossless() { |
563 | | let mut buffer = io::Cursor::new(Vec::new()); |
564 | | let write_spec = WavSpec { |
565 | | channels: 2, |
566 | | sample_rate: 44100, |
567 | | bits_per_sample: 16, |
568 | | sample_format: SampleFormat::Int, |
569 | | }; |
570 | | |
571 | | { |
572 | | let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap(); |
573 | | { |
574 | | { |
575 | | let mut sample_writer = writer.get_i16_writer(1024); |
576 | | for s in -1024_i16..0 { |
577 | | sample_writer.write_sample(s); |
578 | | } |
579 | | sample_writer.flush().unwrap(); |
580 | | } |
581 | | |
582 | | { |
583 | | let mut sample_writer = writer.get_i16_writer(1024); |
584 | | for s in 0i16..1024 { |
585 | | unsafe { sample_writer.write_sample_unchecked(s); } |
586 | | } |
587 | | sample_writer.flush().unwrap(); |
588 | | } |
589 | | } |
590 | | writer.finalize().unwrap(); |
591 | | } |
592 | | |
593 | | { |
594 | | buffer.set_position(0); |
595 | | let mut reader = WavReader::new(&mut buffer).unwrap(); |
596 | | assert_eq!(write_spec, reader.spec()); |
597 | | assert_eq!(reader.len(), 2048); |
598 | | for (expected, read) in (-1024_i16..1024).zip(reader.samples()) { |
599 | | assert_eq!(expected, read.unwrap()); |
600 | | } |
601 | | } |
602 | | } |
603 | | |
604 | | #[test] |
605 | | fn write_read_i8_is_lossless() { |
606 | | let mut buffer = io::Cursor::new(Vec::new()); |
607 | | let write_spec = WavSpec { |
608 | | channels: 16, |
609 | | sample_rate: 48000, |
610 | | bits_per_sample: 8, |
611 | | sample_format: SampleFormat::Int, |
612 | | }; |
613 | | |
614 | | // Write `i8` samples. |
615 | | { |
616 | | let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap(); |
617 | | // Iterate over i16 because we cannot specify the upper bound otherwise. |
618 | | for s in -128_i16..127 + 1 { |
619 | | writer.write_sample(s as i8).unwrap(); |
620 | | } |
621 | | writer.finalize().unwrap(); |
622 | | } |
623 | | |
624 | | // Then read them into `i16`. |
625 | | { |
626 | | buffer.set_position(0); |
627 | | let mut reader = WavReader::new(&mut buffer).unwrap(); |
628 | | assert_eq!(write_spec, reader.spec()); |
629 | | assert_eq!(reader.len(), 256); |
630 | | for (expected, read) in (-128_i16..127 + 1).zip(reader.samples()) { |
631 | | assert_eq!(expected, read.unwrap()); |
632 | | } |
633 | | } |
634 | | } |
635 | | |
636 | | #[test] |
637 | | fn write_read_i24_is_lossless() { |
638 | | let mut buffer = io::Cursor::new(Vec::new()); |
639 | | let write_spec = WavSpec { |
640 | | channels: 16, |
641 | | sample_rate: 96000, |
642 | | bits_per_sample: 24, |
643 | | sample_format: SampleFormat::Int, |
644 | | }; |
645 | | |
646 | | // Write `i32` samples, but with at most 24 bits per sample. |
647 | | { |
648 | | let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap(); |
649 | | for s in -128_i32..127 + 1 { |
650 | | writer.write_sample(s * 256 * 256).unwrap(); |
651 | | } |
652 | | writer.finalize().unwrap(); |
653 | | } |
654 | | |
655 | | // Then read them into `i32`. This should extend the sign in the correct |
656 | | // manner. |
657 | | { |
658 | | buffer.set_position(0); |
659 | | let mut reader = WavReader::new(&mut buffer).unwrap(); |
660 | | assert_eq!(write_spec, reader.spec()); |
661 | | assert_eq!(reader.len(), 256); |
662 | | for (expected, read) in (-128_i32..127 + 1) |
663 | | .map(|x| x * 256 * 256) |
664 | | .zip(reader.samples()) { |
665 | | assert_eq!(expected, read.unwrap()); |
666 | | } |
667 | | } |
668 | | } |
669 | | #[test] |
670 | | fn write_read_f32_is_lossless() { |
671 | | let mut buffer = io::Cursor::new(Vec::new()); |
672 | | let write_spec = WavSpec { |
673 | | channels: 2, |
674 | | sample_rate: 44100, |
675 | | bits_per_sample: 32, |
676 | | sample_format: SampleFormat::Float, |
677 | | }; |
678 | | |
679 | | { |
680 | | let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap(); |
681 | | for s in 1_u32..257 { |
682 | | writer.write_sample(1.0f32 / s as f32).unwrap(); |
683 | | } |
684 | | writer.finalize().unwrap(); |
685 | | } |
686 | | |
687 | | { |
688 | | buffer.set_position(0); |
689 | | let mut reader = WavReader::new(&mut buffer).unwrap(); |
690 | | assert_eq!(write_spec, reader.spec()); |
691 | | assert_eq!(reader.len(), 256); |
692 | | for (expected, read) in (1..257) |
693 | | .map(|x| 1.0_f32 / x as f32) |
694 | | .zip(reader.samples()) { |
695 | | assert_eq!(expected, read.unwrap()); |
696 | | } |
697 | | } |
698 | | } |
699 | | |
700 | | #[test] |
701 | | #[should_panic] |
702 | | fn no_32_bps_for_float_sample_format_panics() { |
703 | | let mut buffer = io::Cursor::new(Vec::new()); |
704 | | let write_spec = WavSpec { |
705 | | channels: 2, |
706 | | sample_rate: 44100, |
707 | | bits_per_sample: 16, // will panic, because value must be 32 for floating point |
708 | | sample_format: SampleFormat::Float, |
709 | | }; |
710 | | |
711 | | WavWriter::new(&mut buffer, write_spec).unwrap(); |
712 | | } |
713 | | |
714 | | #[test] |
715 | | fn flush_should_produce_valid_file() { |
716 | | use std::mem; |
717 | | use std::io::Seek; |
718 | | |
719 | | let mut buffer = io::Cursor::new(Vec::new()); |
720 | | let samples = &[2, 4, 5, 7, 11, 13]; |
721 | | |
722 | | { |
723 | | let spec = WavSpec { |
724 | | channels: 2, |
725 | | sample_rate: 44100, |
726 | | bits_per_sample: 16, |
727 | | sample_format: SampleFormat::Int, |
728 | | }; |
729 | | let mut writer = WavWriter::new(&mut buffer, spec).unwrap(); |
730 | | |
731 | | for &x in samples { |
732 | | writer.write_sample(x).unwrap(); |
733 | | } |
734 | | |
735 | | // We should be able to see everything up to the flush later. |
736 | | writer.flush().unwrap(); |
737 | | |
738 | | // Write more samples. These should be in the buffer, but not read by the |
739 | | // reader if we don't finalize the writer. |
740 | | writer.write_sample(17).unwrap(); |
741 | | writer.write_sample(19).unwrap(); |
742 | | |
743 | | mem::forget(writer); |
744 | | } |
745 | | |
746 | | buffer.seek(io::SeekFrom::Start(0)).unwrap(); |
747 | | |
748 | | let mut reader = WavReader::new(&mut buffer).unwrap(); |
749 | | let read_samples: Vec<i16> = reader.samples() |
750 | | .map(|r| r.unwrap()) |
751 | | .collect(); |
752 | | |
753 | | // We expect to see all samples up to the flush, but not the later ones. |
754 | | assert_eq!(&read_samples[..], &samples[..]); |
755 | | } |
756 | | |
757 | | #[test] |
758 | | fn new_append_should_append() { |
759 | | use std::io::Seek; |
760 | | |
761 | | let mut buffer = io::Cursor::new(Vec::new()); |
762 | | let samples = &[2, 5, 7, 11]; |
763 | | let spec = WavSpec { |
764 | | channels: 2, |
765 | | sample_rate: 44100, |
766 | | bits_per_sample: 16, |
767 | | sample_format: SampleFormat::Int, |
768 | | }; |
769 | | |
770 | | // Write initial file. |
771 | | { |
772 | | let mut writer = WavWriter::new(&mut buffer, spec).unwrap(); |
773 | | for s in samples { writer.write_sample(*s).unwrap(); } |
774 | | } |
775 | | |
776 | | buffer.seek(io::SeekFrom::Start(0)).unwrap(); |
777 | | |
778 | | // Append samples (the same ones a second time). |
779 | | { |
780 | | let mut writer = WavWriter::new_append(&mut buffer).unwrap(); |
781 | | assert_eq!(writer.spec(), spec); |
782 | | for s in samples { writer.write_sample(*s).unwrap(); } |
783 | | } |
784 | | |
785 | | buffer.seek(io::SeekFrom::Start(0)).unwrap(); |
786 | | |
787 | | let mut reader = WavReader::new(&mut buffer).unwrap(); |
788 | | let read_samples: Vec<i16> = reader.samples() |
789 | | .map(|r| r.unwrap()) |
790 | | .collect(); |
791 | | |
792 | | // We expect to see all samples up to the flush, but not the later ones. |
793 | | assert_eq!(&read_samples[..], &[2, 5, 7, 11, 2, 5, 7, 11]); |
794 | | } |
795 | | |
796 | | #[test] |
797 | | fn new_append_does_not_corrupt_files() { |
798 | | use std::io::Read; |
799 | | use std::fs; |
800 | | |
801 | | let sample_files = [ |
802 | | "testsamples/pcmwaveformat-16bit-44100Hz-mono-extra.wav", |
803 | | "testsamples/pcmwaveformat-16bit-44100Hz-mono.wav", |
804 | | "testsamples/pcmwaveformat-8bit-44100Hz-mono.wav", |
805 | | "testsamples/pop.wav", |
806 | | "testsamples/waveformatex-16bit-44100Hz-mono-extra.wav", |
807 | | "testsamples/waveformatex-16bit-44100Hz-mono.wav", |
808 | | "testsamples/waveformatex-16bit-44100Hz-stereo.wav", |
809 | | "testsamples/waveformatextensible-24bit-192kHz-mono.wav", |
810 | | "testsamples/waveformatextensible-32bit-48kHz-stereo.wav", |
811 | | "testsamples/nonstandard-01.wav", |
812 | | "testsamples/nonstandard-02.wav", |
813 | | "testsamples/waveformatex-8bit-11025Hz-mono.wav", |
814 | | ]; |
815 | | |
816 | | for fname in &sample_files { |
817 | | print!("testing {} ... ", fname); |
818 | | |
819 | | let mut buffer = Vec::new(); |
820 | | let mut f = fs::File::open(fname).unwrap(); |
821 | | f.read_to_end(&mut buffer).unwrap(); |
822 | | |
823 | | let samples_orig: Vec<i32>; |
824 | | let samples_after: Vec<i32>; |
825 | | |
826 | | // Read samples first. |
827 | | let mut cursor = io::Cursor::new(buffer); |
828 | | { |
829 | | let mut reader = WavReader::new(&mut cursor).unwrap(); |
830 | | samples_orig = reader.samples().map(|r| r.unwrap()).collect(); |
831 | | } |
832 | | buffer = cursor.into_inner(); |
833 | | |
834 | | // Open in append mode and append one sample. |
835 | | let mut cursor = io::Cursor::new(buffer); |
836 | | { |
837 | | let mut writer = WavWriter::new_append(&mut cursor).unwrap(); |
838 | | writer.write_sample(41_i8).unwrap(); |
839 | | writer.write_sample(43_i8).unwrap(); |
840 | | } |
841 | | buffer = cursor.into_inner(); |
842 | | |
843 | | { |
844 | | let cursor = io::Cursor::new(buffer); |
845 | | let mut reader = WavReader::new(cursor) |
846 | | .expect("Reading wav failed after append."); |
847 | | samples_after = reader.samples().map(|r| r.unwrap()).collect(); |
848 | | } |
849 | | |
850 | | assert_eq!(&samples_orig[..], &samples_after[..samples_orig.len()]); |
851 | | assert_eq!(samples_after[samples_after.len() - 2], 41_i32); |
852 | | assert_eq!(samples_after[samples_after.len() - 1], 43_i32); |
853 | | |
854 | | println!("ok"); |
855 | | } |
856 | | } |
857 | | |
858 | | #[cfg(test)] |
859 | | fn assert_contents(fname: &str, expected: &[i16]) { |
860 | | let mut reader = WavReader::open(fname).unwrap(); |
861 | | let samples: Vec<i16> = reader.samples().map(|s| s.unwrap()).collect(); |
862 | | assert_eq!(&samples[..], expected); |
863 | | } |
864 | | |
865 | | #[test] |
866 | | fn append_works_on_files() { |
867 | | use std::fs; |
868 | | |
869 | | let spec = WavSpec { |
870 | | channels: 1, |
871 | | sample_rate: 44100, |
872 | | bits_per_sample: 16, |
873 | | sample_format: SampleFormat::Int, |
874 | | }; |
875 | | |
876 | | let mut writer = WavWriter::create("append.wav", spec).unwrap(); |
877 | | writer.write_sample(11_i16).unwrap(); |
878 | | writer.write_sample(13_i16).unwrap(); |
879 | | writer.write_sample(17_i16).unwrap(); |
880 | | writer.finalize().unwrap(); |
881 | | |
882 | | assert_contents("append.wav", &[11, 13, 17]); |
883 | | |
884 | | let len = fs::metadata("append.wav").unwrap().len(); |
885 | | |
886 | | let mut appender = WavWriter::append("append.wav").unwrap(); |
887 | | |
888 | | appender.write_sample(19_i16).unwrap(); |
889 | | appender.write_sample(23_i16).unwrap(); |
890 | | appender.finalize().unwrap(); |
891 | | |
892 | | // We appended four bytes of audio data (2 16-bit samples), so the file |
893 | | // should have grown by 4 bytes. |
894 | | assert_eq!(fs::metadata("append.wav").unwrap().len(), len + 4); |
895 | | |
896 | | assert_contents("append.wav", &[11, 13, 17, 19, 23]); |
897 | | } |
898 | | |
899 | | #[cfg(test)] |
900 | | #[test] |
901 | | fn test_into_header_for_infinite_file() { |
902 | | let spec = WavSpec { |
903 | | bits_per_sample: 16, |
904 | | channels: 1, |
905 | | sample_format: SampleFormat::Int, |
906 | | sample_rate: 16000, |
907 | | }; |
908 | | let v = spec.into_header_for_infinite_file(); |
909 | | assert_eq!(&v[..], &b"RIFF\xFF\xFF\xFF\xFFWAVE\ |
910 | | fmt \x10\x00\x00\x00\x01\x00\x01\x00\x80\x3e\x00\x00\x00\x7d\x00\x00\x02\x00\x10\x00\ |
911 | | data\xFF\xFF\xFF\xFF"[..]); |
912 | | |
913 | | let spec = WavSpec { |
914 | | bits_per_sample: 16, |
915 | | channels: 10, |
916 | | sample_format: SampleFormat::Int, |
917 | | sample_rate: 16000, |
918 | | }; |
919 | | let v = spec.into_header_for_infinite_file(); |
920 | | assert_eq!(&v[..], &b"RIFF\xFF\xFF\xFF\xFFWAVE\ |
921 | | fmt \x28\x00\x00\x00\xfe\xff\x0a\x00\x80\x3e\x00\x00\x00\xe2\x04\x00\ |
922 | | \x14\x00\x10\x00\x16\x00\x10\x00\xff\x03\x00\x00\x01\x00\x00\x00\ |
923 | | \x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71\ |
924 | | data\xFF\xFF\xFF\xFF"[..]); |
925 | | } |