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}
52
53/// What a caller asked of one node.
54#[derive(Clone, Debug, PartialEq)]
55pub struct Ask {
56    pub node: String,
57    pub representation: Representation,
58}
59
60#[derive(Clone, Debug, PartialEq)]
61pub enum Output {
62    Lines(Vec<Line>),
63    Atoms(Vec<String>),
64    Symbolic(Box<SpectralSum>),
65    Samples(Box<Buffer>),
66    Spectrum(Box<Spectrum>),
67    Envelope(Vec<EnvelopeFrame>),
68    Pitch(Vec<PitchFrame>),
69    Formants(Vec<FormantFrame>),
70    Stereo(Box<StereoImage>),
71    Bands(Box<Bands>),
72    Crest(Box<Crest>),
73    Loudness(Box<Loudness>),
74    Onsets(Box<Onsets>),
75    Alias(Box<Alias>),
76    Ledger(Vec<LedgerEntry>),
77    Bindings(Vec<Binding>),
78    Arguments(Vec<Arguments>),
79}
80
81/// Every answer says which reading ran and under which profile, so a caller never has to
82/// infer either from the shape of the value.
83#[derive(Clone, Debug, PartialEq)]
84pub struct Answer {
85    pub value: Output,
86    pub source: Source,
87    pub profile: &'static str,
88    pub rate: Option<u32>,
89    /// FORMAT 14.1: a series answers the terms it keeps, the tail beside them.
90    pub dropped: Vec<sva_formula::Line>,
91    pub tail_db: Option<f64>,
92}
93
94impl Answer {
95    pub fn whole(
96        value: Output,
97        source: Source,
98        profile: &'static str,
99        rate: Option<u32>,
100    ) -> Answer {
101        Answer {
102            value,
103            source,
104            profile,
105            rate,
106            dropped: Vec::new(),
107            tail_db: None,
108        }
109    }
110}
111
112impl Representation {
113    pub fn name(self) -> &'static str {
114        match self {
115            Representation::Lines => "lines",
116            Representation::Atoms => "atoms",
117            Representation::Spectrum { .. } => "spectrum",
118            Representation::Envelope { .. } => "envelope",
119            Representation::Derivative => "derivative",
120            Representation::Samples => "samples",
121            Representation::Ledger { .. } => "ledger",
122            Representation::Pitch { .. } => "pitch",
123            Representation::Formants { .. } => "formants",
124            Representation::Stereo { .. } => "stereo",
125            Representation::Bands => "bands",
126            Representation::Crest => "crest",
127            Representation::Loudness => "loudness",
128            Representation::Onsets => "onsets",
129            Representation::Alias { .. } => "alias",
130            Representation::Bindings => "bindings",
131            Representation::Arguments => "arguments",
132        }
133    }
134
135    /// `lines` and `atoms` read a closed form and nothing else. `spectrum`, `envelope`,
136    /// `derivative` and `pitch` read whichever the node holds, so its own `Ty` decides.
137    pub fn consumes(self, closed: bool) -> Consumes {
138        match self {
139            Representation::Lines | Representation::Atoms => Consumes::ClosedForm,
140            Representation::Spectrum { .. }
141            | Representation::Envelope { .. }
142            | Representation::Derivative
143            | Representation::Pitch { .. }
144                if closed =>
145            {
146                Consumes::ClosedForm
147            }
148            // Neither reads a buffer: both are resolved first.
149            Representation::Bindings | Representation::Arguments => Consumes::ClosedForm,
150            _ => Consumes::Buffer,
151        }
152    }
153
154    /// Whether a node's samples alone answer this reading, `sampled` where it holds samples.
155    pub fn off_samples(self, sampled: bool) -> bool {
156        let graph = matches!(
157            self,
158            Representation::Alias { .. } | Representation::Ledger { .. }
159        );
160        !graph && self.consumes(!sampled) == Consumes::Buffer
161    }
162
163    pub fn from_name(name: &str) -> Option<Representation> {
164        Some(match name {
165            "lines" => Representation::Lines,
166            "atoms" => Representation::Atoms,
167            "spectrum" => Representation::Spectrum {
168                max_peaks: 16,
169                frame_secs: None,
170            },
171            "envelope" => Representation::Envelope { frame_secs: None },
172            "derivative" => Representation::Derivative,
173            "samples" => Representation::Samples,
174            "ledger" => Representation::Ledger { depth: 3 },
175            "pitch" => Representation::Pitch {
176                max_notes: 6,
177                frame_secs: DEFAULT_FRAME_SECS,
178            },
179            "formants" => Representation::Formants {
180                max_formants: 5,
181                frame_secs: DEFAULT_FRAME_SECS,
182            },
183            "stereo" => Representation::Stereo {
184                frame_secs: DEFAULT_FRAME_SECS,
185            },
186            "bands" => Representation::Bands,
187            "crest" => Representation::Crest,
188            "loudness" => Representation::Loudness,
189            "onsets" => Representation::Onsets,
190            "alias" => Representation::Alias { oversample: 4 },
191            "bindings" => Representation::Bindings,
192            "arguments" => Representation::Arguments,
193            _ => return None,
194        })
195    }
196}