sim-lib-sound-core 0.2.0

Sound-domain frequency, envelope, partial, spectrum, tone, and sample models.
Documentation
use std::time::Duration;

use super::*;
use sim_lib_pitch_core::Pitch;

#[test]
fn frequency_cents_ops_are_inverse() {
    let base = Frequency(440.0);
    let shifted = base.shift_cents(700.0);
    assert!((shifted.cents_above(base) - 700.0).abs() < 1e-6);
}

#[test]
fn amplitude_db_ops_are_inverse() {
    let amplitude = Amplitude::from_db(-6.0);
    assert!((amplitude.to_db() + 6.0).abs() < 1e-6);
}

#[test]
fn sine_has_one_partial() {
    assert_eq!(
        Tone::sine(Frequency(440.0), Duration::from_secs(1))
            .partials
            .len(),
        1
    );
}

#[test]
fn sawtooth_follows_inverse_harmonics() {
    let tone = Tone::sawtooth(Frequency(440.0), Duration::from_secs(1), 3);
    assert!((tone.partials[1].amplitude.0 - 0.5).abs() < 1e-6);
}

#[test]
fn pitch_to_equal_temperament_maps_a4_to_440() {
    assert!((equal_temperament_frequency(Pitch::from_midi(69)).0 - 440.0).abs() < 1e-6);
}

#[test]
fn partial_tags_and_phase_are_validated() {
    let partial = Partial::tagged(
        Frequency(440.0),
        Amplitude(0.5),
        Phase(-std::f64::consts::FRAC_PI_2),
        PartialTag::harmonic(2).unwrap(),
    )
    .unwrap();
    assert_eq!(partial.tag, PartialTag::Harmonic(2));
    assert!((partial.phase.0 - std::f64::consts::TAU * 0.75).abs() < 1e-9);

    assert_eq!(
        PartialTag::harmonic(0).unwrap_err(),
        SoundCoreError::InvalidPartialTag
    );
    assert_eq!(
        Partial::new(Frequency(f64::NAN), Amplitude(1.0), Phase(0.0)).unwrap_err(),
        SoundCoreError::InvalidFrequency
    );
    assert_eq!(
        Partial::new(Frequency(440.0), Amplitude(-0.1), Phase(0.0)).unwrap_err(),
        SoundCoreError::InvalidAmplitude
    );
    assert_eq!(
        Partial::new(Frequency(440.0), Amplitude(1.0), Phase(f64::INFINITY)).unwrap_err(),
        SoundCoreError::InvalidPhase
    );
}

#[test]
fn tone_from_partials_normalizes_partial_semantics() {
    let tone = Tone::from_partials(
        vec![Partial {
            frequency: Frequency(440.0),
            amplitude: Amplitude(1.0),
            phase: Phase(std::f64::consts::TAU * 3.0),
            tag: PartialTag::Source,
        }],
        default_envelope(),
        Duration::from_secs(1),
    )
    .unwrap();
    assert_eq!(tone.partials[0].phase, Phase(0.0));
    assert_eq!(tone.partials[0].tag, PartialTag::Source);
}