sim-lib-view-daw 0.1.2

DAW timeline, mixer, plugin rack, and synth lenses for SIM Web.
Documentation
use sim_kernel::{Expr, Symbol};
use sim_lib_stream_core::{
    BridgeLatency, ClockDomain, LatencyClass, PcmPacket, PlacedFragment, RateContract,
    StreamDirection, StreamEnvelope, StreamMedia, StreamPacket, TransportProfile, stream_edge,
};
use sim_lib_topology::{
    Edge, Graph, Node, PlacementNodeProfile, PortRef, SiteMap, SiteProfile, place,
};
use sim_lib_web_bridge::{
    BrowserBridgeLane, BrowserPlacementRequest, BrowserWasmEngine, browser_wasm_site_symbol,
};

use crate::placement::{
    PLACEMENT_BRIDGE_TABLE_VIEW_ID, PLACEMENT_BROWSER_DIAGNOSTICS_VIEW_ID,
    PLACEMENT_DISCONNECT_FAULT, PLACEMENT_FAULT_TIMELINE_VIEW_ID, PLACEMENT_FORCED_REFUSAL_FAULT,
    PLACEMENT_GRAPH_VIEW_ID, PLACEMENT_INSPECTOR_VIEW_ID, PLACEMENT_JITTER_SPIKE_FAULT,
    PLACEMENT_LATENCY_BUDGET_VIEW_ID, PLACEMENT_REFUSAL_TABLE_VIEW_ID,
    PLACEMENT_RUNTIME_DIAGNOSTICS_VIEW_ID, PLACEMENT_WORKER_STALL_FAULT,
    PlacementRuntimeDiagnostic, placement_fault_fixture, placement_fault_fixture_names,
    placement_inspector_view, placement_inspector_view_with_diagnostics,
};

#[test]
fn placement_inspector_renders_all_local_report() {
    let report = report_for(all_audio_site_map());
    let scene = placement_inspector_view(&report);

    sim_lib_scene::validate_scene(&scene).expect("placement inspector scene is valid");
    assert!(sim_test_support::contains_kind(&scene, "stack"));
    assert!(sim_test_support::contains_kind(&scene, "graph"));
    assert_eq!(
        field(&scene, "lens"),
        Some(sym(PLACEMENT_INSPECTOR_VIEW_ID))
    );
    assert_eq!(field(&scene, "role"), Some(sym("placement-inspector")));
    assert!(contains_role(&scene, "placement-graph"));
    assert!(contains_role(&scene, "placement-latency-budget"));
    assert!(contains_symbol(&scene, "stream/clock-domain", "block"));
    assert!(contains_symbol(&scene, "stream/latency", "block-local"));
    assert!(contains_symbol_name(&scene, PLACEMENT_GRAPH_VIEW_ID));
    assert!(contains_symbol_name(
        &scene,
        PLACEMENT_LATENCY_BUDGET_VIEW_ID
    ));
}

#[test]
fn placement_inspector_renders_offloaded_bridges_and_budget() {
    let report = report_for(offloaded_fx_site_map());
    let scene = placement_inspector_view(&report);

    sim_lib_scene::validate_scene(&scene).expect("placement inspector scene is valid");
    assert!(contains_role(&scene, "placement-bridge-table"));
    assert!(contains_symbol(
        &scene,
        "stream/bridge",
        "latency-comp-delay"
    ));
    assert!(contains_symbol(
        &scene,
        "stream/bridge-diagnostic",
        "latency-comp-delay"
    ));
    assert!(contains_symbol_name(&scene, PLACEMENT_BRIDGE_TABLE_VIEW_ID));
}

#[test]
fn placement_inspector_accepts_browser_and_runtime_diagnostics() {
    let report = report_for(browser_wasm_site_map());
    let browser = browser_report();
    let runtime = PlacementRuntimeDiagnostic::new(
        Symbol::qualified("placement/runtime", "worker"),
        Symbol::qualified("placement/runtime-diagnostic", "queue-depth"),
        2,
    );
    let scene = placement_inspector_view_with_diagnostics(&report, &[browser], &[runtime], &[]);

    sim_lib_scene::validate_scene(&scene).expect("placement inspector scene is valid");
    assert!(contains_role(&scene, "placement-browser-diagnostics"));
    assert!(contains_role(&scene, "placement-runtime-diagnostics"));
    for lane in [
        BrowserBridgeLane::Ui,
        BrowserBridgeLane::Preview,
        BrowserBridgeLane::Trace,
    ] {
        assert!(contains_exact_symbol(&scene, &lane.symbol()));
    }
    assert!(contains_exact_symbol(&scene, &browser_wasm_site_symbol()));
    assert!(contains_symbol(
        &scene,
        "placement/runtime-diagnostic",
        "queue-depth"
    ));
    assert!(contains_symbol_name(
        &scene,
        PLACEMENT_BROWSER_DIAGNOSTICS_VIEW_ID
    ));
    assert!(contains_symbol_name(
        &scene,
        PLACEMENT_RUNTIME_DIAGNOSTICS_VIEW_ID
    ));
}

#[test]
fn placement_inspector_renders_refusals_and_fault_fixtures() {
    let report = report_for(illegal_realtime_site_map());
    let faults = placement_fault_fixture_names()
        .into_iter()
        .map(|name| placement_fault_fixture(name).expect("known placement fault fixture"))
        .collect::<Vec<_>>();
    let scene = placement_inspector_view_with_diagnostics(&report, &[], &[], &faults);

    sim_lib_scene::validate_scene(&scene).expect("placement inspector scene is valid");
    assert!(contains_role(&scene, "placement-refusals"));
    assert!(contains_role(&scene, "placement-fault-timeline"));
    assert!(contains_symbol(
        &scene,
        "placement/refusal",
        "realtime-pin-violation"
    ));
    for name in [
        PLACEMENT_FORCED_REFUSAL_FAULT,
        PLACEMENT_JITTER_SPIKE_FAULT,
        PLACEMENT_WORKER_STALL_FAULT,
        PLACEMENT_DISCONNECT_FAULT,
    ] {
        assert!(contains_symbol(&scene, "placement/fault", name));
    }
    assert!(contains_symbol_name(
        &scene,
        PLACEMENT_REFUSAL_TABLE_VIEW_ID
    ));
    assert!(contains_symbol_name(
        &scene,
        PLACEMENT_FAULT_TIMELINE_VIEW_ID
    ));
}

fn report_for(site_map: SiteMap) -> sim_lib_topology::PlacementReport {
    let mut cx = test_cx();
    place(&mut cx, &placement_graph(), &site_map).expect("placement graph compiles")
}

fn test_cx() -> sim_kernel::Cx {
    use std::sync::Arc;

    sim_kernel::Cx::new(
        Arc::new(sim_kernel::NoopEvalPolicy),
        Arc::new(sim_kernel::DefaultFactory),
    )
}

fn placement_graph() -> Graph {
    let mut graph = Graph::minimal("placement-view");
    let mut fx = Node::named("fx", "call");
    fx.target = Some(Expr::Symbol(Symbol::qualified("placement", "gain")));
    graph.nodes = vec![Node::named("in", "in"), fx, Node::named("out", "out")];
    graph.edges = vec![
        Edge::new(0, PortRef::output("in"), PortRef::input("fx")),
        Edge::new(1, PortRef::output("fx"), PortRef::input("out")),
    ];
    graph
}

fn all_audio_site_map() -> SiteMap {
    SiteMap::new(SiteProfile::audio_clock("audio"))
        .with_node_profile("in", block_profile())
        .with_node_profile(
            "fx",
            block_profile().with_latency(BridgeLatency::frames(16)),
        )
        .with_node_profile("out", block_profile())
}

fn offloaded_fx_site_map() -> SiteMap {
    SiteMap::new(SiteProfile::audio_clock("audio"))
        .with_site(SiteProfile::local_worker("worker"))
        .assign_node("fx", "worker")
        .with_node_profile("in", block_profile())
        .with_node_profile(
            "fx",
            block_profile().with_latency(BridgeLatency::frames(64)),
        )
        .with_node_profile("out", block_profile())
}

fn browser_wasm_site_map() -> SiteMap {
    SiteMap::new(SiteProfile::audio_clock("audio"))
        .with_site(SiteProfile::buffered_remote("browser-wasm"))
        .assign_node("fx", "browser-wasm")
        .with_node_profile("in", block_profile())
        .with_node_profile(
            "fx",
            PlacementNodeProfile::new(
                RateContract::new(
                    ClockDomain::BrowserFrame,
                    LatencyClass::BufferedPreview,
                    None,
                ),
                false,
            )
            .with_latency(BridgeLatency::packets(2)),
        )
        .with_node_profile("out", block_profile())
}

fn illegal_realtime_site_map() -> SiteMap {
    SiteMap::new(SiteProfile::audio_clock("audio"))
        .with_site(SiteProfile::local_worker("worker"))
        .assign_node("fx", "worker")
        .with_node_profile("in", block_profile())
        .with_node_profile("fx", PlacementNodeProfile::sample_exact(Some(48_000), true))
        .with_node_profile("out", block_profile())
}

fn block_profile() -> PlacementNodeProfile {
    PlacementNodeProfile::block_local()
}

fn browser_report() -> sim_lib_web_bridge::BrowserPlacementReport {
    BrowserPlacementRequest::new(fragment(), browser_engine())
        .run_headless()
        .expect("browser placement report")
}

fn browser_engine() -> BrowserWasmEngine {
    BrowserWasmEngine::browser_local(Symbol::qualified("stream/engine", "browser-headless"))
}

fn fragment() -> PlacedFragment {
    let ui = data_edge(
        "ui",
        RateContract::new(ClockDomain::BrowserFrame, LatencyClass::Interactive, None),
        Expr::String("scene-update".to_owned()),
    );
    let preview = preview_edge();
    let trace = data_edge(
        "trace",
        RateContract::trace_step(),
        Expr::String("trace-step".to_owned()),
    );

    PlacedFragment::new(Symbol::new("browser-node"), Expr::Bool(true))
        .with_output_edge(ui)
        .with_output_edge(preview)
        .with_output_edge(trace)
}

fn data_edge(port: &str, rate: RateContract, payload: Expr) -> sim_lib_stream_core::StreamEdge {
    let edge = stream_edge(port, StreamMedia::Data, StreamDirection::Source, rate);
    let envelope = edge.result_envelope(0, payload).unwrap();
    edge.with_envelopes(vec![envelope])
}

fn preview_edge() -> sim_lib_stream_core::StreamEdge {
    let edge = stream_edge(
        "preview",
        StreamMedia::Pcm,
        StreamDirection::Source,
        RateContract::new(
            ClockDomain::BrowserFrame,
            LatencyClass::BufferedPreview,
            None,
        ),
    );
    let envelope = StreamEnvelope::new(
        edge.metadata().id().clone(),
        Symbol::qualified("stream/browser-packet", "preview-0"),
        StreamMedia::Pcm,
        StreamDirection::Source,
        0,
        Vec::new(),
        ClockDomain::BrowserFrame,
        TransportProfile::buffered_pcm_preview(),
        Vec::new(),
        StreamPacket::Pcm(PcmPacket::f32(1, 1, vec![0.25]).unwrap()),
    )
    .unwrap();
    edge.with_envelopes(vec![envelope])
}

use sim_value::build::sym;

fn field(map: &Expr, name: &str) -> Option<Expr> {
    let Expr::Map(entries) = map else { return None };
    entries.iter().find_map(|(key, value)| {
        matches!(key, Expr::Symbol(s) if &*s.name == name).then(|| value.clone())
    })
}

fn contains_role(expr: &Expr, role: &str) -> bool {
    field(expr, "role") == Some(sym(role))
        || expr_children(expr)
            .iter()
            .any(|child| contains_role(child, role))
}

fn contains_symbol(expr: &Expr, namespace: &str, name: &str) -> bool {
    match expr {
        Expr::Symbol(symbol)
            if symbol.namespace.as_deref() == Some(namespace) && symbol.name.as_ref() == name =>
        {
            true
        }
        _ => expr_children(expr)
            .iter()
            .any(|child| contains_symbol(child, namespace, name)),
    }
}

fn contains_symbol_name(expr: &Expr, name: &str) -> bool {
    match expr {
        Expr::Symbol(symbol) if symbol.name.as_ref() == name => true,
        _ => expr_children(expr)
            .iter()
            .any(|child| contains_symbol_name(child, name)),
    }
}

fn contains_exact_symbol(expr: &Expr, expected: &Symbol) -> bool {
    match expr {
        Expr::Symbol(symbol) if symbol == expected => true,
        _ => expr_children(expr)
            .iter()
            .any(|child| contains_exact_symbol(child, expected)),
    }
}

fn expr_children(expr: &Expr) -> Vec<&Expr> {
    match expr {
        Expr::List(items) | Expr::Vector(items) | Expr::Set(items) | Expr::Block(items) => {
            items.iter().collect()
        }
        Expr::Map(entries) => entries
            .iter()
            .flat_map(|(key, value)| [key, value])
            .collect(),
        Expr::Call { operator, args } => std::iter::once(operator.as_ref()).chain(args).collect(),
        Expr::Infix { left, right, .. } => vec![left, right],
        Expr::Prefix { arg, .. } | Expr::Postfix { arg, .. } => vec![arg],
        Expr::Quote { expr, .. } => vec![expr],
        Expr::Annotated { expr, annotations } => std::iter::once(expr.as_ref())
            .chain(annotations.iter().map(|(_, value)| value))
            .collect(),
        Expr::Extension { payload, .. } => vec![payload],
        _ => Vec::new(),
    }
}