Skip to main content

sim_lib_stream_audio/
cookbook.rs

1//! Deterministic cookbook builders for stream-audio recipes.
2
3use sim_kernel::{Expr, NumberLiteral, Symbol};
4
5use crate::{MemoryPcmSink, PcmBuffer, PcmSink, PcmSpec};
6
7/// Build the modeled PCM spec descriptor used by the cookbook recipe.
8pub fn pcm_spec_demo() -> Expr {
9    let spec = PcmSpec::f32(2, 48_000).expect("valid cookbook PCM spec");
10    let mut sink = MemoryPcmSink::new(spec);
11    sink.write_buffer(
12        PcmBuffer::f32(spec, 2, vec![0.25, -0.25, 0.5, -0.5]).expect("valid cookbook PCM buffer"),
13    )
14    .expect("memory sink accepts matching PCM spec");
15    sink.flush().expect("memory sink flushes");
16
17    Expr::Map(vec![
18        (field("kind"), sym("stream-audio", "pcm-spec")),
19        (field("format"), sym("stream-audio/pcm", "f32-interleaved")),
20        (field("channels"), number(spec.channels())),
21        (field("sample-rate"), number(spec.sample_rate_hz())),
22        (field("frames"), number(2)),
23        (field("sink-buffers"), number(sink.buffers().len())),
24        (field("flush-count"), number(sink.flush_count())),
25    ])
26}
27
28fn field(name: &str) -> Expr {
29    Expr::Symbol(Symbol::qualified("stream-audio", name))
30}
31
32fn sym(namespace: &str, name: &str) -> Expr {
33    Expr::Symbol(Symbol::qualified(namespace, name))
34}
35
36fn number(value: impl ToString) -> Expr {
37    Expr::Number(NumberLiteral {
38        domain: Symbol::qualified("numbers", "i64"),
39        canonical: value.to_string(),
40    })
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn pcm_spec_demo_uses_matching_memory_sink() {
49        let Expr::Map(entries) = pcm_spec_demo() else {
50            panic!("PCM spec demo is a map")
51        };
52        assert!(entries.iter().any(|(_, value)| {
53            matches!(value, Expr::Symbol(symbol) if symbol.as_qualified_str() == "stream-audio/pcm/f32-interleaved")
54        }));
55    }
56}