#wav #u24 #resampler #arg #writer #语言

bin+lib rustwav

WAV format audio file parser and creator, with resampler, can change the sample rate of the audio file, and a lot of sub-format support e.g. ADPCM, aLaw, MuLaw, mp3, opus, flac, etc.

1 unstable release

new 0.0.12 Apr 28, 2025

#165 in Audio

LGPL-2.1

615KB
13K SLoC

RustWAV

I was dissatisfied with the hound library - its API was poor, functionality limited, and iterator implementation subpar. Thus, I decided to reinvent the WAV wheel myself.

Language 语言

English | 简体中文

Features

Audio Reader:

  • Supports reading WAV audio files over 4GB in size.
  • Supports embedded formats including PCM, PCM-aLaw, PCM-muLaw, ADPCM-MS, ADPCM-IMA, ADPCM-YAMAHA, MP3, Opus, etc.
  • Resampler support assists in modifying sample rates.
  • Supports reading WAV files with randomly distributed Chunk storage.
  • Generates corresponding iterators via generic parameters to retrieve audio frames, with sample formats in each frame strictly converted to specified generic types according to their numerical ranges.
    • Supported generic types: i8, i16, i24, i32, i64, u8, u16, u24, u32, u64, f32, f64
    • Regardless of original audio storage format, iterators can convert to the above generic formats.
    • No conversion occurs when original format matches the specified generic type.
  • Reads music metadata information:
    • Special handling for system-specific string encodings (e.g., Code Page 936/GB2312 on Windows):
      • On Windows builds, calls GetACP() to detect code page, retrieves corresponding encoding, and converts to UTF-8 using the encoding crate.
    • Supports ID3 metadata.
  • Allows creating audio readers using any Read + Seek trait implementer as input. In this mode, a temporary file stores the audio's data section.
    • The temporary file will be deleted when the WaveReader drops.
    • No temporary files created when using file paths to initialize readers.
  • No panic! except for explicit parameter errors.

Audio Writer

  • Supports writing WAV audio files over 4GB in size.
  • Supports embedded formats including PCM, PCM-aLaw, PCM-muLaw, ADPCM-MS, ADPCM-IMA, ADPCM-YAMAHA, MP3, Opus, etc.
  • write_frame() function accepts generic parameters, encoding input samples for storage.
  • Writes music metadata and can copy all metadata from other audio readers.
  • No panic! except for explicit parameter errors.

Other Features

  • Supports channel configurations including but not limited to:

    • FrontLeft
    • FrontRight
    • FrontCenter
    • LowFreq
    • BackLeft
    • BackRight
    • FrontLeftOfCenter
    • FrontRightOfCenter
    • BackCenter
    • SideLeft
    • SideRight
    • TopCenter
    • TopFrontLeft
    • TopFrontCenter
    • TopFrontRight
    • TopBackLeft
    • TopBackCenter
    • TopBackRight
  • Most internal structs support direct dbg!() output.

Usage Example

use sampleutils::{SampleType, SampleFrom, i24, u24};
use readwrite::{Reader, Writer};
use wavcore::{Spec, SampleFormat, DataFormat, AdpcmSubFormat};
use wavreader::{WaveDataSource, WaveReader, FrameIter, StereoIter, MonoIter};
use wavwriter::{FileSizeOption, WaveWriter};
use resampler::Resampler;
use errors::{AudioReadError, AudioError, AudioWriteError};

use std::env::args;
use std::error::Error;
use std::process::ExitCode;

const FORMATS: [(&str, DataFormat); 8] = [
        ("pcm", DataFormat::Pcm),
        ("pcm-alaw", DataFormat::PcmALaw),
        ("pcm-ulaw", DataFormat::PcmMuLaw),
        ("adpcm-ms", DataFormat::Adpcm(AdpcmSubFormat::Ms)),
        ("adpcm-ima", DataFormat::Adpcm(AdpcmSubFormat::Ima)),
        ("adpcm-yamaha", DataFormat::Adpcm(AdpcmSubFormat::Yamaha)),
        ("mp3", DataFormat::Mp3),
        ("opus", DataFormat::Opus),
];

#[allow(unused_imports)]
use FileSizeOption::{NeverLargerThan4GB, AllowLargerThan4GB, ForceUse4GBFormat};

fn transfer_audio_from_decoder_to_encoder(decoder: &mut WaveReader, encoder: &mut WaveWriter) {
    // The fft size can be any number greater than the sample rate of the encoder or the decoder.
    // It is for the resampler. A greater number results in better resample quality, but the process could be slower.
    // In most cases, the audio sampling rate is about 11025 to 48000, so 65536 is the best number for the resampler.
    const FFT_SIZE: usize = 65536;

    // This is the resampler, if the decoder's sample rate is different than the encode sample rate, use the resampler to help stretch or compress the waveform.
    // Otherwise, it's not needed there.
    let mut resampler = Resampler::new(FFT_SIZE);

    // The decoding audio spec
    let decode_spec = *decoder.spec();

    // The encoding audio spec
    let encode_spec = *encoder.spec();

    // The number of channels must match
    assert_eq!(encode_spec.channels, decode_spec.channels);

    // Process size is for the resampler to process the waveform, it is the length of the source waveform slice.
    let process_size = resampler.get_process_size(FFT_SIZE, decode_spec.sample_rate, encode_spec.sample_rate);

    // There are three types of iterators for three types of audio channels: mono, stereo, and more than 2 channels of audio.
    // Usually, the third iterator can handle all numbers of channels, but it's the slowest iterator.
    match encode_spec.channels {
        1 => {
            let mut iter = decoder.mono_iter::<f32>().unwrap();
            loop {
                let block: Vec<f32> = iter.by_ref().take(process_size).collect();
                if block.is_empty() {
                    break;
                }
                let block = utils::do_resample_mono(&mut resampler, &block, decode_spec.sample_rate, encode_spec.sample_rate);
                encoder.write_monos(&block).unwrap();
            }
        },
        2 => {
            let mut iter = decoder.stereo_iter::<f32>().unwrap();
            loop {
                let block: Vec<(f32, f32)> = iter.by_ref().take(process_size).collect();
                if block.is_empty() {
                    break;
                }
                let block = utils::do_resample_stereo(&mut resampler, &block, decode_spec.sample_rate, encode_spec.sample_rate);
                encoder.write_stereos(&block).unwrap();
            }
        },
        _ => {
            let mut iter = decoder.frame_iter::<f32>().unwrap();
            loop {
                let block: Vec<Vec<f32>> = iter.by_ref().take(process_size).collect();
                if block.is_empty() {
                    break;
                }
                let block = utils::do_resample_frames(&mut resampler, &block, decode_spec.sample_rate, encode_spec.sample_rate);
                encoder.write_frames(&block).unwrap();
            }
        }
    }
}

// The `test()` function
// arg1: the format, e.g. "pcm"
// arg2: the input file to parse and decode, tests the decoder for the input file.
// arg3: the output file to encode, test the encoder.
// arg4: re-decode arg3 and encode to pcm to test the decoder.
fn test(arg1: &str, arg2: &str, arg3: &str, arg4: &str) -> Result<(), Box<dyn Error>> {
    let mut data_format = DataFormat::Unspecified;
    for format in FORMATS {
        if arg1 == format.0 {
            data_format = format.1;
            break;
        }
    }

    // Failed to match the data format
    if data_format == DataFormat::Unspecified {
        return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("Unknown format `{arg1}`. Please input one of these:\n{}", FORMATS.iter().map(|(s, _v)|{s.to_string()}).collect::<Vec<String>>().join(", "))).into());
    }

    println!("======== TEST 1 ========");

    // This is the decoder
    let mut wavereader = WaveReader::open(arg2).unwrap();

    let orig_spec = *wavereader.spec();

    // The spec for the encoder
    let spec = Spec {
        channels: orig_spec.channels,
        channel_mask: 0,
        sample_rate: 48000, // Specify a sample rate to test the resampler
        bits_per_sample: 16,
        sample_format: SampleFormat::Int,
    };

    // This is the encoder
    let mut wavewriter = WaveWriter::create(arg3, &spec, data_format, NeverLargerThan4GB).unwrap();

    // Transfer audio samples from the decoder to the encoder
    transfer_audio_from_decoder_to_encoder(&mut wavereader, &mut wavewriter);

    // Get the metadata from the decoder
    wavewriter.inherit_metadata_from_reader(&wavereader);

    // It's not needed to call `finalize()` after use, but calling it will free the memory and resources immediately.
    wavewriter.finalize();

    // Show debug info
    dbg!(&wavereader);
    dbg!(&wavewriter);

    println!("======== TEST 2 ========");

    let spec2 = Spec {
        channels: spec.channels,
        channel_mask: 0,
        sample_rate: 44100, // Changed to another sample rate to test the resampler.
        bits_per_sample: 16,
        sample_format: SampleFormat::Int,
    };

    let mut wavereader_2 = WaveReader::open(arg3).unwrap();
    let mut wavewriter_2 = WaveWriter::create(arg4, &spec2, DataFormat::Pcm, NeverLargerThan4GB).unwrap();

    // Transfer audio samples from the decoder to the encoder
    transfer_audio_from_decoder_to_encoder(&mut wavereader_2, &mut wavewriter_2);

    // Get the metadata from the decoder
    wavewriter_2.inherit_metadata_from_reader(&wavereader_2);

    // It's not needed to call `finalize()` after use, but calling it will free the memory and resources immediately.
    wavewriter_2.finalize();

    // Show debug info
    dbg!(&wavereader_2);
    dbg!(&wavewriter_2);

    Ok(())
}

#[test]
fn testrun() {
    for format in FORMATS {
        test(format.0, "test.wav", "output.wav", "output2.wav").unwrap();
    }
}

fn main() -> ExitCode {
    let args: Vec<String> = args().collect();
    if args.len() < 5 {return ExitCode::from(1);}

    match test(&args[1], &args[2], &args[3], &args[4]) {
        Ok(_) => ExitCode::from(0),
        Err(e) => {
            println!("Error: {}", e);
            ExitCode::from(2)
        },
    }
}

Dependencies

~8–47MB
~737K SLoC