sim-lib-stream-file 0.1.3

File-backed source and sink adapters for STREAM 6.
Documentation
use crate::{
    ChannelMatrix, DitherPolicy, PcmConversionError, QuantizationPolicy, convert_f32_to_pcm16,
    convert_f32_to_wav_bytes,
};

#[test]
fn explicit_channel_matrices_preserve_frames_and_report_clipping() {
    let mono = [0.25, -0.5, 1.25];
    let policy = QuantizationPolicy {
        max_frames: 3,
        dither: DitherPolicy::None,
    };
    let conversion = convert_f32_to_pcm16(&mono, &ChannelMatrix::mono_to_stereo(), policy).unwrap();

    assert_eq!(conversion.samples.len(), 6);
    assert_eq!(conversion.samples[0], conversion.samples[1]);
    assert_eq!(conversion.samples[2], conversion.samples[3]);
    assert_eq!(conversion.report.frames, 3);
    assert_eq!(conversion.report.output_channels, 2);
    assert_eq!(conversion.report.clipped_samples, 2);
    assert_eq!(conversion.report.peak_before_quantization, 1.25);

    let downmix = convert_f32_to_pcm16(
        &[0.75, 0.25, -0.5, 0.5],
        &ChannelMatrix::stereo_to_mono(),
        policy,
    )
    .unwrap();
    assert_eq!(downmix.samples, vec![16_384, 0]);
}

#[test]
fn seeded_tpdf_and_noise_shaping_are_reproducible_and_distinct() {
    let input = vec![1.0 / 65_536.0; 2_048];
    let matrix = ChannelMatrix::identity(1).unwrap();
    let tpdf = QuantizationPolicy {
        max_frames: input.len(),
        dither: DitherPolicy::Tpdf { seed: 0x5eed },
    };
    let first = convert_f32_to_pcm16(&input, &matrix, tpdf).unwrap();
    let second = convert_f32_to_pcm16(&input, &matrix, tpdf).unwrap();
    assert_eq!(first, second);
    assert!(first.samples.iter().any(|sample| *sample != 1));

    let shaped = convert_f32_to_pcm16(
        &input,
        &matrix,
        QuantizationPolicy {
            max_frames: input.len(),
            dither: DitherPolicy::NoiseShapedTpdf {
                seed: 0x5eed,
                feedback: 0.85,
            },
        },
    )
    .unwrap();
    assert_ne!(first.samples, shaped.samples);
    assert_eq!(shaped.report.clipped_samples, 0);
}

#[test]
fn conversion_bounds_fail_closed_before_output() {
    let matrix = ChannelMatrix::identity(2).unwrap();
    let error = convert_f32_to_pcm16(
        &[0.0; 6],
        &matrix,
        QuantizationPolicy {
            max_frames: 2,
            dither: DitherPolicy::None,
        },
    )
    .unwrap_err();
    assert_eq!(
        error,
        PcmConversionError::FrameLimit {
            supplied: 3,
            maximum: 2,
        }
    );
}

#[test]
fn float_conversion_flows_through_canonical_pcm16_wav_encoder() {
    let (bytes, report) = convert_f32_to_wav_bytes(
        48_000,
        &[0.0, 0.5, -0.5, 1.1],
        &ChannelMatrix::identity(2).unwrap(),
        QuantizationPolicy {
            max_frames: 2,
            dither: DitherPolicy::Tpdf { seed: 7 },
        },
    )
    .unwrap();
    assert_eq!(&bytes[0..4], b"RIFF");
    assert_eq!(&bytes[8..12], b"WAVE");
    assert_eq!(report.frames, 2);
    assert_eq!(report.clipped_samples, 1);
}