A wav encoding and decoding library in Rust

Overview

Hound

A wav encoding and decoding library in Rust.

Build Status Crates.io version Changelog Documentation

Hound can read and write the WAVE audio format, an ubiquitous format for raw, uncompressed audio. The main motivation to write it was to test Claxon, a FLAC decoding library written in Rust.

Examples

The following example renders a 440 Hz sine wave, and stores it as a mono wav file with a sample rate of 44.1 kHz and 16 bits per sample.

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();
}

The file is finalized implicitly when the writer is dropped, call writer.finalize() to observe errors.

The following example computes the root mean square (RMS) of an audio file with at most 16 bits per sample.

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());

Features

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)

Contributing

Contributions in the form of bug reports, feature requests, or pull requests are welcome. See contributing.md.

License

Hound is licensed under the Apache 2.0 license. It may be used in free software as well as closed-source applications, both for commercial and non-commercial use under the conditions given in the license. If you want to use Hound in your GPLv2-licensed software, you can add an exception to your copyright notice. Please do not open an issue if you disagree with the choice of license.

Comments
  • cpal.rs example does not build using cargo test

    cpal.rs example does not build using cargo test

    I've never used the cpal library but it seems that the version of cpal that hound is currently tied to is quite out of date. The cargo.toml specifies v0.1.2 where the latest version is v0.4.0. When trying to build and run the test target for hound compilation failed due to the dependency on cpal failing to build.

    I believe the cpal example either needs to be removed or updated to work with the current version of cpal.

    opened by zyvitski 14
  • basic support for arbitrary chunks

    basic support for arbitrary chunks

    Hello there, this is an extension to support writting and reading arbitrary chunks between "fmt " and "data".

    Let me know what you think.

    (By the way, I think there is a tiny bug in skip_bytes call while looking for the data chunk. As far as I understand it, RIFF chunks must be aligned to even offsets so there might be a padding zero byte... This patch actually fixes it.)

    opened by kali 12
  • How to append to existing wav without reading the wav data first?

    How to append to existing wav without reading the wav data first?

    I want to record an audio stream from a live performance to wav, in chunks every few seconds. How to append to an existing wav file with hound without having to read the existing wav data first? (The recording session could be many hours long.)

    opened by Boscop 10
  • Add `WavReader::seek_sample` method

    Add `WavReader::seek_sample` method

    This adds a method to WavReader that allows the user to seek to a given sample position within the file.

    This method requires that the inner reader R implements io::Seek.

    The given sample position represents the number of samples from the beginning of the audio data. Thus the given sample position should not exceed the length of the file in samples (returned by len()). The behaviour when seeking beyond len() depends on the reader's Seek implementation.

    A test for the new method is included at the bottom of the module.

    This closes #18.

    opened by mitchmindtree 10
  • Add support for reading wav files with the WAVE_FORMAT_IEEE_FLOAT format.

    Add support for reading wav files with the WAVE_FORMAT_IEEE_FLOAT format.

    I'm using hound to load samples for a sampler instrument I'm working on. I noticed that about a third of my sample library was failing to load as many wav files were formatted with IEEE_FLOAT data, so I thought I'd have a go at implementing support.

    This only adds working support for reading the float format so far. It does add a WriterExt::write_le_f32 in order to complete Sample implementation for f32, however functionality for writing IEEE_FLOAT waves is not yet implemented.

    This also adds a SampleFormat type and a sample_format field to the WavSpec in order to help the user distinguish between whether the sample is formatted as float or integer data.

    I'll try to add a test .wav file with a couple tests over the next couple of days if I can find the time.

    @ruuda it would be great if you could have a look over this and let me know your thoughts, whether or not your interested, if you'd like me to change the anything, etc. I really appreciate your work on hound - super nice and simple API and the src has been very easy to read through :)

    opened by mitchmindtree 7
  • WavReader may be too strict on WAVEFORMATEX data

    WavReader may be too strict on WAVEFORMATEX data

    At least one of the WAV files used by Quake (pak0.pak/sound/doors/baseuse.wav) results in FormatError("unexpected WAVEFORMATEX size") when trying to open it. This file opens and plays successfully in mplayer, VLC and mpv.

    Using hound 3.3.0:

    extern crate hound;
    
    fn main() {
        let _ = hound::WavReader::open("baseuse.wav").unwrap();
    }
    

    Output:

    $ cargo run
    (...)
    thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: \
        FormatError("unexpected WAVEFORMATEX size")', libcore/result.rs:916:5
    

    Perhaps this should be a warning instead of a fatal error?

    (This is using pak0.pak with md5sum 85fc9cee2035b66290da1e33be2ac86b)

    opened by cormac-obrien 6
  • Add WASM Support.

    Add WASM Support.

    My understanding is that we need to strip out certain libraries that are unavailable with wasm bindings such as fs. Any idea what else we could remove for this? I want to be able to quickly encode/decode/process wav files in the browser. Does anyone else think wasm support would be a worthwhile contribution?

    opened by noahdahlman 4
  • Smpl chunk: play loops

    Smpl chunk: play loops

    Hello, I've come to your library thanks to rodio and cpal. I need to play wave files that contain 'smpl' chunk and play forever the loop stored in this chunk (forever, ie, until the user wants to stop the sound). I've figured out how to parse this chunk (it's pretty easy) but don't know how to perform the loop. I don't know it you're interested in implementing this kind of features, but can you help me and just say where I should start? Thanks in advance,

    Philippe

    NB: my informations about the smpl chunk comes from here: http://www.piclist.com/techref/io/serial/midi/wave.html

    opened by paucazou 4
  • Incorrectly read wav-file with 1 channel and 8 bits per sample

    Incorrectly read wav-file with 1 channel and 8 bits per sample

    I'm trying to read a wav file with this spec: WavSpec { channels: 1, sample_rate: 8000, bits_per_sample: 8, sample_format: Int } - one channel, 8 bits per sample, so each sample should fit in u8.

    I read it like this (which very well may be incorrect since I'm not very good in Rust):

        let samples: Vec<u8> = wavreader.samples()
            .map(|s: Result<i32, hound::Error>| s.unwrap() as u8)
            .collect();
    

    and then I print it:

        for i in (0..samples.len()).step_by(11) {
            for j in 0..11 {
                print!("0x{:02x}, ", samples[i+j]);
            }
            println!();
        }
    

    Here's a piece from the beginning of the file:

    samples
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 
    0xff, 0xff, 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x01, 0x01, 
    0x00, 0xff, 0xfe, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 
    0xfe, 0xfe, 0xff, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 
    0xfc, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 
    0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x03, 0x04, 0x06, 0x07, 0x08, 
    

    This, however, is not equal to what I see in hex editor (since every sample is 8 bit and there's only 1 channel, every byte should be equal to one sample, right?):

    hex editor

    Here's side-by-side comparison, differences are highlighted:

    Looks like most significant bit of every byte is inverted o_o

    Can you please tell me what am I doing wrong? Here's the troublesome file - https://gofile.io/d/s8GPnc

    opened by Amomum 4
  • Add S24_LE wav files and tests

    Add S24_LE wav files and tests

    The second patch in the series gets read_wav_wave_format_extensible_pcm_24bit_4byte() to pass, but the other test will need some deeper changes to work (potentially adding a frame size field which is block_align / num_channels ?)

    opened by fletcherw 4
  • [HELP] How to play a wav file with 2 channels ?

    [HELP] How to play a wav file with 2 channels ?

    我对 wav 格式 以及 音频 不是太熟悉,在多声道音频的处理上遇到些麻烦,我阅读过 项目的样例文件, 但是这些 样例 似乎都是为处理 单声道 音频 准备的。

    那么对于多声道音频应该如何处理呢?

    谢谢。

    Hi, I'm not too familiar with WAV format and audio, I'm having trouble with multi-channel audio processing, I've read the project sample files, but these samples seem to be prepared for single channel audio processing.

    So what should I do with multichannel audio? Thank you very much.

    -- From Bing Translator

    Code:

    #![cfg(any(target_os = "macos", target_os = "ios"))]
    
    extern crate coreaudio;
    extern crate hound;
    
    
    use coreaudio::audio_unit::{ AudioUnit, IOType, SampleFormat };
    use coreaudio::audio_unit::render_callback::{ self, data };
    
    use std::env;
    use std::sync::{Arc, Mutex};
    
    // Usage:
    //      $ ./play sample.wav
    // 
    
    fn main () {
        // WAV File with 2 channels have problem
        let filename = env::args().nth(1).expect("play ./sample.wav");
    
        let wav_reader = hound::WavReader::open(filename).unwrap();
        let wav_spec = wav_reader.spec();
        println!("WAV Reader Spec: {:?}", wav_spec );
    
        let mut audio_unit = AudioUnit::new(IOType::DefaultOutput).unwrap();
        let stream_format = audio_unit.output_stream_format().unwrap();
        println!("CoreAudio Output: {:?}", stream_format);
    
        assert_eq!(wav_spec.channels == 1 || wav_spec.channels == 2, true);
        assert_eq!(wav_spec.bits_per_sample, 16);
        assert_eq!(wav_spec.sample_format, hound::SampleFormat::Int);
    
        assert!(SampleFormat::F32 == stream_format.sample_format);
    
        let mut samples = match wav_spec.channels {
            1 => {
                wav_reader.into_samples::<i16>()
                    .map(|sample| sample.expect("failed to decode WAV stream") as _)
                    .collect::<Vec<i16>>()
                    .into_iter()
            },
            2 => {
                wav_reader.into_samples::<i32>()
                    .map(|sample| sample.expect("failed to decode WAV stream") as _)
                    // .collect::<Vec<i32>>()
                    // .windows(2)
                    .map(|left_and_right_sample: i32| {
                        let left_and_right: [i16; 2] = unsafe { 
                            mem::transmute(left_and_right_sample.to_be())
                        };
                        left_and_right[0]
                    })
                    .collect::<Vec<i16>>()
                    .into_iter()
            },
            _ => unreachable!(),
        };
    
        let stop = Arc::new(Mutex::new(false));
        let stop_clone = stop.clone();
    
        type Args = render_callback::Args<data::NonInterleaved<f32>>;
        audio_unit.set_render_callback(move |args| {
            let Args { num_frames, mut data, .. } = args;
            for i in 0..num_frames {
                let sample: i16 = match samples.next() {
                    Some(sample) => sample,
                    None => {
                        *stop_clone.lock().unwrap() = true;
                        0
                    }
                };
                
                // i16 sample to f32 sample
                let mut sample: f64 = sample as f64 / (std::i16::MAX as f64);
                
                if stream_format.channels_per_frame == 1 {
                    sample /= stream_format.sample_rate;
                }
    
                for channel in data.channels_mut() {
                    channel[i] = sample as f32;
                }
            }
            Ok(())
        }).unwrap();
    
        audio_unit.start().expect("failed to play.");
    
        loop {
            if *stop.lock().unwrap() == true {
                break;
            }
            
            std::thread::sleep(std::time::Duration::from_millis(1000));
        }
    }
    
    
    opened by LuoZijun 4
  • "sample bits exceeds size of sample" with FL Studio samples

    I tried to load FL Studio's "FPC 1 Kick.wav" using hound 3.5.0 but it failed to load with a FormatError sample bits exceeds size of sample.

    VLC also can't open the file, but Audacity can and FL Studio obviously

    Sample file (rename .png to .wav): FPC 1 Kick

    opened by kangalioo 0
  • Getting error

    Getting error "data chunk length is not a multiple of sample size" for some files

    Hello,

    I am getting this error for certain audio files only. The code works fine with other files! Could you point me to the right direction on what might be the cause?

    This is the function that I am using:

    pub fn load_fundsp_wav(path: &str) -> Vec<Arc<fundsp::hacker::Wave64>> {
        let paths = fs::read_dir(path).unwrap();
        let mut paths_vec = Vec::new();
        for path in paths {
            let mut reader = hound::WavReader::open(path.unwrap().path()).unwrap();
            let spec = reader.spec();
            nih_dbg!("{:?}", spec);
    
            let mut samples: Vec<f64> = Vec::new();
            if spec.bits_per_sample == 16 {
                let read = reader.samples().collect::<Result<Vec<i32>, hound::Error>>();
                if let Ok(result) = read {
                    const I16_MAX: i16 = std::i16::MAX;
                    samples = result
                        .iter()
                        .map(|val| *val as f64 / I16_MAX as f64)
                        .collect::<Vec<f64>>();
                }
            } else if spec.bits_per_sample == 24 {
                let read = reader.samples().collect::<Result<Vec<i32>, hound::Error>>();
                if let Ok(result) = read {
                    const I24_MAX: i32 = 2_i32.pow(23) - 1;
                    samples = result
                        .iter()
                        .map(|val| *val as f64 / I24_MAX as f64)
                        .collect::<Vec<f64>>();
                }
            } else {
                nih_error!("There was no compatible file found")
            };
    
            let mut wavefile =
                Wave64::with_capacity(spec.channels.into(), spec.sample_rate.into(), samples.len());
    
            wavefile.resize(samples.len());
    
            for (pos, s) in samples.iter().enumerate() {
                wavefile.set(0, pos, *s);
            }
    
            paths_vec.push(Arc::new(wavefile));
        }
        paths_vec
    }
    
    opened by dathinaios 0
  • Request to cherry pick changes in dot release.

    Request to cherry pick changes in dot release.

    Hello, thank you for taking the time to cherry pick changes for 3.5.0 release. If possible, would you be interested in taking https://github.com/ruuda/hound/pull/58 as well? I didn't see a branch on GitHub corresponding to the released version on https://crates.io/crates/hound, but I have patched the changes and uploaded them to https://github.com/spacecams/hound/tree/release-with-safe-changes. I ran the append and decode_full fuzzers and did not see any crashes. Let me know if I can make any changes or updates that would be helpful. Thanks!

    opened by spacecams 0
  • Replace try! with question mark operator

    Replace try! with question mark operator

    There are a lot of compiler warnings when building hound:

    warning: use of deprecated macro `try`: use the `?` operator instead

    So I tried to fix that. Does this make sense to you?

    opened by yoshinorisano 1
  • Should I normalize when reading int as f32?

    Should I normalize when reading int as f32?

    Hi, thanks for making a great crate.

    I am new to Rust so I may have misunderstood something. This is abount the feature that was added in https://github.com/ruuda/hound/pull/37 .

    When a wav in f32 format is loaded, a number in the range -1.0~+1.0 is expected. However, when reading a wav in 16bit Int format as f32, a number in the range -32678.0~+32767.0 is returned. When reading as f32, I think it is common to return a range of -1.0~+1.0 with normalization. If you think it would be better to normalize, I can help.

    opened by AkiyukiOkayasu 5
  • Mono 44.1 kHz 24 bit wav refuses to load.

    Mono 44.1 kHz 24 bit wav refuses to load.

    Hound v3.4.0

    Running the tool soxi on the wav:

    Input File     : '/media/internal/70sElRaunchoFusionLead C3.wav'
    Channels       : 1
    Sample Rate    : 44100
    Precision      : 24-bit
    Duration       : 00:00:04.89 = 215490 samples = 366.48 CDDA sectors
    File Size      : 651k
    Bit Rate       : 1.07M
    Sample Encoding: 24-bit Signed Integer PCM
    

    Opening this using: let mut reader = hound::WavReader::open(&filename)?; yields:

    IoError(Custom { kind: Other, error: "Failed to read enough bytes." })
    

    The crate 'wav' does load this file without problems.

    opened by pietervandermeer 1
Owner
Ruud van Asseldonk
Ruud van Asseldonk
Program to check if stereo wav files have identical channels (faux-stereo) and convert them to mono.

zrtstr Command line application for checking WAV-files for identical channels, detecting faux-stereo files generated by some audio-editing software an

Kirill 22 Nov 6, 2022
A library and application for lossless, format-preserving, two-pass optimization and repair of Vorbis data, reducing its size without altering any audio information.

OptiVorbis A library and application for lossless, format-preserving, two-pass optimization and repair of Vorbis data, reducing its size without alter

OptiVorbis 27 Jan 3, 2023
A CLI and library to convert data to sound, and vice versa (dependency-free)

Data to sound A simple crate to convert data to sound, and sound to data. The sound file format is wave (.wav). You can use it as a library or as a co

Awiteb 8 Feb 28, 2023
ears is a simple library to play Sounds and Musics in Rust

ears ears is a simple library to play Sounds and Musics in Rust. ears is build on the top of OpenAL and libsndfile. Provides an access to the OpenAL s

Jeremy Letang 56 Dec 1, 2022
A low-overhead and adaptable audio playback library for Rust

Awedio   A low-overhead and adaptable audio playback library for Rust. Examples Play a single sound file: let (mut manager, backend) = awedio::start()

10 Buttons 20 May 25, 2023
A rust binding for the FMOD library

rust-fmod This is a rust binding for FMOD, the library developped by FIRELIGHT TECHNOLOGIES. FMOD website : www.fmod.org You can also find it on crate

Guillaume Gomez 55 Nov 2, 2022
Cross-platform audio I/O library in pure Rust

CPAL - Cross-Platform Audio Library Low-level library for audio input and output in pure Rust. This library currently supports the following: Enumerat

null 1.8k Jan 8, 2023
Rust audio playback library

Audio playback library Rust playback library. Playback is handled by cpal. MP3 decoding is handled by minimp3. WAV decoding is handled by hound. Vorbi

null 1.2k Jan 1, 2023
Rust bindings for the soloud audio engine library

soloud-rs A crossplatform Rust bindings for the soloud audio engine library. Supported formats: wav, mp3, ogg, flac. The library also comes with a spe

Mohammed Alyousef 38 Dec 8, 2022
Rust Sound Synthesis Library

Oscen Oscen [“oh-sin”] is a library for building modular synthesizers in Rust. It contains a collection of components frequently used in sound synthes

Reed Rosenbluth 104 Nov 2, 2022
Small music theory library with MIDI capabilities written in Rust

mumuse Small music theory library with MIDI capabilities written in Rust (wip). Examples Creating notes and transpositions // Declare Note from &str l

Alexis LOUIS 4 Jul 27, 2022
A song analysis library for making playlists

bliss-rs is the Rust improvement of bliss, a library used to make playlists by analyzing songs, and computing distance between them.

null 49 Dec 25, 2022
A library for constructing Groth-Sahai proofs using pre-built wrappers

Groth-Sahai Wrappers A Rust library containing wrappers that facilitate the construction of non-interactive witness-indistinguishable and zero-knowled

Jacob White 1 Mar 7, 2022
Flutter crossplatform audio playback library powered by golang beep & oto

Rowdy Pure Rust based Dart/Flutter audio playback library Installation $ flutter pub add rowdy Configuration You'll need the Rust toolchain installed

Kingkor Roy Tirtho 3 Aug 31, 2022
TinyAudio is a cross-platform audio output library

TinyAudio TinyAudio is a cross-platform audio output library. Its main goal to provide unified access to a default sound output device of your operati

Dmitry Stepanov 39 Apr 4, 2023
Idiomatic Rust bindings for OpenAL 1.1 and extensions (including EFX).

alto alto provides idiomatic Rust bindings for OpenAL 1.1 and extensions (including EFX). WARNING Because Alto interacts with global C state via dynam

null 80 Aug 7, 2022
High-level PortMidi bindings and wrappers for Rust

portmidi-rs High-level PortMidi bindings for Rust. PortMidi website: http://portmedia.sourceforge.net/portmidi/ Installation Add this to your Cargo.to

Philippe Delrieu 69 Dec 1, 2022
PortAudio bindings and wrappers for Rust.

rust-portaudio PortAudio bindings and wrappers for Rust. PortAudio is a free, cross-platform, open-source, audio I/O library. rust-portaudio is still

null 331 Dec 23, 2022
A Rust environment for sound synthesis and algorithmic composition.

Sorceress A Rust environment for sound synthesis and algorithmic composition, powered by SuperCollider. Overview Sorceress is a Rust crate that provid

Wesley Merkel 82 Dec 26, 2022