hound

repository·release·Indexed 20 days ago

https://github.com/ruuda/hound

A Rust library for encoding and decoding the WAVE audio format, providing tools to read and write uncompressed, raw audio files. It supports various bit depths and sample formats (PCM and IEEE Float) through WavReader and WavWriter, including capabilities for seeking, appending to existing files, and high-performance 16-bit integer writing.

Tokens
5K
Snippets
24
Records
26
Agent score
70%

What's inside hound

  1. Read a WAV file with WavReader

    release

    To decode audio data from a WAV file, use hound::WavReader. You can iterate over samples using the .samples::<T>() method, where T is a type implementing the Sample trait (e.g., i16, i32, f32).

    Note that the type T must be compatible with the file's bit depth and SampleFormat to avoid InvalidSampleFormat or TooWide errors.

    use hound;
    
    let mut reader = hound::WavReader::open("testsamples/pop.wav").unwrap();
    let sqr_sum = reader.samples::<i16>()
                        .fold(0.0, |sqr_sum, s| {
        let sample = s.unwrap() as f64;
        sqr_sum + sample * sample
    });
    println!("RMS is {}", (sqr_sum / reader.len() as f64).sqrt());
  2. Append samples to an existing WAVE file

    release

    You can append audio data to an existing WAVE file using WavWriter::append (for files) or WavWriter::new_append (for generic writers). This reads the existing header to determine the specification and seeks to the end of the data chunk before writing.

    // Appending to a file on disk
    let mut writer = WavWriter::append("existing.wav")?;
    
    // Appending to an existing stream (must implement Read + Write + Seek)
    // let mut writer = WavWriter::new_append(&mut my_stream)?;
  3. Create a new WAVE file with `WavWriter::create`

    release

    Use WavWriter::create to create a new WAVE file at a specified path. This method automatically wraps the file in a BufWriter for efficiency. The file will be overwritten if it already exists.

    let spec = WavSpec {
        channels: 2,
        sample_rate: 44100,
        bits_per_sample: 16,
        sample_format: SampleFormat::Int,
    };
    let mut writer = WavWriter::create("output.wav", spec)?;
  4. Append samples to an existing WAV file

    release

    If you need to add audio data to the end of an existing WAV file, use WavWriter::append(path) or WavWriter::new_append(&mut cursor). This method reads the existing header and allows you to continue writing samples. Remember to call finalize() to update the file's metadata with the new total size.

    // Appending to a file on disk
    let mut appender = hound::WavWriter::append("append.wav").unwrap();
    appender.write_sample(19_i16).unwrap();
    appender.finalize().unwrap();
  5. Write samples to a WAVE file using `WavWriter`

    release

    To write audio data, use the write_sample method. Samples are interleaved across channels. The number of samples written must be a multiple of the number of channels.

    Important: You must call finalize() to correctly update the WAVE headers (file size and data chunk length). While WavWriter will attempt to finalize on drop, any errors during that process (like an unfinished sample) will be silently ignored. Always call finalize() explicitly to catch errors.

    // Assuming writer is a WavWriter instance
    writer.write_sample(sample_value)?;
    
    // Finalize to ensure headers are updated and errors are caught
    writer.finalize()?;
  6. Open a WAVE file with WavReader::open

    release

    The easiest way to read a WAVE file is to use the WavReader::open convenience constructor. This method opens the specified file, wraps it in a BufReader, and initializes the WavReader by reading the header and format chunks immediately. The actual audio data is read on demand (streaming).

    let mut reader = WavReader::open("path/to/file.wav")?;
  7. Write a WAV file with WavWriter

    release

    To write a WAV file, use hound::WavWriter::create(path, spec). You must provide a hound::WavSpec defining the audio properties. After writing samples using write_sample(), the file is finalized implicitly when the writer is dropped. To catch potential errors during the finalization process (like disk space issues), call writer.finalize() explicitly.

    use std::f32::consts::PI;
    use std::i16;
    use hound;
    
    let spec = hound::WavSpec {
        channels: 1,
        sample_rate: 44100,
        bits_per_sample: 16,
        sample_format: hound::SampleFormat::Int,
    };
    let mut writer = hound::WavWriter::create("sine.wav", spec).unwrap();
    for t in (0 .. 44100).map(|x| x as f32 / 44100.0) {
        let sample = (t * 440.0 * 2.0 * PI).sin();
        let amplitude = i16::MAX as f32;
        writer.write_sample((sample * amplitude) as i16).unwrap();
    }
  8. Read samples from a WAV file with WavReader

    release

    To read a WAV file, use hound::WavReader::open(path). You can iterate over samples by calling .samples::<T>(), where T is the type of the sample (e.g., i16, i32, f32). The reader.len() method provides the total number of samples in the file.

    use hound;
    
    let mut reader = hound::WavReader::open("testsamples/pop.wav").unwrap();
    let sqr_sum = reader.samples::<i16>()
                        .fold(0.0, |sqr_sum, s| {
        let sample = s.unwrap() as f64;
        sqr_sum + sample * sample
    });
    println!("RMS is {}", (sqr_sum / reader.len() as f64).sqrt());
  9. Supported WAV formats and encodings

    release

    Hound supports the following formats and encodings for reading and writing WAVE audio:

    |                 | Read                                                    | Write                                   |
    |-----------------|---------------------------------------------------------|-----------------------------------------|
    | Format          | `PCMWAVEFORMAT`, `WAVEFORMATEX`, `WAVEFORMATEXTENSIBLE` | `PCMWAVEFORMAT`, `WAVEFORMATEXTENSIBLE` |
    | Encoding         | Integer PCM, IEEE Float                                 | Integer PCM, IEEE Float                 |
    | Bits per sample | 8, 16, 24, 32 (integer), 32 (float)                     | 8, 16, 24, 32 (integer), 32 (float)     |
  10. Handle `WavWriter` errors and unfinished samples

    release

    When working with WavWriter, be aware of these specific error conditions:

    • Error::UnfinishedSample: Returned by finalize() or flush() if the number of samples written is not a multiple of the channel count.
    • Error::Unsupported: Returned if you attempt to use a bit depth not supported by Hound (only 8, 16, 24, and 32 bits are supported).
    • Error::FormatError: Returned during append operations if the existing data chunk length is not a multiple of the sample size.
  11. Use `SampleWriter16` for high-performance 16-bit integer writing

    release

    If you are writing 16-bit integer samples, WavWriter::get_i16_writer provides a specialized, high-performance writer. It uses an internal buffer to batch writes, reducing the overhead of individual io::Write calls and eliminating many dynamic checks.

    Usage requirements:

    • The WavSpec must have sample_format: SampleFormat::Int and bits_per_sample: 16.
    • You must specify the exact num_samples you intend to write. Attempting to write more will cause a panic.
    • You must call flush() on the SampleWriter16 to commit the buffered samples to the underlying WavWriter.
    // Get a specialized writer for a specific number of samples
    let mut i16_writer = writer.get_i16_writer(num_samples);
    
    for i in 0..num_samples {
        i16_writer.write_sample(sample_value);
    }
    
    // Flush the internal buffer to the WavWriter
    i16_writer.flush()?;
  12. Consume WavReader with into_samples

    release

    If you want to take ownership of the WavReader and consume it while iterating, use into_samples::<S>(). This is functionally similar to samples::<S>() but moves the reader instead of borrowing it.

    let reader = WavReader::open("audio.wav")?;
    let samples: Vec<i16> = reader.into_samples::<i16>()
        .map(|r| r.unwrap())
        .collect();