humster 0.0.2

Modern music toolkit for Rust
Documentation
use std::fs;
use std::time::{SystemTime, UNIX_EPOCH};

fn temp_path(filename: &str) -> std::path::PathBuf {
    let mut path = std::env::temp_dir();
    let nanos = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_nanos();
    path.push(format!("humster_{nanos}_{filename}"));
    path
}

#[cfg(feature = "midi")]
struct VelocityBoost(f32);

#[cfg(feature = "midi")]
impl humster::Processor for VelocityBoost {
    fn process_midi(&self, track: &mut humster::MidiTrack) {
        for event in track.events.iter_mut() {
            let boosted = (event.velocity as f32 * self.0).round() as u32;
            event.velocity = boosted.min(127) as u8;
        }
    }
}

#[cfg(feature = "midi")]
#[test]
fn velocity_boost_integration() {
    let midi_contents = "60 80\n64 90\n";
    let midi_path = temp_path("in.mid");
    fs::write(&midi_path, midi_contents).unwrap();

    let mut track = humster::Track::from_midi(&midi_path).expect("load midi");
    let processor = VelocityBoost(1.25);
    track.apply(&processor);

    let midi = track.as_midi().unwrap();
    assert_eq!(midi.events()[0].velocity, 100);
    assert_eq!(midi.events()[1].velocity, 113);

    let out_path = temp_path("out.mid");
    track.export(&out_path).expect("export midi");
    let exported = fs::read_to_string(&out_path).unwrap();
    assert_eq!(exported, "60 100\n64 113\n");

    fs::remove_file(midi_path).unwrap();
    fs::remove_file(out_path).unwrap();
}

#[cfg(feature = "audio")]
struct Gain(f32);

#[cfg(feature = "audio")]
impl humster::Processor for Gain {
    fn process_audio(&self, track: &mut humster::AudioTrack) {
        for sample in track.samples.iter_mut() {
            *sample *= self.0;
        }
    }
}

#[cfg(feature = "audio")]
#[test]
fn audio_gain_integration() {
    let mut track = humster::Track::from_audio(vec![0.1, -0.3, 0.5], 48_000);
    let processor = Gain(2.0);
    track.apply(&processor);

    let audio = track.as_audio().unwrap();
    assert_eq!(audio.samples, vec![0.2, -0.6, 1.0]);
    assert_eq!(audio.sample_rate, 48_000);

    let out_path = temp_path("audio.txt");
    track.export(&out_path).expect("export audio");
    let exported = fs::read_to_string(&out_path).unwrap();
    assert!(exported.starts_with("sample_rate 48000"));

    fs::remove_file(out_path).unwrap();
}