1use sva_formula::{C64, Line, SpectralSum, Var};
4use sva_samples::measure::harmony::dissonance::Dissonance;
5use sva_samples::measure::harmony::inharmonicity::Inharmonicity;
6use sva_samples::measure::harmony::tonnetz::TonalDistance;
7use sva_samples::measure::harmony::voice_leading::{Norm, VoiceLeading};
8use sva_samples::measure::loudness_pattern::LoudnessPattern;
9use sva_samples::measure::modulation::Modulation;
10use sva_samples::measure::panel::Panel;
11use sva_samples::measure::partials::Partials;
12use sva_samples::measure::periodicity::Periodicity;
13use sva_samples::measure::self_similarity::SelfSimilarity;
14use sva_samples::{
15 Alias, Bands, Buffer, Consumes, Crest, EnvelopeFrame, FormantFrame, LedgerEntry, Loudness,
16 PitchFrame, Score, Source, Spectrum, StereoImage,
17};
18
19use crate::arguments::Arguments;
20use crate::bindings::Binding;
21
22pub const DEFAULT_FRAME_SECS: f64 = 0.05;
24pub const DEFAULT_MAX_PEAKS: usize = 16;
25pub const DEFAULT_LEDGER_DEPTH: usize = 3;
26pub const DEFAULT_OVERSAMPLE: u32 = 4;
27pub const DEFAULT_BUFFER_SECS: f64 = 0.05;
29pub const DEFAULT_FLOOR_DB: f64 = -40.0;
31pub const DEFAULT_MAX_PAIRS: usize = 1 << 17;
33pub const DEFAULT_LOUDNESS_WINDOW_SECS: f64 = sva_samples::measure::loudness::MOMENTARY_SECS;
35pub const DEFAULT_PERIODICITY_WINDOW_SECS: f64 = 6.0;
37pub const DEFAULT_MODULATION_WINDOW_SECS: f64 = 4.0;
39pub const DEFAULT_CELL_SECS: f64 = 0.5;
41pub const DEFAULT_SMOOTH_SECS: f64 = 4.0;
43pub const DEFAULT_NOVELTY_WINDOW_SECS: f64 = 8.0;
45pub const DEFAULT_PARTIALS_FRAME_SECS: f64 = 0.1;
47pub const DEFAULT_PARTIALS_WINDOW_SECS: f64 = 0.5;
49pub const DEFAULT_PARTIALS_HOP_SECS: f64 = 0.005;
50pub const DEFAULT_PARTIALS_FLOOR_DB: f64 = -70.0;
51pub const DEFAULT_PARTIALS_TRACKS: usize = 120;
52pub const DEFAULT_PATTERN_FRAME_SECS: f64 = sva_samples::measure::panel::FRAME_SECS;
54pub use sva_samples::measure::loudness_pattern::DEFAULT_SPL_DB;
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub enum Arg {
59 Peaks,
60 Frame,
61 Depth,
62 Oversample,
63 Brief,
64 Skim,
65 Node,
66 Buffer,
67 Floor,
68 Norm,
69 Pairs,
70 Window,
71 Curve,
72 Bpm,
73 Cell,
74 Smooth,
75 Hop,
76 Spl,
77}
78
79impl Arg {
80 pub fn key(self) -> &'static str {
81 match self {
82 Arg::Peaks => "peaks",
83 Arg::Frame => "frame",
84 Arg::Depth => "depth",
85 Arg::Oversample => "oversample",
86 Arg::Brief => "brief",
87 Arg::Skim => "skim",
88 Arg::Node => "node",
89 Arg::Buffer => "buffer",
90 Arg::Floor => "floor",
91 Arg::Norm => "norm",
92 Arg::Pairs => "pairs",
93 Arg::Window => "window",
94 Arg::Curve => "curve",
95 Arg::Bpm => "bpm",
96 Arg::Cell => "cell",
97 Arg::Smooth => "smooth",
98 Arg::Hop => "hop",
99 Arg::Spl => "spl",
100 }
101 }
102}
103
104#[derive(Clone, Copy, Debug, PartialEq)]
106pub struct Given {
107 pub peaks: Option<usize>,
109 pub frame: Option<f64>,
112 pub depth: usize,
113 pub oversample: u32,
114 pub floor_db: Option<f64>,
116 pub norm: Norm,
117 pub max_pairs: usize,
118 pub window: Option<f64>,
120 pub curve: bool,
121 pub bpm: Option<f64>,
122 pub cell_secs: f64,
123 pub smooth_secs: f64,
124 pub hop: Option<f64>,
125 pub spl_db: Option<f64>,
127}
128
129impl Default for Given {
130 fn default() -> Given {
131 Given {
132 peaks: None,
133 frame: None,
134 depth: DEFAULT_LEDGER_DEPTH,
135 oversample: DEFAULT_OVERSAMPLE,
136 floor_db: None,
137 norm: Norm::L1,
138 max_pairs: DEFAULT_MAX_PAIRS,
139 window: None,
140 curve: false,
141 bpm: None,
142 cell_secs: DEFAULT_CELL_SECS,
143 smooth_secs: DEFAULT_SMOOTH_SECS,
144 hop: None,
145 spl_db: None,
146 }
147 }
148}
149
150#[derive(Clone, Copy, Debug, PartialEq, Eq)]
152pub enum Reads {
153 ClosedForm,
155 Either,
157 Samples,
159 Graph,
161 Structure,
163}
164
165impl Reads {
166 pub fn consumes(self, closed: bool) -> Consumes {
167 match self {
168 Reads::ClosedForm | Reads::Structure => Consumes::ClosedForm,
169 Reads::Either if closed => Consumes::ClosedForm,
170 Reads::Either | Reads::Samples | Reads::Graph => Consumes::Buffer,
171 }
172 }
173
174 pub fn off_samples(self, sampled: bool) -> bool {
175 match self {
176 Reads::Samples => true,
177 Reads::Either => sampled,
178 Reads::ClosedForm | Reads::Graph | Reads::Structure => false,
179 }
180 }
181}
182
183pub struct Reading {
184 pub name: &'static str,
185 pub takes: &'static [Arg],
186 pub reads: Reads,
187 pub help: &'static str,
188 build: fn(&Given) -> Representation,
189}
190
191impl Reading {
192 pub fn representation(&self, given: &Given) -> Representation {
193 (self.build)(given)
194 }
195}
196
197static LINES: Reading = Reading {
198 name: "lines",
199 takes: &[],
200 reads: Reads::ClosedForm,
201 help: "the exact line list of a closed form, every channel's in channel order with no \
202 channel named; `atoms` names each term's",
203 build: |_| Representation::Lines,
204};
205
206static ATOMS: Reading = Reading {
207 name: "atoms",
208 takes: &[],
209 reads: Reads::ClosedForm,
210 help: "each term of a closed form: its channel, sketch, line, decay, polynomial and window",
211 build: |_| Representation::Atoms,
212};
213
214static SPECTRUM: Reading = Reading {
215 name: "spectrum",
216 takes: &[Arg::Peaks, Arg::Frame],
217 reads: Reads::Either,
218 help: "its lines, or its spectrum averaged over every frame and, power per bin, every \
219 channel; off samples a Hann spans the samples a frame holds, zero past them, each \
220 third-octave band reads as the amplitude of the one sine carrying its power, and, \
221 loudest first, a local maximum is no peak where its bin holds no more than the \
222 peaks kept, and a DC bin 0 holds as its maximum, can leak there, each bounded by \
223 Harris's (1978) Hann sidelobe bound 1/(pi m (m^2 - 1)) at m bins, its sinusoid taken \
224 anywhere within half a bin of its own: a bound where the frame is stationary \
225 sinusoids at the kept peaks",
226 build: |g| Representation::Spectrum {
227 max_peaks: g.peaks.unwrap_or(DEFAULT_MAX_PEAKS),
228 frame_secs: g.frame,
229 },
230};
231
232static ENVELOPE: Reading = Reading {
233 name: "envelope",
234 takes: &[Arg::Frame],
235 reads: Reads::Either,
236 help: "off a closed form the squared modulus of its analytic signal, x^2 + H{x}^2, as \
237 atoms per channel; off samples rms and peak per frame: the rms of every channel's \
238 samples together, the peak of any channel",
239 build: |g| Representation::Envelope {
240 frame_secs: g.frame,
241 },
242};
243
244static DERIVATIVE: Reading = Reading {
245 name: "derivative",
246 takes: &[],
247 reads: Reads::Either,
248 help: "the derivative's atoms per channel, each series listed as `atoms` lists it; off \
249 samples the first difference, (x[n] - x[n-1]) * rate at every sample but the first",
250 build: |_| Representation::Derivative,
251};
252
253static SAMPLES: Reading = Reading {
254 name: "samples",
255 takes: &[Arg::Buffer],
256 reads: Reads::Samples,
257 help: "the samples themselves; a `.wav` destination writes them as audio, an \
258 `alsa:<device>` one plays them as they render",
259 build: |_| Representation::Samples,
260};
261
262static LEDGER: Reading = Reading {
263 name: "ledger",
264 takes: &[Arg::Depth, Arg::Brief, Arg::Skim],
265 reads: Reads::Graph,
266 help: "rms, peak, share and clipping per node under the target",
267 build: |g| Representation::Ledger { depth: g.depth },
268};
269
270static PITCH: Reading = Reading {
271 name: "pitch",
272 takes: &[Arg::Peaks, Arg::Frame],
273 reads: Reads::Either,
274 help: "the `peaks` loudest notes it holds, per frame off samples, every channel's power \
275 per bin averaged; off samples its peaks are picked as `spectrum` picks them, and a series' \
276 fundamental is read within half a bin of its root's peak, the rate over the samples \
277 its Hann spans",
278 build: |g| Representation::Pitch {
279 max_notes: g.peaks.unwrap_or(DEFAULT_MAX_PEAKS),
280 frame_secs: g.frame.unwrap_or(DEFAULT_FRAME_SECS),
281 },
282};
283
284static FORMANTS: Reading = Reading {
285 name: "formants",
286 takes: &[Arg::Peaks, Arg::Frame],
287 reads: Reads::Samples,
288 help: "formant frequencies, bandwidths and levels per frame, the `peaks` lowest, off \
289 every channel's autocorrelation averaged, an all-pole fit of 2 poles and 1 per kHz \
290 of rate, at most 64: a frame or target holding no more samples than its order \
291 refuses, and a tail frame reads zero past its end",
292 build: |g| Representation::Formants {
293 max_formants: g.peaks.unwrap_or(DEFAULT_MAX_PEAKS),
294 frame_secs: g.frame.unwrap_or(DEFAULT_FRAME_SECS),
295 },
296};
297
298static STEREO: Reading = Reading {
299 name: "stereo",
300 takes: &[Arg::Frame],
301 reads: Reads::Samples,
302 help: "correlation, width and balance between exactly two channels, per frame; any other \
303 count refuses",
304 build: |g| Representation::Stereo {
305 frame_secs: g.frame.unwrap_or(DEFAULT_FRAME_SECS),
306 },
307};
308
309static BANDS: Reading = Reading {
310 name: "bands",
311 takes: &[],
312 reads: Reads::Samples,
313 help: "each auditory band's level over time, every channel's band energy averaged, at 2 kHz",
314 build: |_| Representation::Bands,
315};
316
317static CREST: Reading = Reading {
318 name: "crest",
319 takes: &[],
320 reads: Reads::Samples,
321 help: "peak against rms, broadband and per band: any channel's peak against the rms of \
322 every channel's samples together",
323 build: |_| Representation::Crest,
324};
325
326static LOUDNESS: Reading = Reading {
327 name: "loudness",
328 takes: &[Arg::Window],
329 reads: Reads::Samples,
330 help: "integrated, momentary and short-term loudness in LUFS, the momentary blocks `window` \
331 long and the integrated gated over them",
332 build: |g| Representation::Loudness {
333 window_secs: g.window.unwrap_or(DEFAULT_LOUDNESS_WINDOW_SECS),
334 },
335};
336
337static SCORE: Reading = Reading {
338 name: "score",
339 takes: &[],
340 reads: Reads::Samples,
341 help: "its events as a score: each one's onset, placed at the sample its strike rises, its \
342 end and the next, new, a re-strike or held from the start, tonal, noisy-pitched or \
343 noisy, an unpitched hit's kind, its attack, level and periodicity, and its voices, \
344 several where a chord sounds, each with its f0, tonal or noisy, whether it sounds \
345 newly there, its bend and its partials, the loudest new one its main; every \
346 channel's samples averaged",
347 build: |_| Representation::Score,
348};
349
350static LOUDNESS_PATTERN: Reading = Reading {
351 name: "loudness_pattern",
352 takes: &[Arg::Frame, Arg::Spl],
353 reads: Reads::Samples,
354 help: "specific loudness in sone per Cam, band by band every `frame`, and each frame's \
355 total, after the ear's transfer and the excitation's spread, so a sound another \
356 masks adds little, a full-scale sine at `spl` dB SPL; every channel's power per \
357 bin averaged",
358 build: |g| Representation::LoudnessPattern {
359 frame_secs: g.frame.unwrap_or(DEFAULT_PATTERN_FRAME_SECS),
360 spl_db: g.spl_db.unwrap_or(DEFAULT_SPL_DB),
361 },
362};
363
364static PANEL: Reading = Reading {
365 name: "panel",
366 takes: &[Arg::Spl],
367 reads: Reads::Samples,
368 help: "the perceptual axes per note of `score` and over the whole: sharpness in acum, \
369 decay in phon/s, beating in phon, the 10-90 % attack, and each note's pitch to \
370 under a cent; its notes and their pitch off every channel's samples averaged, \
371 every other axis off `loudness_pattern` at its default frame and every channel's \
372 power averaged; a cost subtracting two reads each axis in its just-noticeable \
373 differences",
374 build: |g| Representation::Panel {
375 spl_db: g.spl_db.unwrap_or(DEFAULT_SPL_DB),
376 },
377};
378
379static ALIAS: Reading = Reading {
380 name: "alias",
381 takes: &[Arg::Oversample],
382 reads: Reads::Graph,
383 help: "what folded, against a re-render at a multiple of the rate",
384 build: |g| Representation::Alias {
385 oversample: g.oversample,
386 },
387};
388
389static DISSONANCE: Reading = Reading {
390 name: "dissonance",
391 takes: &[Arg::Frame, Arg::Floor, Arg::Pairs],
392 reads: Reads::Either,
393 help: "Sethares' sensory roughness per frame of its partials over a floor, with its mean, \
394 max and min; measured partials off every channel's power per bin averaged",
395 build: |g| Representation::Harmony {
396 reading: Harmonic::Dissonance {
397 max_pairs: g.max_pairs,
398 },
399 frame_secs: g.frame.unwrap_or(DEFAULT_FRAME_SECS),
400 floor_db: g.floor_db.unwrap_or(DEFAULT_FLOOR_DB),
401 },
402};
403
404static VOICE_LEADING: Reading = Reading {
405 name: "voice_leading",
406 takes: &[Arg::Frame, Arg::Floor, Arg::Norm],
407 reads: Reads::Either,
408 help: "its chords over time, and the smallest voice leading between each two, l1 or l2 \
409 in semitones; measured partials off every channel's power per bin averaged",
410 build: |g| Representation::Harmony {
411 reading: Harmonic::VoiceLeading { norm: g.norm },
412 frame_secs: g.frame.unwrap_or(DEFAULT_FRAME_SECS),
413 floor_db: g.floor_db.unwrap_or(DEFAULT_FLOOR_DB),
414 },
415};
416
417static TONAL_DISTANCE: Reading = Reading {
418 name: "tonal_distance",
419 takes: &[Arg::Frame, Arg::Floor],
420 reads: Reads::Either,
421 help: "its chords over time, the Tonnetz steps between each two triads and each one's \
422 from the first triad; measured partials off every channel's power per bin averaged",
423 build: |g| Representation::Harmony {
424 reading: Harmonic::TonalDistance,
425 frame_secs: g.frame.unwrap_or(DEFAULT_FRAME_SECS),
426 floor_db: g.floor_db.unwrap_or(DEFAULT_FLOOR_DB),
427 },
428};
429
430static INHARMONICITY: Reading = Reading {
431 name: "inharmonicity",
432 takes: &[Arg::Frame, Arg::Floor],
433 reads: Reads::Either,
434 help: "its harmonic series per frame: each member's cents off n f1, the stiff-string B each \
435 fits, and the energy in no series; measured partials off every channel's power per \
436 bin averaged",
437 build: |g| Representation::Harmony {
438 reading: Harmonic::Inharmonicity,
439 frame_secs: g.frame.unwrap_or(DEFAULT_FRAME_SECS),
440 floor_db: g.floor_db.unwrap_or(DEFAULT_FLOOR_DB),
441 },
442};
443
444static PERIODICITY: Reading = Reading {
445 name: "periodicity",
446 takes: &[Arg::Frame, Arg::Window, Arg::Curve],
447 reads: Reads::Samples,
448 help: "how strongly its onset strength repeats per sliding window, broadband, under 150 Hz \
449 and over: the leading periods up to 2 s, or half the window, in seconds and bpm \
450 with their bounds, their \
451 autocorrelation heights, the drift between windows; every channel's band energy \
452 averaged",
453 build: |g| Representation::Periodicity {
454 frame_secs: g.frame.unwrap_or(DEFAULT_FRAME_SECS),
455 window_secs: g.window.unwrap_or(DEFAULT_PERIODICITY_WINDOW_SECS),
456 curve: g.curve,
457 },
458};
459
460static MODULATION: Reading = Reading {
461 name: "modulation",
462 takes: &[Arg::Frame, Arg::Window, Arg::Bpm],
463 reads: Reads::Samples,
464 help: "how its band envelopes move per sliding window, under 150 Hz, to 2 kHz and over: \
465 the leading modulation rates from 0.5 Hz, or the slowest two of whose periods the \
466 window holds, to 20 Hz, in beats where `bpm` is given, \
467 their depth as a modulation index, each with its bounds, the level, and the \
468 spectral centroid's mean and its own swing in Hz; every channel's band energy \
469 averaged",
470 build: |g| Representation::Modulation {
471 frame_secs: g.frame.unwrap_or(DEFAULT_FRAME_SECS),
472 window_secs: g.window.unwrap_or(DEFAULT_MODULATION_WINDOW_SECS),
473 bpm: g.bpm,
474 },
475};
476
477static SELF_SIMILARITY: Reading = Reading {
478 name: "self_similarity",
479 takes: &[
480 Arg::Frame,
481 Arg::Cell,
482 Arg::Smooth,
483 Arg::Window,
484 Arg::Floor,
485 Arg::Peaks,
486 Arg::Curve,
487 ],
488 reads: Reads::Samples,
489 help: "how its stretches resemble one another, by chroma and by band levels, over cells \
490 of `cell`: the repeated stretches as stripes, each pair of spans with its \
491 similarity and, by chroma, its transposition in semitones; Foote's novelty at every \
492 cell boundary and its peaks; `curve=1` adds the cells and the matrix; every \
493 channel's power per bin and band energy averaged",
494 build: |g| Representation::SelfSimilarity {
495 frame_secs: g.frame.unwrap_or(DEFAULT_FRAME_SECS),
496 cell_secs: g.cell_secs,
497 smooth_secs: g.smooth_secs,
498 window_secs: g.window.unwrap_or(DEFAULT_NOVELTY_WINDOW_SECS),
499 floor_db: g.floor_db.unwrap_or(DEFAULT_FLOOR_DB),
500 listed: g.peaks.unwrap_or(DEFAULT_MAX_PEAKS),
501 curve: g.curve,
502 },
503};
504
505static PARTIALS: Reading = Reading {
506 name: "partials",
507 takes: &[Arg::Frame, Arg::Window, Arg::Hop, Arg::Floor, Arg::Peaks],
508 reads: Reads::Samples,
509 help: "each partial as a track: its frequency and amplitude as knot tables for `pwl` and \
510 `pwl_integral`, tonal or breathy by its amplitude's flutter and its band's \
511 coherence, each with its resolution and the residual its tables leave; peaks \
512 of `frame` Hann frames' power averaged over `window`, found every `hop` over the \
513 floor, the `peaks` tracks of most energy kept; every channel's samples averaged",
514 build: |g| Representation::Partials {
515 frame_secs: g.frame.unwrap_or(DEFAULT_PARTIALS_FRAME_SECS),
516 window_secs: g.window.unwrap_or(DEFAULT_PARTIALS_WINDOW_SECS),
517 hop_secs: g.hop.unwrap_or(DEFAULT_PARTIALS_HOP_SECS),
518 floor_db: g.floor_db.unwrap_or(DEFAULT_PARTIALS_FLOOR_DB),
519 tracks: g.peaks.unwrap_or(DEFAULT_PARTIALS_TRACKS),
520 },
521};
522
523static BINDINGS: Reading = Reading {
524 name: "bindings",
525 takes: &[Arg::Node],
526 reads: Reads::Structure,
527 help: "the bindings one instance resolved, each with its source",
528 build: |_| Representation::Bindings,
529};
530
531static ARGUMENTS: Reading = Reading {
532 name: "arguments",
533 takes: &[],
534 reads: Reads::Structure,
535 help: "each builtin call's named arguments as the numbers it was lowered with",
536 build: |_| Representation::Arguments,
537};
538
539pub static READINGS: [&Reading; 27] = [
541 &LINES,
542 &ATOMS,
543 &SPECTRUM,
544 &ENVELOPE,
545 &DERIVATIVE,
546 &SAMPLES,
547 &LEDGER,
548 &PITCH,
549 &FORMANTS,
550 &STEREO,
551 &BANDS,
552 &CREST,
553 &LOUDNESS,
554 &SCORE,
555 &ALIAS,
556 &DISSONANCE,
557 &VOICE_LEADING,
558 &TONAL_DISTANCE,
559 &INHARMONICITY,
560 &PERIODICITY,
561 &MODULATION,
562 &SELF_SIMILARITY,
563 &PARTIALS,
564 &LOUDNESS_PATTERN,
565 &PANEL,
566 &BINDINGS,
567 &ARGUMENTS,
568];
569
570#[derive(Clone, Copy, Debug, PartialEq)]
571pub enum Representation {
572 Lines,
573 Atoms,
574 Spectrum {
575 max_peaks: usize,
576 frame_secs: Option<f64>,
577 },
578 Envelope {
579 frame_secs: Option<f64>,
580 },
581 Derivative,
582 Samples,
583 Ledger {
584 depth: usize,
585 },
586 Pitch {
587 max_notes: usize,
588 frame_secs: f64,
589 },
590 Formants {
591 max_formants: usize,
592 frame_secs: f64,
593 },
594 Stereo {
595 frame_secs: f64,
596 },
597 Bands,
598 Crest,
599 Loudness {
601 window_secs: f64,
602 },
603 Score,
604 Alias {
605 oversample: u32,
606 },
607 Harmony {
609 reading: Harmonic,
610 frame_secs: f64,
611 floor_db: f64,
612 },
613 Periodicity {
615 frame_secs: f64,
616 window_secs: f64,
617 curve: bool,
618 },
619 Modulation {
621 frame_secs: f64,
622 window_secs: f64,
623 bpm: Option<f64>,
624 },
625 SelfSimilarity {
628 frame_secs: f64,
629 cell_secs: f64,
630 smooth_secs: f64,
631 window_secs: f64,
632 floor_db: f64,
633 listed: usize,
634 curve: bool,
635 },
636 Partials {
637 frame_secs: f64,
638 window_secs: f64,
639 hop_secs: f64,
640 floor_db: f64,
641 tracks: usize,
642 },
643 LoudnessPattern {
644 frame_secs: f64,
645 spl_db: f64,
646 },
647 Panel {
648 spl_db: f64,
649 },
650 Bindings,
651 Arguments,
652}
653
654#[derive(Clone, Copy, Debug, PartialEq, Eq)]
656pub enum Harmonic {
657 Dissonance { max_pairs: usize },
658 VoiceLeading { norm: Norm },
659 TonalDistance,
660 Inharmonicity,
661}
662
663#[derive(Clone, Debug, PartialEq)]
665pub struct Ask {
666 pub node: String,
667 pub representation: Representation,
668}
669
670#[derive(Clone, Debug, PartialEq)]
671pub enum Output {
672 Lines(Vec<Line>),
673 Atoms(Vec<Atom>),
674 Symbolic(Box<SpectralSum>),
675 Samples(Box<Buffer>),
676 Spectrum(Box<Spectrum>),
677 Envelope(Vec<EnvelopeFrame>),
678 Pitch(Vec<PitchFrame>),
679 Formants(Vec<FormantFrame>),
680 Stereo(Box<StereoImage>),
681 Bands(Box<Bands>),
682 Crest(Box<Crest>),
683 Loudness(Box<Loudness>),
684 Score(Box<Score>),
685 Alias(Box<Alias>),
686 Ledger(Vec<LedgerEntry>),
687 Dissonance(Box<Dissonance>),
688 VoiceLeading(Box<VoiceLeading>),
689 TonalDistance(Box<TonalDistance>),
690 Inharmonicity(Box<Inharmonicity>),
691 Periodicity(Box<Periodicity>),
692 Modulation(Box<Modulation>),
693 SelfSimilarity(Box<SelfSimilarity>),
694 Partials(Box<Partials>),
695 LoudnessPattern(Box<LoudnessPattern>),
696 Panel(Box<Panel>),
697 Bindings(Vec<Binding>),
698 Arguments(Vec<Arguments>),
699}
700
701#[derive(Clone, Debug, PartialEq)]
703pub struct Atom {
704 pub var: Var,
705 pub channel: usize,
707 pub sketch: String,
708 pub amp: C64,
709 pub turns: f64,
710 pub decay: f64,
711 pub decay_from: f64,
712 pub degree: u16,
713 pub polynomial_at: f64,
714 pub start: Option<f64>,
715 pub end: Option<f64>,
716}
717
718#[derive(Clone, Debug, PartialEq)]
721pub struct Answer {
722 pub value: Output,
723 pub source: Source,
724 pub profile: &'static str,
725 pub rate: Option<u32>,
726 pub dropped: Vec<sva_formula::Line>,
728 pub tail_db: Option<f64>,
729}
730
731impl Answer {
732 pub fn whole(
733 value: Output,
734 source: Source,
735 profile: &'static str,
736 rate: Option<u32>,
737 ) -> Answer {
738 Answer {
739 value,
740 source,
741 profile,
742 rate,
743 dropped: Vec::new(),
744 tail_db: None,
745 }
746 }
747}
748
749impl Representation {
750 pub fn reading(self) -> &'static Reading {
752 match self {
753 Representation::Lines => &LINES,
754 Representation::Atoms => &ATOMS,
755 Representation::Spectrum { .. } => &SPECTRUM,
756 Representation::Envelope { .. } => &ENVELOPE,
757 Representation::Derivative => &DERIVATIVE,
758 Representation::Samples => &SAMPLES,
759 Representation::Ledger { .. } => &LEDGER,
760 Representation::Pitch { .. } => &PITCH,
761 Representation::Formants { .. } => &FORMANTS,
762 Representation::Stereo { .. } => &STEREO,
763 Representation::Bands => &BANDS,
764 Representation::Crest => &CREST,
765 Representation::Loudness { .. } => &LOUDNESS,
766 Representation::Score => &SCORE,
767 Representation::Alias { .. } => &ALIAS,
768 Representation::Harmony { reading, .. } => match reading {
769 Harmonic::Dissonance { .. } => &DISSONANCE,
770 Harmonic::VoiceLeading { .. } => &VOICE_LEADING,
771 Harmonic::TonalDistance => &TONAL_DISTANCE,
772 Harmonic::Inharmonicity => &INHARMONICITY,
773 },
774 Representation::Periodicity { .. } => &PERIODICITY,
775 Representation::Modulation { .. } => &MODULATION,
776 Representation::SelfSimilarity { .. } => &SELF_SIMILARITY,
777 Representation::Partials { .. } => &PARTIALS,
778 Representation::LoudnessPattern { .. } => &LOUDNESS_PATTERN,
779 Representation::Panel { .. } => &PANEL,
780 Representation::Bindings => &BINDINGS,
781 Representation::Arguments => &ARGUMENTS,
782 }
783 }
784
785 pub fn name(self) -> &'static str {
786 self.reading().name
787 }
788
789 pub fn consumes(self, closed: bool) -> Consumes {
790 self.reading().reads.consumes(closed)
791 }
792
793 pub fn off_samples(self, sampled: bool) -> bool {
795 self.reading().reads.off_samples(sampled)
796 }
797
798 pub fn from_name(name: &str) -> Option<Representation> {
799 READINGS
800 .iter()
801 .find(|row| row.name == name)
802 .map(|row| row.representation(&Given::default()))
803 }
804}