nord-cli 0.6.0

Your Nord from the terminal: inspect, edit and move the sounds in Nord files and on a connected instrument
//! The WAVs the encoders take, refused where the file is named.
//!
//! `sample encode`, `sample build` and `piano build` all turn recordings into encoded
//! strokes, and what a stroke can carry is a property of the formats rather than of a
//! verb. Reading them here refuses the file that is wrong by name, rather than inside a
//! build that has already read every other one.

use std::path::Path;

use nord_format::wav::Pcm16;

/// One WAV as an encoder takes it: 16-bit PCM, mono or stereo.
///
/// ⚠️ A stereo file becomes a stereo stroke — both channels under one header — and
/// neither format has a stroke that holds more than two.
pub fn pcm16(path: &Path) -> Result<Pcm16, String> {
    let named = |e: &dyn std::fmt::Display| format!("{}: {e}", path.display());
    let bytes = std::fs::read(path).map_err(|e| named(&e))?;
    let source = nord_format::wav::read_pcm16(&bytes).map_err(|e| named(&e))?;
    if source.channels != 1 && source.channels != 2 {
        return Err(named(&format!(
            "{} channels — a stroke holds one channel or two and nothing else",
            source.channels
        )));
    }
    Ok(source)
}

#[cfg(test)]
mod tests {
    use super::*;

    /// The channel count is refused as the file is read, not after a build has taken
    /// every other WAV in the directory.
    #[test]
    fn a_wav_with_more_than_two_channels_is_refused_by_name() {
        let dir = crate::edit::tests::scratch("wav-channels");
        let path = dir.join("quad.wav");
        let quad = nord_format::wav::pcm16(&[0i16; 16], 44_100, 4).unwrap();
        std::fs::write(&path, quad).unwrap();

        let err = pcm16(&path).unwrap_err();
        assert!(err.contains("quad.wav"), "{err}");
        assert!(err.contains("4 channels"), "{err}");

        let stereo = nord_format::wav::pcm16(&[0i16; 16], 44_100, 2).unwrap();
        std::fs::write(&path, stereo).unwrap();
        assert_eq!(pcm16(&path).unwrap().channels, 2);
        std::fs::remove_dir_all(&dir).unwrap();
    }
}