Skip to main content

sva_engine/
query.rs

1// Concern: names every observation, what each consumes, and the envelope an answer carries | Non-concern: taking one reading (answer.rs, sva-samples) | IO: (name) -> Representation, Consumes
2
3use sva_formula::{Line, SpectralSum};
4use sva_samples::{
5    Alias, Bands, Buffer, Consumes, Crest, EnvelopeFrame, FormantFrame, LedgerEntry, Loudness,
6    Onsets, PitchFrame, Source, Spectrum, StereoImage,
7};
8
9use crate::arguments::Arguments;
10use crate::bindings::Binding;
11
12/// The default window a framed reading uses when the observation names none.
13pub const DEFAULT_FRAME_SECS: f64 = 0.05;
14
15#[derive(Clone, Copy, Debug, PartialEq)]
16pub enum Representation {
17    Lines,
18    Atoms,
19    Spectrum {
20        max_peaks: usize,
21        frame_secs: Option<f64>,
22    },
23    Envelope {
24        frame_secs: Option<f64>,
25    },
26    Derivative,
27    Samples,
28    Ledger {
29        depth: usize,
30    },
31    Pitch {
32        max_notes: usize,
33        frame_secs: f64,
34    },
35    Formants {
36        max_formants: usize,
37        frame_secs: f64,
38    },
39    Stereo {
40        frame_secs: f64,
41    },
42    Bands,
43    Crest,
44    Loudness,
45    Onsets,
46    Alias {
47        oversample: u32,
48    },
49    Bindings,
50    Arguments,
51    Flops,
52}
53
54/// What a caller asked of one node.
55#[derive(Clone, Debug, PartialEq)]
56pub struct Ask {
57    pub node: String,
58    pub representation: Representation,
59}
60
61#[derive(Clone, Debug, PartialEq)]
62pub enum Output {
63    Lines(Vec<Line>),
64    Atoms(Vec<String>),
65    Symbolic(Box<SpectralSum>),
66    Samples(Box<Buffer>),
67    Spectrum(Box<Spectrum>),
68    Envelope(Vec<EnvelopeFrame>),
69    Pitch(Vec<PitchFrame>),
70    Formants(Vec<FormantFrame>),
71    Stereo(Box<StereoImage>),
72    Bands(Box<Bands>),
73    Crest(Box<Crest>),
74    Loudness(Box<Loudness>),
75    Onsets(Box<Onsets>),
76    Alias(Box<Alias>),
77    Ledger(Vec<LedgerEntry>),
78    Bindings(Vec<Binding>),
79    Arguments(Vec<Arguments>),
80    Flops(Box<crate::flops::Tree>),
81}
82
83/// Every answer says which reading ran and under which profile, so a caller never has to
84/// infer either from the shape of the value.
85#[derive(Clone, Debug, PartialEq)]
86pub struct Answer {
87    pub value: Output,
88    pub source: Source,
89    pub profile: &'static str,
90    pub rate: Option<u32>,
91    /// FORMAT 14.1: a series answers the terms it keeps, the tail beside them.
92    pub dropped: Vec<sva_formula::Line>,
93    pub tail_db: Option<f64>,
94}
95
96impl Answer {
97    pub fn whole(
98        value: Output,
99        source: Source,
100        profile: &'static str,
101        rate: Option<u32>,
102    ) -> Answer {
103        Answer {
104            value,
105            source,
106            profile,
107            rate,
108            dropped: Vec::new(),
109            tail_db: None,
110        }
111    }
112}
113
114impl Representation {
115    pub fn name(self) -> &'static str {
116        match self {
117            Representation::Lines => "lines",
118            Representation::Atoms => "atoms",
119            Representation::Spectrum { .. } => "spectrum",
120            Representation::Envelope { .. } => "envelope",
121            Representation::Derivative => "derivative",
122            Representation::Samples => "samples",
123            Representation::Ledger { .. } => "ledger",
124            Representation::Pitch { .. } => "pitch",
125            Representation::Formants { .. } => "formants",
126            Representation::Stereo { .. } => "stereo",
127            Representation::Bands => "bands",
128            Representation::Crest => "crest",
129            Representation::Loudness => "loudness",
130            Representation::Onsets => "onsets",
131            Representation::Alias { .. } => "alias",
132            Representation::Bindings => "bindings",
133            Representation::Arguments => "arguments",
134            Representation::Flops => "flops",
135        }
136    }
137
138    /// `lines` and `atoms` read a closed form and nothing else. `spectrum`, `envelope`,
139    /// `derivative` and `pitch` read whichever the node holds, so its own `Ty` decides.
140    pub fn consumes(self, closed: bool) -> Consumes {
141        match self {
142            Representation::Lines | Representation::Atoms => Consumes::ClosedForm,
143            Representation::Spectrum { .. }
144            | Representation::Envelope { .. }
145            | Representation::Derivative
146            | Representation::Pitch { .. }
147                if closed =>
148            {
149                Consumes::ClosedForm
150            }
151            // None reads a buffer: two are resolved first, the other counts the schedule.
152            Representation::Bindings | Representation::Arguments | Representation::Flops => {
153                Consumes::ClosedForm
154            }
155            _ => Consumes::Buffer,
156        }
157    }
158
159    /// Whether a node's samples alone answer this reading, `sampled` where it holds samples.
160    pub fn off_samples(self, sampled: bool) -> bool {
161        let graph = matches!(
162            self,
163            Representation::Alias { .. } | Representation::Ledger { .. }
164        );
165        !graph && self.consumes(!sampled) == Consumes::Buffer
166    }
167
168    pub fn from_name(name: &str) -> Option<Representation> {
169        Some(match name {
170            "lines" => Representation::Lines,
171            "atoms" => Representation::Atoms,
172            "spectrum" => Representation::Spectrum {
173                max_peaks: 16,
174                frame_secs: None,
175            },
176            "envelope" => Representation::Envelope { frame_secs: None },
177            "derivative" => Representation::Derivative,
178            "samples" => Representation::Samples,
179            "ledger" => Representation::Ledger { depth: 3 },
180            "pitch" => Representation::Pitch {
181                max_notes: 6,
182                frame_secs: DEFAULT_FRAME_SECS,
183            },
184            "formants" => Representation::Formants {
185                max_formants: 5,
186                frame_secs: DEFAULT_FRAME_SECS,
187            },
188            "stereo" => Representation::Stereo {
189                frame_secs: DEFAULT_FRAME_SECS,
190            },
191            "bands" => Representation::Bands,
192            "crest" => Representation::Crest,
193            "loudness" => Representation::Loudness,
194            "onsets" => Representation::Onsets,
195            "alias" => Representation::Alias { oversample: 4 },
196            "bindings" => Representation::Bindings,
197            "arguments" => Representation::Arguments,
198            "flops" => Representation::Flops,
199            _ => return None,
200        })
201    }
202}