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