Skip to main content

sva_engine/
lib.rs

1// Concern: resolves a graph into instances a caller can ask about, and keeps their buffers | Non-concern: parsing (sva-ast), CLI framing (sva-cli) | IO: (&Graph, root) -> Instances, Traced
2
3mod arguments;
4mod bindings;
5mod cache;
6mod cast;
7mod error;
8pub mod flops;
9pub mod instantiate;
10mod loops;
11mod lower;
12mod meaning;
13mod offset;
14pub mod overload;
15pub mod query;
16mod refs;
17mod release;
18pub mod render;
19mod schedule;
20mod trace;
21mod typing;
22mod vocabulary;
23
24pub use arguments::{Argument, Arguments, Called, Chosen};
25pub use bindings::Binding;
26pub use cache::{
27    Cache, CacheStats, DEFAULT_SLOT_BYTES, DiskCache, ENGINE_DIR_PREFIX, Entry, Expected, Hash,
28    IO_NANOS_PER_BYTE, Lookup, Medium, MemoryCache, Outcome, PACK_FORMAT, Pack, Payload,
29    PayloadKind, Put, RawF64, SampleCodec, Slots, Tier, Tiered, VecMedium, buffer_key,
30    symbolic_key,
31};
32pub use cast::Cast;
33pub use error::{BindingFault, Diagnostic, EngineError, Located, REGISTRY};
34pub use flops::{Row as FlopRow, Tree as FlopTree};
35pub use loops::{Delay, Shift};
36pub use meaning::{Meaning, meaning};
37pub use offset::Offset;
38pub use query::{Answer, Ask, DEFAULT_FRAME_SECS, Output, Representation};
39pub use refs::{Read, identity, nodes_in, resolve, spectral_sum_of, symbolic_hash};
40pub use render::{
41    Block, Checkpoint, Render, RenderConfig, STREAMED, Silent, Stream, StreamConfig, answer,
42    answer_buffer, ledger_over, render, render_until_silent, render_with_slots, sketch_atom,
43};
44pub use schedule::{Order, Schedule, schedule_from};
45pub use sva_formula::{C64, Codomain, Held, Line, NodeId, SpectralSum, Ty, Var};
46pub use sva_samples::{
47    Alias, AliasBand, BAND_COUNT, BandCrest, BandTrack, Bands, Buffer, Cost, Crest, Detail,
48    EnvelopeFrame, FormantFrame, Frames, Horizon, Label, LedgerEntry, Loudness, LoudnessFrame,
49    MAX_PINNED_FRAME, PSYCHOACOUSTIC_V1, PitchFrame, Profile, Rule, SignalKind, Source, Spectrum,
50    StereoFrame, StereoImage, measure_alias, pinned_frame,
51};
52pub use trace::{Traced, Up, trace};
53pub use typing::{Typing, Value};
54pub use vocabulary::{BUILTINS, MAX_WIDTH, is_builtin, named_may_move, recognized_named};
55
56use sva_ast::Graph;
57
58pub const DEFAULT_SAMPLE_RATE: u32 = 44100;
59
60pub const SOURCE_HASH: &str = env!("SVA_ENGINE_SRC_HASH");
61
62/// Every crate whose source shapes a rendered sample, folded: equal only where a render is.
63pub const RENDER_FINGERPRINT: u64 = sva_fingerprint::fold(&[
64    sva_ast::SOURCE_HASH,
65    sva_formula::SOURCE_HASH,
66    sva_samples::SOURCE_HASH,
67    SOURCE_HASH,
68]);
69
70/// One inference, so a lint and a render refuse identically.
71pub fn check_structure(graph: &Graph, root: &str) -> Result<(), EngineError> {
72    types(graph, root).map(|_| ())
73}
74
75/// One `Ty` per node, across refs, over the instances `root` reaches.
76pub fn types(graph: &Graph, root: &str) -> Result<Typing, EngineError> {
77    let instances = instantiate::instantiate(graph, root)?;
78    // A root names the instance its own defaults resolved to, as `render` asks for.
79    let held = instances.instance_of(root)?;
80    let order = schedule::schedule_from(&instances, std::slice::from_ref(&held))?;
81    typing::infer_all(&instances, &order)
82}
83
84/// Every instance whose samples are a function of the RATE, not just of `t`.
85pub fn rate_dependent(graph: &Graph, root: &str) -> Result<Vec<String>, EngineError> {
86    let instances = instantiate::instantiate(graph, root)?;
87    Ok(instances
88        .paths()
89        .filter(|p| instances.at(p).is_some_and(|(e, _)| reads_rate(e)))
90        .map(str::to_string)
91        .collect())
92}
93
94fn reads_rate(e: &sva_ast::Expr) -> bool {
95    match e {
96        sva_ast::Expr::Lit(sva_ast::Literal::Samples(_)) => true,
97        sva_ast::Expr::Lit(_) => false,
98        sva_ast::Expr::Var(_) => false,
99        sva_ast::Expr::Bin(_, a, b) => reads_rate(a) || reads_rate(b),
100        sva_ast::Expr::SelfRef { .. } => true,
101        sva_ast::Expr::Ref { arg, binds, .. } => {
102            reads_rate(arg) || binds.iter().any(|(_, v)| reads_rate(v))
103        }
104        sva_ast::Expr::Call { name, args, .. } => {
105            let drawn = name == "rand"
106                && !matches!(
107                    args.first(),
108                    Some(sva_ast::Arg::Pos(sva_ast::Expr::Lit(_))) | None
109                );
110            drawn
111                || args.iter().any(|a| match a {
112                    sva_ast::Arg::Pos(v) | sva_ast::Arg::Named(_, v) => reads_rate(v),
113                })
114        }
115    }
116}