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