dioxus-audio 0.2.1

Audio recording, playback, analysis, and UI components for Dioxus
Documentation
use dioxus_audio::analysis::{
    AnalysisMetadata, LiveAnalysisOptions, WaveformSelection, downsample_peaks, peak_amplitude,
    rms_level, trim_interleaved_pcm,
};
use std::time::Duration;

#[test]
fn analysis_metadata_maps_frequency_bins_and_decibels() {
    let metadata = AnalysisMetadata::new(48_000.0, 1_024, -100.0, -30.0, 0.8);

    assert_eq!(metadata.sample_rate(), 48_000.0);
    assert_eq!(metadata.fft_size(), 1_024);
    assert_eq!(metadata.frequency_bin_count(), 512);
    assert_eq!(metadata.frequency_bin_width(), 46.875);
    assert_eq!(metadata.frequency_for_bin(0), Some(0.0));
    assert_eq!(metadata.frequency_for_bin(511), Some(23_953.125));
    assert_eq!(metadata.frequency_for_bin(512), None);
    assert_eq!(metadata.decibels_for_frequency_value(0.0), -100.0);
    assert_eq!(metadata.decibels_for_frequency_value(0.5), -65.0);
    assert_eq!(metadata.decibels_for_frequency_value(1.0), -30.0);
    assert_eq!(metadata.smoothing(), 0.8);
}

#[test]
fn live_analysis_cadence_is_clamped_to_documented_bounds() {
    assert_eq!(
        LiveAnalysisOptions::default().cadence(),
        Duration::from_millis(50)
    );
    assert_eq!(
        LiveAnalysisOptions::default()
            .with_cadence(Duration::ZERO)
            .cadence(),
        Duration::from_millis(16)
    );
    assert_eq!(
        LiveAnalysisOptions::default()
            .with_cadence(Duration::from_secs(5))
            .cadence(),
        Duration::from_secs(1)
    );
}

#[test]
fn analysis_level_is_normalized_root_mean_square_amplitude() {
    assert_eq!(rms_level(&[]), 0.0);
    assert_eq!(rms_level(&[0.5, -0.5]), 0.5);
    assert!((rms_level(&[1.0, 0.0]) - std::f32::consts::FRAC_1_SQRT_2).abs() < f32::EPSILON);
    assert_eq!(rms_level(&[2.0, -2.0]), 1.0);
}

#[test]
fn short_waveforms_do_not_gain_empty_buckets() {
    assert_eq!(downsample_peaks(&[12, 90], 32), vec![12, 90]);
}

#[test]
fn long_waveforms_keep_the_peak_from_each_window() {
    assert_eq!(downsample_peaks(&[10, 80, 30, 60], 2), vec![80, 60]);
}

#[test]
fn time_domain_amplitude_uses_the_full_byte_range() {
    assert_eq!(peak_amplitude(&[128, 128]), 0);
    assert_eq!(peak_amplitude(&[128, 64]), 128);
    assert_eq!(peak_amplitude(&[0, 128, 255]), 255);
}

#[test]
fn waveform_selection_is_an_ordered_source_time_interval() {
    let selection = WaveformSelection::new(7.5, 2.5);

    assert_eq!(selection, WaveformSelection::new(2.5, 7.5));
    assert_eq!(selection.start(), 2.5);
    assert_eq!(selection.end(), 7.5);
    assert_eq!(selection.with_start(9.0), WaveformSelection::new(7.5, 7.5));
    assert_eq!(selection.with_end(1.0), WaveformSelection::new(2.5, 2.5));
    assert!(selection.with_start(9.0).is_collapsed());
}

#[test]
fn waveform_selection_keeps_only_finite_non_negative_source_times() {
    assert_eq!(
        WaveformSelection::new(f64::NAN, f64::INFINITY),
        WaveformSelection::new(0.0, 0.0)
    );
    assert_eq!(
        WaveformSelection::new(f64::NEG_INFINITY, -2.0),
        WaveformSelection::new(0.0, 0.0)
    );

    let selection = WaveformSelection::new(2.5, 7.5);
    assert_eq!(selection.with_start(f64::NAN), selection);
    assert_eq!(selection.with_end(f64::INFINITY), selection);
}

#[test]
fn waveform_selection_clamps_to_duration_without_swapping_boundaries() {
    assert_eq!(
        WaveformSelection::new(2.5, 7.5).clamped_to_duration(5.0),
        WaveformSelection::new(2.5, 5.0)
    );
    assert_eq!(
        WaveformSelection::new(7.5, 9.0).clamped_to_duration(5.0),
        WaveformSelection::new(5.0, 5.0)
    );

    assert!(WaveformSelection::new(2.5, 5.0).is_playable_within(5.0));
    assert!(!WaveformSelection::new(2.5, 2.5).is_playable_within(5.0));
    assert!(!WaveformSelection::new(2.5, 7.5).is_playable_within(5.0));
    assert!(!WaveformSelection::new(0.0, 1.0).is_playable_within(f64::NAN));
}

#[test]
fn source_time_pcm_trimming_resolves_complete_channel_frames() {
    let stereo_with_incomplete_frame = [1_i16, 2, 3, 4, 5, 6, 7, 8, 9];

    assert_eq!(
        trim_interleaved_pcm(
            &stereo_with_incomplete_frame,
            2,
            4.0,
            WaveformSelection::new(1.25, 2.25),
        ),
        vec![3, 4, 5, 6]
    );
    assert_eq!(
        trim_interleaved_pcm(
            &stereo_with_incomplete_frame,
            2,
            4.0,
            WaveformSelection::new(3.0, 7.0),
        ),
        vec![7, 8]
    );
    assert!(
        trim_interleaved_pcm(
            &stereo_with_incomplete_frame,
            2,
            4.0,
            WaveformSelection::new(2.0, 2.0),
        )
        .is_empty()
    );
    assert!(
        trim_interleaved_pcm(
            &stereo_with_incomplete_frame,
            0,
            4.0,
            WaveformSelection::new(1.0, 2.0),
        )
        .is_empty()
    );
    assert!(
        trim_interleaved_pcm(
            &stereo_with_incomplete_frame,
            2,
            f64::NAN,
            WaveformSelection::new(1.0, 2.0),
        )
        .is_empty()
    );
}