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