Skip to main content

sound_to_patch/music/
entry.rs

1//! One entry of the music dictionary, its checkable note rule and its
2//! example fragments. `schemas/music.schema.json` describes the same
3//! shape for editors.
4
5use serde::{Deserialize, Serialize};
6
7use crate::synth::Synth;
8
9/// One sense of a producer word and the rule a note list must meet for it.
10#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
11#[serde(deny_unknown_fields)]
12pub struct Entry {
13    /// Stable id, `<group>.<word>` or `<group>.<word>.<sense>`.
14    pub id: String,
15    /// The topic the entry belongs to.
16    pub group: Group,
17    /// The word a producer uses.
18    pub word: String,
19    /// Other spellings and names of the word.
20    pub aliases: Vec<String>,
21    /// The plain meaning, in our own words.
22    pub meaning: String,
23    /// The sense id within the word, for a word with several senses.
24    pub sense: Option<String>,
25    /// The sense to take when the request gives no context.
26    pub default: bool,
27    /// Ids of the other senses of the word and of entries this one cannot
28    /// share a note list with.
29    pub conflicts: Vec<String>,
30    /// The checkable note rule.
31    pub rule: Rule,
32    /// A fragment that meets the rule; none for [`Rule::None`].
33    pub example_pass: Option<Fragment>,
34    /// A fragment that breaks the rule; none for [`Rule::None`].
35    pub example_fail: Option<Fragment>,
36    /// The roles the entry applies to.
37    pub roles: Vec<String>,
38    /// Ids of the genre entries the entry belongs to.
39    pub genres: Vec<String>,
40    /// How much the entry is needed.
41    pub tier: Tier,
42    /// Independent publishers who state the claim, an upper bound.
43    pub n_sources: u16,
44    /// Ids from `dictionary/sources.json`.
45    pub source: Vec<String>,
46    /// How the claim is known.
47    pub tag: Tag,
48    /// The day the claim was last read, `YYYY-MM-DD`.
49    pub verified: String,
50    /// Whether the word alone names notes.
51    pub notes_trigger: bool,
52    /// The preset controls the entry needs, per synth.
53    pub controls: Vec<Control>,
54    /// Chord entries: the spellings after the root.
55    pub symbols: Vec<String>,
56    /// Genre and rhythm entries: the tempo range.
57    pub bpm: Option<Bpm>,
58    /// Progression entries: the scale for the chord-root check.
59    pub scale: Option<String>,
60    /// Ask words: the one question and its options.
61    pub ask: Option<Ask>,
62    /// Mood words: the scale to pick when the producer names none.
63    pub default_pick: Option<DefaultPick>,
64}
65
66/// The topic of an entry. Each topic file holds one or more groups.
67#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
68#[serde(rename_all = "kebab-case")]
69#[non_exhaustive]
70pub enum Group {
71    /// Note names, middle C and spelling.
72    Notation,
73    /// Key names, Camelot, Open Key and Roman numerals.
74    Key,
75    /// The pitch range and default voice of a role.
76    Role,
77    /// Scales and modes.
78    Scale,
79    /// Mood words with a default scale.
80    Mood,
81    /// The snap rule and the allowed outside notes.
82    Outside,
83    /// Chord qualities and symbols.
84    Chord,
85    /// Voicing words and the low-interval limits.
86    Voicing,
87    /// Voice-leading rules.
88    VoiceLeading,
89    /// Named progressions.
90    Progression,
91    /// Harmonic rhythm, borrowed chords and genre chord habits.
92    Harmony,
93    /// Pulse and position words.
94    Position,
95    /// Swing.
96    Swing,
97    /// Feel and humanize words.
98    Feel,
99    /// Named rhythms.
100    Rhythm,
101    /// Euclidean patterns.
102    Euclid,
103    /// Note length and articulation.
104    Articulation,
105    /// Bass techniques.
106    Bass,
107    /// Lead and phrase techniques.
108    Lead,
109    /// Arpeggio techniques.
110    Arp,
111    /// Pad, stab and keys techniques.
112    Pad,
113    /// Phrase structure.
114    Phrase,
115    /// Genre templates.
116    Genre,
117}
118
119impl Group {
120    /// The text form, as in the data.
121    #[must_use]
122    pub fn as_str(self) -> &'static str {
123        match self {
124            Self::Notation => "notation",
125            Self::Key => "key",
126            Self::Role => "role",
127            Self::Scale => "scale",
128            Self::Mood => "mood",
129            Self::Outside => "outside",
130            Self::Chord => "chord",
131            Self::Voicing => "voicing",
132            Self::VoiceLeading => "voice-leading",
133            Self::Progression => "progression",
134            Self::Harmony => "harmony",
135            Self::Position => "position",
136            Self::Swing => "swing",
137            Self::Feel => "feel",
138            Self::Rhythm => "rhythm",
139            Self::Euclid => "euclid",
140            Self::Articulation => "articulation",
141            Self::Bass => "bass",
142            Self::Lead => "lead",
143            Self::Arp => "arp",
144            Self::Pad => "pad",
145            Self::Phrase => "phrase",
146            Self::Genre => "genre",
147        }
148    }
149}
150
151/// How much an entry is needed. M1 entries must all land.
152#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
153pub enum Tier {
154    /// Needed in a first test set.
155    M1,
156    /// One or two sources, or a jazz or world idiom with a rule.
157    M2,
158    /// Single source, inferred or theory only.
159    M3,
160}
161
162impl Tier {
163    /// Every tier, in order.
164    pub const ALL: [Self; 3] = [Self::M1, Self::M2, Self::M3];
165
166    /// The smallest share of the planned entries that must land, in
167    /// percent.
168    #[must_use]
169    pub fn floor(self) -> u8 {
170        match self {
171            Self::M1 => 100,
172            Self::M2 => 50,
173            Self::M3 => 25,
174        }
175    }
176}
177
178/// How a claim is known. One tag per cell, never two.
179#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
180#[serde(rename_all = "lowercase")]
181pub enum Tag {
182    /// A source states it.
183    #[serde(rename = "source states")]
184    SourceStates,
185    /// Measured on data.
186    Measured,
187    /// Our computation on public data.
188    Derived,
189    /// Our reading.
190    Inferred,
191    /// Rests on a search excerpt.
192    Snippet,
193    /// One publisher states it.
194    #[serde(rename = "single source")]
195    SingleSource,
196    /// An invented value, a policy.
197    Proposal,
198}
199
200impl Tag {
201    /// The text form, as in the data.
202    #[must_use]
203    pub fn as_str(self) -> &'static str {
204        match self {
205            Self::SourceStates => "source states",
206            Self::Measured => "measured",
207            Self::Derived => "derived",
208            Self::Inferred => "inferred",
209            Self::Snippet => "snippet",
210            Self::SingleSource => "single source",
211            Self::Proposal => "proposal",
212        }
213    }
214}
215
216/// The checkable note rule of an entry. The kind list is fixed; free text
217/// is rejected.
218#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
219#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
220pub enum Rule {
221    /// Every pitch class lies in `offsets` above the root.
222    PitchSet {
223        /// Semitones above the root.
224        offsets: Vec<u8>,
225        /// Offsets a line shows at least once per 4 bars to claim the
226        /// scale.
227        colour: Vec<u8>,
228        /// Offsets that name a neighbouring scale instead.
229        giveaway: Vec<u8>,
230    },
231    /// The first or last note of a phrase, or the held bass note, is the
232    /// root.
233    Tonic {},
234    /// The chord holds these tones above its root.
235    ChordTones {
236        /// Semitones above the chord root; 14 is a ninth.
237        offsets: Vec<u8>,
238        /// Tones of which at least some must also sound.
239        choose: Option<Choose>,
240    },
241    /// The spacing of a chord, or the motion between chords.
242    Voicing(Box<VoicingRule>),
243    /// A chord loop: roots above the tonic and a chord per root.
244    Cycle {
245        /// Semitones above the tonic, one per chord.
246        roots: Vec<u8>,
247        /// Chord entry ids, one per root.
248        chords: Vec<String>,
249        /// Bars per chord, when the entry fixes it.
250        bars_per_chord: Option<f64>,
251    },
252    /// Onsets fall on these slots, per part.
253    OnsetSet {
254        /// The slots of each part.
255        parts: Vec<PartSlots>,
256    },
257    /// At least `min_share` of the onsets fall on `slots`.
258    OnsetShare {
259        /// The slots that count.
260        slots: Vec<u8>,
261        /// The smallest share, 0 to 1.
262        min_share: f64,
263    },
264    /// The number of notes per bar.
265    CountPerBar {
266        /// The fewest notes.
267        min: u8,
268        /// The most notes.
269        max: u8,
270    },
271    /// The sounding length, `len` times `gate`.
272    Length {
273        /// Beats, or a share of the grid step.
274        unit: LengthUnit,
275        /// The shortest length.
276        min: Option<f64>,
277        /// The longest length.
278        max: Option<f64>,
279    },
280    /// The velocity band.
281    Velocity {
282        /// The lowest velocity.
283        min: u8,
284        /// The highest velocity.
285        max: u8,
286    },
287    /// The next note starts before this one ends.
288    Overlap {
289        /// The smallest overlap in ms.
290        min_ms: f64,
291    },
292    /// The second note of each pair starts late.
293    Swing {
294        /// 16 for a pair of sixteenths, 8 for a pair of eighths.
295        note: u8,
296        /// The lowest pair percent.
297        min_pct: f64,
298        /// The highest pair percent.
299        max_pct: f64,
300    },
301    /// Every onset moves by the same amount, late positive.
302    Shift {
303        /// The smallest shift in ms.
304        min_ms: f64,
305        /// The largest shift in ms.
306        max_ms: f64,
307    },
308    /// Onsets and velocities vary at random.
309    Jitter {
310        /// The largest onset deviation in ms.
311        max_ms: f64,
312        /// The largest velocity deviation.
313        velocity: u8,
314    },
315    /// The pitch range and default voice of a role.
316    Register {
317        /// Where most notes sit, inclusive.
318        core: [u8; 2],
319        /// The limits outside which a note is rejected, inclusive.
320        hard: [u8; 2],
321        /// Mono, poly or either.
322        voice: Voice,
323    },
324    /// A Euclidean pattern: `k` onsets spread over `n` steps.
325    Pattern {
326        /// Onsets.
327        k: u8,
328        /// Steps.
329        n: u8,
330        /// Steps the pattern is turned by.
331        rotation: u8,
332    },
333    /// Chord notes one per step.
334    Arp {
335        /// The play order.
336        order: ArpOrder,
337        /// Beats per step.
338        rate: f64,
339        /// The gate range in percent of the step.
340        gate: Option<[u8; 2]>,
341        /// `sequence`: chord degrees in play order.
342        #[serde(default, skip_serializing_if = "Vec::is_empty")]
343        degrees: Vec<u8>,
344    },
345    /// Bars identical to an earlier bar.
346    Repeat {
347        /// The smallest share of identical bars.
348        min_share: Option<f64>,
349        /// The largest share of identical bars.
350        max_share: Option<f64>,
351        /// The fewest bars the repetition lasts.
352        min_bars: Option<u8>,
353    },
354    /// Change events fall on bars `1 + k * every`.
355    PhraseLength {
356        /// The bar counts between changes.
357        every: Vec<u8>,
358    },
359    /// The word names no note rule.
360    None {},
361}
362
363impl Rule {
364    /// The kind name, as in the data.
365    #[must_use]
366    pub fn kind(&self) -> &'static str {
367        match self {
368            Self::PitchSet { .. } => "pitch_set",
369            Self::Tonic {} => "tonic",
370            Self::ChordTones { .. } => "chord_tones",
371            Self::Voicing(_) => "voicing",
372            Self::Cycle { .. } => "cycle",
373            Self::OnsetSet { .. } => "onset_set",
374            Self::OnsetShare { .. } => "onset_share",
375            Self::CountPerBar { .. } => "count_per_bar",
376            Self::Length { .. } => "length",
377            Self::Velocity { .. } => "velocity",
378            Self::Overlap { .. } => "overlap",
379            Self::Swing { .. } => "swing",
380            Self::Shift { .. } => "shift",
381            Self::Jitter { .. } => "jitter",
382            Self::Register { .. } => "register",
383            Self::Pattern { .. } => "pattern",
384            Self::Arp { .. } => "arp",
385            Self::Repeat { .. } => "repeat",
386            Self::PhraseLength { .. } => "phrase_length",
387            Self::None {} => "none",
388        }
389    }
390}
391
392/// Tones of which at least `at_least` must sound.
393#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
394#[serde(deny_unknown_fields)]
395pub struct Choose {
396    /// The candidate tones, in semitones above the root.
397    pub from: Vec<u8>,
398    /// How many of them must sound.
399    pub at_least: u8,
400}
401
402/// The parameters of a voicing rule. `shape` names the test; the numbers
403/// that shape uses are set and the rest are absent.
404#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
405#[serde(deny_unknown_fields)]
406pub struct VoicingRule {
407    /// The test the evaluator runs.
408    pub shape: VoicingShape,
409    /// The widest span from the lowest to the highest tone.
410    #[serde(default, skip_serializing_if = "Option::is_none")]
411    pub max_span: Option<u8>,
412    /// The narrowest span.
413    #[serde(default, skip_serializing_if = "Option::is_none")]
414    pub min_span: Option<u8>,
415    /// The widest gap between adjacent tones.
416    #[serde(default, skip_serializing_if = "Option::is_none")]
417    pub max_gap: Option<u8>,
418    /// The narrowest gap between adjacent tones.
419    #[serde(default, skip_serializing_if = "Option::is_none")]
420    pub min_gap: Option<u8>,
421    /// The interval between adjacent tones.
422    #[serde(default, skip_serializing_if = "Option::is_none")]
423    pub step: Option<u8>,
424    /// How far a step may differ from `step`.
425    #[serde(default, skip_serializing_if = "Option::is_none")]
426    pub step_tolerance: Option<u8>,
427    /// The fewest notes.
428    #[serde(default, skip_serializing_if = "Option::is_none")]
429    pub min_notes: Option<u8>,
430    /// The most notes.
431    #[serde(default, skip_serializing_if = "Option::is_none")]
432    pub max_notes: Option<u8>,
433    /// The fewest distinct pitch classes.
434    #[serde(default, skip_serializing_if = "Option::is_none")]
435    pub min_pitch_classes: Option<u8>,
436    /// No tone below this pitch.
437    #[serde(default, skip_serializing_if = "Option::is_none")]
438    pub lowest: Option<u8>,
439    /// With `max_below`: the pitch under which tones are counted.
440    #[serde(default, skip_serializing_if = "Option::is_none")]
441    pub below: Option<u8>,
442    /// The most tones under `below`.
443    #[serde(default, skip_serializing_if = "Option::is_none")]
444    pub max_below: Option<u8>,
445    /// Voices counted from the top that move down an octave.
446    #[serde(default, skip_serializing_if = "Vec::is_empty")]
447    pub drop: Vec<u8>,
448    /// The chord tone in the bass.
449    #[serde(default, skip_serializing_if = "Option::is_none")]
450    pub bass: Option<BassTone>,
451    /// Chord degrees from the bottom up.
452    #[serde(default, skip_serializing_if = "Vec::is_empty")]
453    pub degrees: Vec<u8>,
454    /// The largest leap of an upper voice between chords.
455    #[serde(default, skip_serializing_if = "Option::is_none")]
456    pub max_leap: Option<u8>,
457    /// The lowest pitch of the lower note of each interval.
458    #[serde(default, skip_serializing_if = "Vec::is_empty")]
459    pub floors: Vec<Floor>,
460}
461
462/// The test a voicing rule runs.
463#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
464#[serde(rename_all = "kebab-case")]
465#[non_exhaustive]
466#[allow(
467    missing_docs,
468    reason = "each shape is the voicing word of the same name in the reference"
469)]
470pub enum VoicingShape {
471    Close,
472    Open,
473    Inversion,
474    Slash,
475    #[serde(rename = "drop-2")]
476    Drop2,
477    #[serde(rename = "drop-3")]
478    Drop3,
479    #[serde(rename = "drop-2-and-4")]
480    Drop2And4,
481    Shell,
482    RootlessA,
483    RootlessB,
484    Power,
485    Quartal,
486    Quintal,
487    NeoSoul,
488    Lush,
489    Wide,
490    Muddy,
491    Airy,
492    Thick,
493    IntervalFloor,
494    CommonTone,
495    NearestTone,
496    SmallestMotion,
497    NoParallels,
498    TopHold,
499    Planing,
500    LoopReturn,
501    Cluster,
502    Polychord,
503}
504
505/// The chord tone in the bass.
506#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
507#[serde(rename_all = "lowercase")]
508pub enum BassTone {
509    /// The root.
510    Root,
511    /// The third.
512    Third,
513    /// The fifth.
514    Fifth,
515    /// The seventh.
516    Seventh,
517    /// The note after the slash.
518    Named,
519}
520
521/// The lowest allowed pitch for the lower note of an interval.
522#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
523#[serde(deny_unknown_fields)]
524pub struct Floor {
525    /// The interval in semitones.
526    pub interval: u8,
527    /// The floor; none for an interval with no floor.
528    pub lowest: Option<u8>,
529}
530
531/// The slots of one part.
532#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
533#[serde(deny_unknown_fields)]
534pub struct PartSlots {
535    /// The part, such as `kick`, or `any`.
536    pub part: String,
537    /// Slots 0 to 15.
538    pub slots: Vec<u8>,
539}
540
541/// The unit of a length rule.
542#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
543#[serde(rename_all = "lowercase")]
544pub enum LengthUnit {
545    /// Beats; a quarter note is 1.
546    Beat,
547    /// A share of the grid step.
548    Step,
549}
550
551/// The default voice of a role.
552#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
553#[serde(rename_all = "lowercase")]
554pub enum Voice {
555    /// One note at a time.
556    Mono,
557    /// Chords.
558    Poly,
559    /// Either.
560    Either,
561}
562
563/// The play order of an arpeggio.
564#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
565#[serde(rename_all = "kebab-case")]
566pub enum ArpOrder {
567    /// Low to high.
568    Up,
569    /// High to low.
570    Down,
571    /// Low to high and back.
572    UpDown,
573    /// The degrees in `degrees`.
574    Sequence,
575}
576
577/// A tiny note list that meets or breaks a rule.
578#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
579#[serde(deny_unknown_fields)]
580pub struct Fragment {
581    /// The tonic or chord root the rule reads, as a pitch class.
582    pub root: Option<u8>,
583    /// The tempo, for rules in ms.
584    pub bpm: Option<f64>,
585    /// Slots per bar: 12, 16 or 24.
586    pub grid: u8,
587    /// The notes, sorted by start.
588    pub notes: Vec<FragmentNote>,
589}
590
591/// One note of a fragment.
592#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
593#[serde(deny_unknown_fields)]
594pub struct FragmentNote {
595    /// MIDI pitch, 21 to 108.
596    pub pitch: u8,
597    /// The onset slot, counted from bar 1 slot 0.
598    pub start: u16,
599    /// The written length in beats.
600    pub len: f64,
601    /// Velocity, 1 to 127.
602    pub vel: u8,
603    /// Percent of `len` that sounds.
604    #[serde(default = "full_gate")]
605    pub gate: u8,
606    /// Shift from the slot in ms, late positive.
607    #[serde(default)]
608    pub offset_ms: f64,
609    /// The drum or instrument part.
610    #[serde(default)]
611    pub part: Option<String>,
612}
613
614fn full_gate() -> u8 {
615    100
616}
617
618/// A preset control an entry needs.
619#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
620#[serde(deny_unknown_fields)]
621pub struct Control {
622    /// The synth whose registry holds the label.
623    pub synth: Synth,
624    /// The registry label.
625    pub label: String,
626    /// Which way to move it.
627    pub direction: String,
628    /// The value or step to write, with its unit.
629    pub step: String,
630    /// The order among the entry's controls.
631    pub order: u8,
632    /// Source ids.
633    pub source: Vec<String>,
634    /// `from facts`, `measured` or `uncalibrated`.
635    pub tag: String,
636}
637
638/// A tempo range.
639#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
640#[serde(deny_unknown_fields)]
641pub struct Bpm {
642    /// The slowest tempo.
643    pub low: f64,
644    /// The usual tempo, when the sources give one.
645    pub center: Option<f64>,
646    /// The fastest tempo.
647    pub high: f64,
648}
649
650/// The one question of an ask word.
651#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
652#[serde(deny_unknown_fields)]
653pub struct Ask {
654    /// The question, as the agent asks it.
655    pub question: String,
656    /// Entry ids of the senses to choose from.
657    pub options: Vec<String>,
658    /// The option a batch run takes without asking.
659    pub default: String,
660    /// When the question is asked.
661    pub when: String,
662}
663
664/// The scale a mood word picks when the producer names none. A policy, not
665/// a fact.
666#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
667#[serde(deny_unknown_fields)]
668pub struct DefaultPick {
669    /// The scale entry id, or none when the word maps to no scale.
670    pub scale: Option<String>,
671    /// Other scale entry ids the sources attach to the word.
672    pub alternatives: Vec<String>,
673    /// How the pick combines with other words.
674    pub note: String,
675}
676
677#[cfg(test)]
678mod tests {
679    use super::*;
680
681    #[test]
682    fn rules_read_their_kind_and_reject_unknown_fields() {
683        let voicing: Rule =
684            serde_json::from_str(r#"{"kind": "voicing", "shape": "drop-2", "drop": [2]}"#).unwrap();
685        assert_eq!(voicing.kind(), "voicing");
686        let Rule::Voicing(rule) = &voicing else {
687            unreachable!()
688        };
689        assert_eq!(
690            (rule.shape, rule.drop.as_slice()),
691            (VoicingShape::Drop2, &[2][..])
692        );
693        let set: Rule = serde_json::from_str(
694            r#"{"kind": "pitch_set", "offsets": [0, 2, 3], "colour": [], "giveaway": []}"#,
695        )
696        .unwrap();
697        assert_eq!(set.kind(), "pitch_set");
698        assert!(serde_json::from_str::<Rule>(r#"{"kind": "none", "text": "x"}"#).is_err());
699        assert!(
700            serde_json::from_str::<Rule>(r#"{"kind": "voicing", "shape": "drop-2", "x": 1}"#)
701                .is_err()
702        );
703        assert!(serde_json::from_str::<Rule>(r#"{"kind": "free text"}"#).is_err());
704        assert!(serde_json::from_str::<Rule>(r#"{"kind": "voicing", "shape": "pretty"}"#).is_err());
705    }
706}