sim-lib-stream-host 0.1.0-rc.1

Host-device stream backend substrate for STREAM 6.
Documentation
use std::collections::HashMap;

use sim_kernel::{Expr, Symbol};
use sim_lib_stream_core::{
    BufferPolicy, ClockDomain, DomainBridgeKind, LatencyClass, PcmPacket, PlacedFragment,
    RateContract, StreamDirection, StreamItem, StreamMedia, StreamPacket, TransportProfile,
    stream_edge,
};

use crate::{
    AudioDeviceCard, AudioPlacementRequest, AudioRouter, AudioSiteKey, FakeBackend, HostDirection,
    HostStreamConfigRequest, LanPlacementMode, LanPlacementRequest, ModeledAudioSite,
    fake_backend_symbol, lan_bar_delay_mode_symbol, lan_experimental_remote_sample_capability,
    lan_jitter_buffered_mode_symbol, lan_peer_site_symbol,
    lan_pinned_sample_experimental_diagnostic, lan_pinned_sample_refusal_diagnostic,
};

#[test]
fn placement_card_round_trip() {
    let key = AudioSiteKey::new("sim:modeled-stereo");
    let card = AudioDeviceCard::modeled(key.clone(), "Modeled Stereo");

    let mut registry: HashMap<AudioSiteKey, AudioDeviceCard> = HashMap::new();
    registry.insert(key.clone(), card);

    let found = registry.get(&key).unwrap();
    assert_eq!(found.display_name, "Modeled Stereo");
    assert_eq!(found.channels_out, 2);
    assert_eq!(found.channels_in, 2);
    assert_eq!(found.sample_rates, [44_100, 48_000]);
    assert!(!found.hardware_required);
}

#[test]
fn audio_site_router_opens_modeled_backend() {
    let key = AudioSiteKey::new("sim:fake-modeled");
    let card = AudioDeviceCard::modeled(key.clone(), "Fake Modeled Stereo");
    let site = std::sync::Arc::new(ModeledAudioSite::new(
        card,
        std::sync::Arc::new(FakeBackend::new()),
    ));
    let mut router = AudioRouter::new();
    router.register(site);

    assert!(router.site(&key).is_some());
    let opened = router
        .open_placement(AudioPlacementRequest {
            site_key: key,
            stream_request: HostStreamConfigRequest::new(
                fake_backend_symbol(),
                Symbol::new("fake/pcm"),
                StreamMedia::Pcm,
                HostDirection::Output,
                BufferPolicy::bounded(8).unwrap(),
            ),
        })
        .unwrap();

    assert_eq!(opened.config().media(), StreamMedia::Pcm);
    opened.close().unwrap();
}

#[test]
fn audio_router_filters_sites_by_capability() {
    let mono_key = AudioSiteKey::new("sim:test-mono");
    let stereo_key = AudioSiteKey::new("sim:test-stereo");
    let mut router = AudioRouter::new();
    router.register(std::sync::Arc::new(ModeledAudioSite::new(
        AudioDeviceCard {
            key: mono_key,
            display_name: "Mono".to_owned(),
            channels_out: 1,
            channels_in: 0,
            sample_rates: vec![48_000],
            hardware_required: false,
        },
        std::sync::Arc::new(FakeBackend::new()),
    )));
    router.register(std::sync::Arc::new(ModeledAudioSite::new(
        AudioDeviceCard {
            key: stereo_key.clone(),
            display_name: "Stereo".to_owned(),
            channels_out: 2,
            channels_in: 0,
            sample_rates: vec![44_100, 48_000],
            hardware_required: false,
        },
        std::sync::Arc::new(FakeBackend::new()),
    )));

    assert_eq!(router.sites_by_capability(2, &[48_000]), vec![stereo_key]);
    assert!(router.sites_by_capability(1, &[96_000]).is_empty());
}

#[test]
fn lan_peer_inserts_jitter_and_latency_bridges() {
    let fragment = data_fragment(RateContract::control());
    let mode = LanPlacementMode::jitter_buffered(4, 128).unwrap();
    let report = LanPlacementRequest::new(fragment, mode).plan().unwrap();

    assert_eq!(report.site(), &lan_peer_site_symbol());
    assert_eq!(report.mode().symbol(), lan_jitter_buffered_mode_symbol());
    assert_eq!(report.latency_class(), LatencyClass::BufferedPreview);
    assert_eq!(report.bridges()[0].kind(), DomainBridgeKind::JitterBuffer);
    assert_eq!(
        report.bridges()[1].kind(),
        DomainBridgeKind::LatencyCompDelay
    );
    assert_eq!(report.added_bridge_latency().packet_count(), 4);
    assert_eq!(report.added_bridge_latency().frame_count(), 128);
    assert_eq!(
        report.output_envelopes()[0].profile().latency_class(),
        LatencyClass::BufferedPreview
    );
}

#[test]
fn bar_delay_mode_declares_musical_alignment() {
    let fragment = data_fragment(RateContract::midi_tick());
    let mode = LanPlacementMode::bar_delay(2, 120, 2, 64).unwrap();
    let report = LanPlacementRequest::new(fragment, mode).plan().unwrap();

    assert_eq!(report.mode().symbol(), lan_bar_delay_mode_symbol());
    assert_eq!(report.latency_class(), LatencyClass::CollabBarDelay);
    assert_eq!(report.bar_delay_millis(), Some(4_000));
    assert_eq!(report.added_bridge_latency().packet_count(), 2);
    assert_eq!(report.added_bridge_latency().frame_count(), 64);
    assert_eq!(
        report.output_envelopes()[0].profile().latency_class(),
        LatencyClass::CollabBarDelay
    );
}

#[test]
fn pinned_sample_domain_lan_node_requires_experimental_capability() {
    let fragment = sample_fragment();
    let mode = LanPlacementMode::jitter_buffered(3, 256).unwrap();
    let err = LanPlacementRequest::new(fragment, mode)
        .with_realtime_pin(true)
        .plan()
        .unwrap_err();

    assert!(format!("{err}").contains(&lan_pinned_sample_refusal_diagnostic().as_qualified_str()));
}

#[test]
fn experimental_pinned_sample_lan_output_is_not_sample_exact() {
    let fragment = sample_fragment();
    let mode = LanPlacementMode::bar_delay(1, 100, 3, 256).unwrap();
    let report = LanPlacementRequest::new(fragment, mode)
        .with_realtime_pin(true)
        .with_capability(lan_experimental_remote_sample_capability())
        .plan()
        .unwrap();

    assert!(
        report
            .diagnostics()
            .contains(&lan_pinned_sample_experimental_diagnostic())
    );
    assert_ne!(
        report.output_envelopes()[0].profile().latency_class(),
        LatencyClass::SampleExact
    );
    assert_eq!(
        report.output_envelopes()[0].clock_domain(),
        ClockDomain::Sample
    );
}

fn data_fragment(rate_contract: RateContract) -> PlacedFragment {
    let edge = stream_edge(
        "out",
        StreamMedia::Data,
        StreamDirection::Source,
        rate_contract,
    );
    let envelope = edge
        .result_envelope(7, Expr::String("payload".to_owned()))
        .unwrap();
    PlacedFragment::new(Symbol::new("lan-node"), Expr::Bool(true))
        .with_output_edge(edge.with_envelopes(vec![envelope]))
}

fn sample_fragment() -> PlacedFragment {
    let edge = stream_edge(
        "audio",
        StreamMedia::Pcm,
        StreamDirection::Source,
        RateContract::sample_exact(Some(48_000)),
    );
    let item = StreamItem::new(StreamPacket::Pcm(PcmPacket::i16(1, 2, vec![0, 1]).unwrap()));
    let envelope = sim_lib_stream_core::StreamEnvelope::from_item_with_profile(
        edge.metadata(),
        0,
        &item,
        TransportProfile::realtime_local_audio(),
    )
    .unwrap();
    PlacedFragment::new(Symbol::new("sample-node"), Expr::Bool(true))
        .with_output_edge(edge.with_envelopes(vec![envelope]))
}