Skip to main content

music21_rs/humdrum/
mod.rs

1//! Humdrum: music21's `humdrum.spineParser`, which reads `**kern` scores.
2//!
3//! A Humdrum file is a table. Each column is a *spine* -- a part -- and each
4//! line a moment; a cell holds a note (`4c#` is a quarter C sharp above
5//! middle C), a barline (`=12`), an instruction (`*clefG2`, `*M3/4`) or a
6//! dot where nothing new happens. A spine may split into two voices (`*^`)
7//! and come together again (`*v`). `**dynam` spines carry dynamics and
8//! `**text` spines the words sung, and `**harm` spines the harmony read
9//! into the music, each said by `*staff` to belong to a `**kern` spine.
10
11pub mod harm;
12mod tables;
13
14use std::collections::BTreeMap;
15
16use crate::articulations::{Articulation, ArticulationKind};
17use crate::bar::{Barline, BarlineType, RepeatDirection};
18use crate::chord::Chord;
19use crate::clef::{Clef, ClefKind};
20use crate::defaults::{FloatType, IntegerType};
21use crate::duration::{Duration, DurationType, Grace, Tuplet, TupletType};
22use crate::dynamics::Dynamic;
23use crate::error::{Error, Result};
24use crate::expressions::{Expression, Fermata, Ornament, OrnamentKind};
25use crate::instrument::Instrument;
26use crate::key::{Key, KeySignature};
27use crate::makenotation::op_frac;
28use crate::metadata::{Metadata, is_contributor_unique_name};
29use crate::meter::TimeSignature;
30use crate::notation::{BeamDirection, BeamType, StemDirection, Tie, TieType};
31use crate::note::Note;
32use crate::pitch::{Accidental, PitchOptions};
33use crate::rest::Rest;
34use crate::roman::{Minor67Default, RomanNumeral};
35use crate::stream::{Stream, StreamElement, StreamEvent, StreamKind};
36use crate::tempo::MetronomeMark;
37
38use tables::{INSTRUMENT_CLASSES, INSTRUMENTS, REFERENCE_NAMES};
39
40fn humdrum_error(message: impl Into<String>) -> Error {
41    Error::Humdrum(message.into())
42}
43
44/// The tokens that say where a spine goes rather than what it holds.
45const SPINE_PATHS: [&str; 6] = ["*+", "*-", "*^", "*v", "*x", "*"];
46
47/// A barline as a spine states it: the measure it opens.
48#[derive(Clone, Debug, Default)]
49struct Measure {
50    number: IntegerType,
51    suffix: Option<String>,
52    left: Option<Barline>,
53    right: Option<Barline>,
54}
55
56/// What a cell of a spine came to.
57// Nearly every thing is an element, so boxing it would cost more than the
58// few smaller variants waste.
59#[allow(clippy::large_enum_variant)]
60#[derive(Clone, Debug)]
61enum Thing {
62    Element(StreamElement),
63    Measure(Measure),
64    /// An instruction with no meaning here, kept for `*staff`.
65    Tandem(String),
66    /// A word of a `**text` spine.
67    Word(String),
68}
69
70/// A thing in a spine's stream: where it stands, the line it was written
71/// on -- which music21 sorts by ahead of everything but the offset -- and
72/// the voice it belongs to while a spine is split.
73#[derive(Clone, Debug)]
74struct Placed {
75    offset: FloatType,
76    line: usize,
77    /// Nought for a thing no line can be asked for any more.
78    priority: usize,
79    voice: Option<usize>,
80    thing: Thing,
81}
82
83impl Placed {
84    fn length(&self) -> FloatType {
85        match &self.thing {
86            Thing::Element(element) => element.quarter_length(),
87            _ => 0.0,
88        }
89    }
90
91    fn class_order(&self) -> i32 {
92        match &self.thing {
93            Thing::Element(element) => element.class_sort_order(),
94            Thing::Measure(_) => -20,
95            _ => 0,
96        }
97    }
98}
99
100/// music21's stream order: offset, then the priority, then the class.
101fn sort(things: &mut [Placed]) {
102    things.sort_by(|left, right| {
103        left.offset
104            .total_cmp(&right.offset)
105            .then(left.priority.cmp(&right.priority))
106            .then(left.class_order().cmp(&right.class_order()))
107    });
108}
109
110fn highest(things: &[Placed]) -> FloatType {
111    things
112        .iter()
113        .map(|thing| thing.offset + thing.length())
114        .fold(0.0, FloatType::max)
115}
116
117/// One spine: music21's `HumdrumSpine`.
118#[derive(Clone, Debug, Default)]
119struct Spine {
120    events: Vec<(String, usize)>,
121    parent: Option<usize>,
122    first_voice: bool,
123    /// The spines this one splits into, by the line it splits on.
124    splits: BTreeMap<usize, Vec<usize>>,
125    kind: String,
126    stream: Vec<Placed>,
127}
128
129/// The lengths a number names: music21's `typeFromNumDict`.
130fn type_of_number(number: u64) -> Option<DurationType> {
131    ((1..=2048).contains(&number) && number.is_power_of_two())
132        .then(|| {
133            DurationType::ALL
134                .into_iter()
135                .find(|kind| kind.quarter_length() == 4.0 / number as FloatType)
136        })
137        .flatten()
138}
139
140fn first_number(text: &str) -> Option<&str> {
141    let start = text.find(|c: char| c.is_ascii_digit())?;
142    let rest = &text[start..];
143    let end = rest
144        .find(|c: char| !c.is_ascii_digit())
145        .unwrap_or(rest.len());
146    Some(&rest[..end])
147}
148
149/// music21's `hdStringToDuration`: `4` is a quarter, `8.` a dotted eighth,
150/// `12` an eighth of a triplet, `0` a breve, and `q` a grace note.
151fn duration_of(contents: &str) -> Result<Duration> {
152    let dots = contents.matches('.').count() as u32;
153    let has_grace = contents.contains('q') || contents.contains('Q');
154    let number = first_number(contents);
155    let rational = contents.contains('%').then(|| {
156        let at = contents.find('%')?;
157        let before = &contents[..at];
158        let start = before
159            .rfind(|c: char| !c.is_ascii_digit())
160            .map_or(0, |index| index + 1);
161        let first = before[start..].parse::<FloatType>().ok()?;
162        let second = first_number(&contents[at + 1..])
163            .filter(|_| contents[at + 1..].starts_with(|c: char| c.is_ascii_digit()))?
164            .parse::<FloatType>()
165            .ok()?;
166        Some((first, second))
167    });
168    let mut duration = if let Some(Some((first, second))) = rational {
169        let plain = Duration::new(op_frac(4.0 * second / first))?;
170        match plain.type_and_dots() {
171            Some((kind, _)) if dots > 0 => Duration::from_type_with_dots(kind, dots),
172            _ => plain,
173        }
174    } else if let Some(number) = number {
175        let value: u64 = number
176            .parse()
177            .map_err(|_| humdrum_error(format!("Could not find a duration in {contents:?}")))?;
178        if value == 0 {
179            let kind = match number {
180                "000" => DurationType::Maxima,
181                "00" => DurationType::Longa,
182                _ => DurationType::Breve,
183            };
184            Duration::from_type_with_dots(kind, dots)
185        } else if let Some(kind) = type_of_number(value) {
186            Duration::from_type_with_dots(kind, dots)
187        } else {
188            // A number that is no power of two is that many in the time of
189            // the power of two below it: twelfths are triplet eighths.
190            let base = 1_u64 << value.ilog2();
191            let kind = type_of_number(base)
192                .ok_or_else(|| humdrum_error(format!("no note value for {value}")))?;
193            let divisor = gcd(value, base);
194            let mut tuplet =
195                Tuplet::new((value / divisor) as u32, (base / divisor) as u32, kind, 0);
196            if dots > 0 {
197                tuplet.set_duration_normal(Some((kind, dots)));
198            }
199            let mut duration = Duration::from_type(kind);
200            duration.append_tuplet(tuplet);
201            duration
202        }
203    } else if has_grace {
204        Duration::from_type(DurationType::Eighth)
205    } else {
206        return Err(humdrum_error(format!(
207            "Could not find a duration in {contents:?}"
208        )));
209    };
210    if has_grace {
211        duration = duration.grace_duration();
212        let mut grace = Grace::new();
213        if contents.matches('q').count() != 1 {
214            grace.set_slash(false);
215        }
216        duration.set_grace(Some(grace));
217    } else if contents.contains('P') {
218        duration = duration.grace_duration();
219        duration.set_grace(Some(Grace::appoggiatura()));
220    }
221    Ok(duration)
222}
223
224fn gcd(mut left: u64, mut right: u64) -> u64 {
225    while right != 0 {
226        (left, right) = (right, left % right);
227    }
228    left
229}
230
231/// What is read off a note's token besides the note itself.
232struct Marks {
233    tie: Option<TieType>,
234    expressions: Vec<Expression>,
235    articulations: Vec<Articulation>,
236    stem: StemDirection,
237    beams: Vec<(BeamType, Option<BeamDirection>)>,
238}
239
240fn marks_of(contents: &str) -> Marks {
241    let has = |mark: char| contents.contains(mark);
242    let tie = if has('[') {
243        Some(TieType::Start)
244    } else if has(']') {
245        Some(TieType::Stop)
246    } else if has('_') {
247        Some(TieType::Continue)
248    } else {
249        None
250    };
251    let ornament = |kind: OrnamentKind| Expression::from(Ornament::of_kind(kind));
252    let mut expressions = Vec::new();
253    if has('t') {
254        expressions.push(ornament(OrnamentKind::HalfStepTrill));
255    } else if has('T') {
256        expressions.push(ornament(OrnamentKind::WholeStepTrill));
257    }
258    if has('w') {
259        expressions.push(ornament(OrnamentKind::HalfStepInvertedMordent));
260    } else if has('W') {
261        expressions.push(ornament(OrnamentKind::WholeStepInvertedMordent));
262    } else if has('m') {
263        expressions.push(ornament(OrnamentKind::HalfStepMordent));
264    } else if has('M') {
265        expressions.push(ornament(OrnamentKind::WholeStepMordent));
266    }
267    if has('S') {
268        expressions.push(ornament(OrnamentKind::Turn));
269    } else if has('$') {
270        expressions.push(ornament(OrnamentKind::InvertedTurn));
271    } else if has('R') {
272        expressions.push(ornament(OrnamentKind::Turn));
273    }
274    if has('O') {
275        expressions.push(ornament(OrnamentKind::Ornament));
276    }
277    let mut articulations = Vec::new();
278    let mut articulation = |class: &str| {
279        if let Some(kind) = ArticulationKind::from_class_name(class) {
280            articulations.push(Articulation::of_kind(kind));
281        }
282    };
283    if has('\'') {
284        articulation("Staccato");
285    }
286    if has('"') {
287        articulation("Pizzicato");
288    }
289    if has('`') {
290        articulation("Staccatissimo");
291    }
292    if has('~') {
293        articulation("Tenuto");
294    }
295    if has('^') {
296        articulation("Accent");
297    }
298    if has(';') {
299        expressions.push(Expression::Fermata(Fermata::new()));
300    }
301    if has('v') {
302        articulation("UpBow");
303    } else if has('u') {
304        articulation("DownBow");
305    }
306    let stem = if has('/') {
307        StemDirection::Up
308    } else if has('\\') {
309        StemDirection::Down
310    } else {
311        StemDirection::Unspecified
312    };
313    let mut beams = Vec::new();
314    for (mark, beam, direction) in [
315        ('L', BeamType::Start, None),
316        ('J', BeamType::Stop, None),
317        ('k', BeamType::PartialBeam, Some(BeamDirection::Left)),
318        ('K', BeamType::PartialBeam, Some(BeamDirection::Right)),
319    ] {
320        for _ in 0..contents.matches(mark).count() {
321            beams.push((beam, direction));
322        }
323    }
324    Marks {
325        tie,
326        expressions,
327        articulations,
328        stem,
329        beams,
330    }
331}
332
333/// music21's `hdStringToNote`: a note or a rest from its token.
334fn note_of(contents: &str, given: Option<&Duration>) -> Result<StreamElement> {
335    // A token with no length is a quarter, as music21 makes it with a
336    // warning.
337    let duration = match given {
338        Some(duration) => duration.clone(),
339        None => duration_of(contents).unwrap_or_else(|_| Duration::quarter()),
340    };
341    let marks = marks_of(contents);
342    if contents.contains('r') {
343        let mut rest = Rest::new(duration);
344        rest.set_tie(marks.tie.map(Tie::new));
345        *rest.expressions_mut() = marks.expressions;
346        *rest.articulations_mut() = marks.articulations;
347        return Ok(rest.into());
348    }
349    let letters = |c: char| matches!(c, 'a'..='g' | 'A'..='G');
350    let start = contents
351        .find(letters)
352        .ok_or_else(|| humdrum_error(format!("Could not parse {contents} for note information")))?;
353    let name: Vec<char> = contents[start..]
354        .chars()
355        .take_while(|c| letters(*c))
356        .collect();
357    let first = name[0];
358    let count = name.len() as IntegerType;
359    let octave = if first.is_ascii_lowercase() {
360        3 + count
361    } else {
362        4 - count
363    };
364    let mut pitch = PitchOptions::new()
365        .step(first.to_ascii_uppercase())
366        .octave(octave)
367        .build()?;
368    let sharps = contents.matches('#').count() as IntegerType;
369    let flats = contents.matches('-').count() as IntegerType;
370    if sharps > 0 {
371        pitch.set_written_accidental(Some(Accidental::new(sharps)?));
372    } else if flats > 0 {
373        pitch.set_written_accidental(Some(Accidental::new(-flats)?));
374    } else if contents.contains('n') {
375        pitch.set_written_accidental(Some(Accidental::new("n")?));
376    }
377    let mut note = Note::from_pitch(pitch).with_duration(duration);
378    note.set_tie(marks.tie.map(Tie::new));
379    *note.expressions_mut() = marks.expressions;
380    *note.articulations_mut() = marks.articulations;
381    note.set_stem_direction(marks.stem);
382    let mut beams = note.beams().clone();
383    for (beam, direction) in marks.beams {
384        beams.append(beam, direction);
385    }
386    note.set_beams(beams);
387    Ok(note.into())
388}
389
390/// music21's `hdStringToMeasure`: the measure a barline opens, and the
391/// barline it puts at the end of the measure before.
392fn measure_of(contents: &str, has_previous: bool) -> (Measure, Barline) {
393    let mut measure = Measure::default();
394    if let Some(number) = first_number(contents) {
395        measure.number = number.parse().unwrap_or(0);
396        let after = &contents[contents.find(number).unwrap_or(0) + number.len()..];
397        measure.suffix = after
398            .chars()
399            .next()
400            .filter(char::is_ascii_lowercase)
401            .map(String::from);
402    }
403    let bar_type = if contents.contains('-') {
404        Some(BarlineType::None)
405    } else if contents.contains('\'') {
406        Some(BarlineType::Short)
407    } else if contents.contains('`') {
408        Some(BarlineType::Tick)
409    } else if contents.contains("||") || contents.contains("==") {
410        Some(BarlineType::Double)
411    } else if contents.contains("!!") {
412        Some(BarlineType::HeavyHeavy)
413    } else if contents.contains("|!") {
414        Some(BarlineType::Final)
415    } else if contents.contains("!|") {
416        Some(BarlineType::HeavyLight)
417    } else if contents.contains('|') {
418        Some(BarlineType::Regular)
419    } else {
420        None
421    };
422    let chars: Vec<char> = contents.chars().collect();
423    let beside = |colon_first: bool| {
424        chars.windows(2).any(|pair| {
425            let (colon, bar) = if colon_first {
426                (pair[0], pair[1])
427            } else {
428                (pair[1], pair[0])
429            };
430            colon == ':' && matches!(bar, '|' | '!' | '=')
431        })
432    };
433    let both = contents.matches(':').count() > 1;
434    let end_repeat = both || beside(true);
435    let start_repeat = both || beside(false);
436    let make = |direction: Option<RepeatDirection>| {
437        let mut barline = match direction {
438            Some(direction) => Barline::repeat(direction, None),
439            None => Barline::new(BarlineType::Regular),
440        };
441        if let Some(bar_type) = bar_type {
442            barline.set_bar_type(bar_type);
443        }
444        barline
445    };
446    let right = make(end_repeat.then_some(RepeatDirection::End));
447    if has_previous {
448        if start_repeat {
449            measure.left = Some(make(Some(RepeatDirection::Start)));
450        }
451    } else {
452        measure.left = Some(if start_repeat {
453            make(Some(RepeatDirection::Start))
454        } else {
455            right.clone()
456        });
457    }
458    (measure, right)
459}
460
461/// music21's `kernTandemToObject`: what an instruction in a `**kern` spine
462/// stands for.
463fn tandem_of(tandem: &str) -> Result<Option<Thing>> {
464    let element = |element: StreamElement| Ok(Some(Thing::Element(element)));
465    if SPINE_PATHS.contains(&tandem) {
466        return Ok(None);
467    }
468    if let Some(kind) = tandem.strip_prefix("*clef") {
469        let clef = match kind {
470            "-" => Clef::of_kind(ClefKind::NoClef),
471            "X" => Clef::of_kind(ClefKind::PercussionClef),
472            "Gv2" | "Gv" => Clef::of_kind(ClefKind::Treble8vbClef),
473            "G^2" | "G^" => Clef::of_kind(ClefKind::Treble8vaClef),
474            "Fv4" | "Fv" => Clef::of_kind(ClefKind::Bass8vbClef),
475            other => Clef::from_string(other, 0)
476                .map_err(|_| humdrum_error(format!("Unknown clef type {tandem} found")))?,
477        };
478        return element(clef.into());
479    }
480    if let Some(text) = tandem.strip_prefix("*MM") {
481        let mark = match text.parse::<FloatType>() {
482            Ok(number) => MetronomeMark::new(number),
483            Err(_) => {
484                let text = text.strip_prefix('[').unwrap_or(text);
485                let text = text.trim_end();
486                MetronomeMark::from_text(text.strip_suffix(']').unwrap_or(text))
487            }
488        };
489        return element(mark.into());
490    }
491    if let Some(meter) = tandem.strip_prefix("*M") {
492        let digits = |text: &str| text.chars().take_while(char::is_ascii_digit).count();
493        let top = digits(meter);
494        let bottom = meter
495            .get(top + 1..)
496            .filter(|_| top > 0 && meter[top..].starts_with('/'))
497            .map(|rest| &rest[..digits(rest)])
498            .filter(|bottom| !bottom.is_empty())
499            .ok_or_else(|| humdrum_error(format!("Incorrect meter: {tandem} found")))?;
500        let numerator: u32 = meter[..top]
501            .parse()
502            .map_err(|_| humdrum_error(format!("Incorrect meter: {tandem} found")))?;
503        let signature = match bottom {
504            "0" => TimeSignature::new(numerator * 2, 1)?,
505            "00" => TimeSignature::new(numerator * 4, 1)?,
506            "000" => TimeSignature::new(numerator * 8, 1)?,
507            _ => TimeSignature::from_ratio_string(meter)?,
508        };
509        return element(signature.into());
510    }
511    let instrument = |table: &[(&str, &str)], code: &str| -> Option<Instrument> {
512        let class = table.iter().find(|(held, _)| *held == code)?.1;
513        Instrument::of_kind(class).ok()
514    };
515    if let Some(class) = tandem.strip_prefix("*IC") {
516        return Ok(Some(match instrument(INSTRUMENT_CLASSES, class) {
517            Some(instrument) => Thing::Element(instrument.into()),
518            None => Thing::Tandem(class.to_string()),
519        }));
520    }
521    if tandem.starts_with("*IG") || tandem.starts_with("*ITr") {
522        return Ok(Some(Thing::Tandem(tandem.to_string())));
523    }
524    if let Some(code) = tandem.strip_prefix("*I") {
525        return Ok(Some(match instrument(INSTRUMENTS, code) {
526            Some(instrument) => Thing::Element(instrument.into()),
527            None => Thing::Tandem(code.to_string()),
528        }));
529    }
530    if tandem.starts_with("*k") {
531        let mut sharps = tandem.matches('#').count() as IntegerType;
532        if sharps == 0 {
533            sharps = -(tandem.matches('-').count() as IntegerType);
534        }
535        return element(KeySignature::new(sharps).into());
536    }
537    if let Some(key) = tandem.strip_suffix(':') {
538        return element(StreamElement::Key(Key::from_tonic(&key[1..])?));
539    }
540    Ok(Some(Thing::Tandem(tandem.to_string())))
541}
542
543/// What a `**kern` spine keeps from one note to the next: music21's
544/// `KernSpine` state.
545#[derive(Default)]
546struct KernState {
547    in_tuplet: bool,
548    last_note: Option<usize>,
549    beams: usize,
550    tuplet_length: FloatType,
551    wanted_length: FloatType,
552}
553
554fn with_duration(element: &mut StreamElement, change: impl FnOnce(&mut Duration)) {
555    let Some(mut duration) = element.duration().cloned() else {
556        return;
557    };
558    change(&mut duration);
559    match element {
560        StreamElement::Note(note) => note.set_duration(duration),
561        StreamElement::Chord(chord) => chord.set_duration(duration),
562        StreamElement::Rest(rest) => rest.set_duration(duration),
563        _ => {}
564    }
565}
566
567fn mark_first_tuplet(element: &mut StreamElement, kind: TupletType) {
568    with_duration(element, |duration| {
569        let mut tuplets = duration.tuplets();
570        if let Some(first) = tuplets.first_mut() {
571            first.set_tuplet_type(Some(kind));
572        }
573        duration.set_tuplets(tuplets);
574    });
575}
576
577impl KernState {
578    /// music21's `setBeamsForNote`: a note between the start and the end of
579    /// a beam carries it on.
580    fn beam(&mut self, element: &mut StreamElement) {
581        let beams = match element {
582            StreamElement::Note(note) => note.beams().clone(),
583            StreamElement::Chord(chord) => chord.beams().clone(),
584            _ => return,
585        };
586        if self.beams != 0 && beams.is_empty() {
587            let mut carried = beams;
588            for _ in 0..self.beams {
589                carried.append(BeamType::Continue, None);
590            }
591            match element {
592                StreamElement::Note(note) => note.set_beams(carried),
593                StreamElement::Chord(chord) => chord.set_beams(carried),
594                _ => {}
595            }
596        } else if let Some(first) = beams.beams().first() {
597            if first.beam_type() == Some(BeamType::Stop) {
598                self.beams = 0;
599            } else {
600                self.beams += beams
601                    .beams()
602                    .iter()
603                    .filter(|beam| beam.beam_type() != Some(BeamType::Stop))
604                    .count();
605            }
606        }
607    }
608
609    /// music21's `setTupletTypeForNote`: where a run of tuplet notes starts
610    /// and where it has filled its bracket.
611    fn tuplet(&mut self, stream: &mut [Placed], element: &mut StreamElement) {
612        let Some(duration) = element.duration().cloned() else {
613            return;
614        };
615        let tuplets = duration.tuplets();
616        if !self.in_tuplet && !tuplets.is_empty() {
617            self.in_tuplet = true;
618            self.wanted_length = tuplets[0].total_tuplet_length();
619            self.tuplet_length = duration.quarter_length();
620            mark_first_tuplet(element, TupletType::Start);
621        } else if self.in_tuplet && tuplets.is_empty() {
622            self.in_tuplet = false;
623            self.wanted_length = 0.0;
624            self.tuplet_length = 0.0;
625            if let Some(last) = self.last_note
626                && let Thing::Element(last) = &mut stream[last].thing
627            {
628                mark_first_tuplet(last, TupletType::Stop);
629            }
630        } else if self.in_tuplet {
631            self.tuplet_length = op_frac(self.tuplet_length + duration.quarter_length());
632            if self.tuplet_length >= self.wanted_length
633                && self.tuplet_length % self.wanted_length == 0.0
634            {
635                mark_first_tuplet(element, TupletType::Stop);
636                self.in_tuplet = false;
637                self.tuplet_length = 0.0;
638                self.wanted_length = 0.0;
639            }
640        }
641    }
642}
643
644/// music21's `processChordEvent`: the notes of a cell holding several.
645fn chord_of(contents: &str) -> Result<StreamElement> {
646    let mut notes: Vec<Note> = Vec::new();
647    let mut first: Option<(Duration, String)> = None;
648    for token in contents.split_whitespace() {
649        let length: String = token
650            .chars()
651            .filter(|c| c.is_ascii_digit() || matches!(c, '.' | '%'))
652            .collect();
653        let bare: String = token
654            .chars()
655            .filter(|c| !(c.is_ascii_digit() || matches!(c, '.' | '%')))
656            .collect();
657        let element = match &first {
658            Some((duration, written)) if length.is_empty() || length == *written => {
659                note_of(&bare, Some(duration))?
660            }
661            _ => {
662                let element = note_of(token, None)?;
663                if first.is_none()
664                    && let Some(duration) = element.duration()
665                {
666                    first = Some((duration.clone(), length));
667                }
668                element
669            }
670        };
671        if let StreamElement::Note(note) = element {
672            notes.push(note);
673        }
674    }
675    let last_beams = notes
676        .last()
677        .map(|note| note.beams().clone())
678        .ok_or_else(|| humdrum_error("a chord with no notes in it"))?;
679    let duration = first.map(|(duration, _)| duration).unwrap_or_default();
680    let mut chord = Chord::new(notes)?.with_duration(duration);
681    chord.set_beams(last_beams);
682    Ok(chord.into())
683}
684
685/// Appends a thing to a spine's stream, after all it holds.
686fn append(stream: &mut Vec<Placed>, line: usize, priority: usize, thing: Thing) -> usize {
687    let offset = highest(stream);
688    stream.push(Placed {
689        offset,
690        line,
691        priority,
692        voice: None,
693        thing,
694    });
695    stream.len() - 1
696}
697
698/// music21's `KernSpine.parse`.
699fn parse_kern(events: &[(String, usize)]) -> Vec<Placed> {
700    let mut stream: Vec<Placed> = Vec::new();
701    let mut state = KernState::default();
702    // The measure a barline closes, by its place in the stream.
703    let mut last_measure: Option<usize> = None;
704    for (contents, line) in events {
705        let made: Result<Option<Thing>> = (|| {
706            if contents == "." || contents.is_empty() {
707                return Ok(None);
708            }
709            if contents.starts_with('*') {
710                return tandem_of(contents);
711            }
712            if contents.starts_with('=') {
713                // music21 starts every spine off with a measure nought of
714                // its own, so the first barline always has one before it.
715                let (measure, right) = measure_of(contents, true);
716                if let Some(previous) = last_measure
717                    && let Thing::Measure(previous) = &mut stream[previous].thing
718                {
719                    previous.right = Some(right);
720                }
721                return Ok(Some(Thing::Measure(measure)));
722            }
723            if contents.starts_with('!') {
724                return Ok(None);
725            }
726            let mut element = if contents.contains(' ') {
727                chord_of(contents)?
728            } else {
729                note_of(contents, None)?
730            };
731            state.beam(&mut element);
732            state.tuplet(&mut stream, &mut element);
733            Ok(Some(Thing::Element(element)))
734        })();
735        // A cell that cannot be read is passed over, as music21 passes over
736        // it with a warning.
737        let Ok(Some(thing)) = made else {
738            continue;
739        };
740        let is_measure = matches!(thing, Thing::Measure(_));
741        let sounds = matches!(
742            &thing,
743            Thing::Element(
744                StreamElement::Note(_) | StreamElement::Chord(_) | StreamElement::Rest(_)
745            )
746        );
747        let index = append(&mut stream, *line, *line, thing);
748        if is_measure {
749            last_measure = Some(index);
750        }
751        if sounds {
752            state.last_note = Some(index);
753        }
754    }
755    stream
756}
757
758/// music21's `DynamSpine.parse` and the generic one: the marks and words of
759/// a spine that is no `**kern`, each with its line.
760fn parse_other(events: &[(String, usize)], dynamics: bool) -> Vec<Placed> {
761    let mut stream = Vec::new();
762    for (contents, line) in events {
763        let thing = if contents == "." || contents.is_empty() {
764            continue;
765        } else if contents.starts_with('*') {
766            if SPINE_PATHS.contains(&contents.as_str()) {
767                continue;
768            }
769            Thing::Tandem(contents.clone())
770        } else if contents.starts_with('=') || contents.starts_with('!') {
771            continue;
772        } else if dynamics {
773            if contents.starts_with('<') || contents.starts_with('>') {
774                continue;
775            }
776            Thing::Element(Dynamic::new(contents.as_str()).into())
777        } else {
778            Thing::Word(contents.clone())
779        };
780        append(&mut stream, *line, 0, thing);
781    }
782    stream
783}
784
785/// music21's `HarmSpine.parse`: the harmonic annotations of a `**harm`
786/// spine, each read as a roman numeral in the key the spine last named.
787///
788/// A chord carries the numeral, as every numeral this crate reads does, so
789/// the notes are the ones the figure stands for in that key. The minor
790/// sixth and seventh are read as music21 reads them here -- the sixth flat
791/// and the seventh sharp -- and a token that is no `**harm` token is passed
792/// over, which is what music21 does after warning about it.
793fn parse_harm(events: &[(String, usize)]) -> Result<Vec<Placed>> {
794    let mut stream = Vec::new();
795    // music21's `HarmSpine` starts in C major and takes every later key
796    // from the spine's own tandems.
797    let mut key = Key::from_tonic("C")?;
798    for (contents, line) in events {
799        if contents == "." || contents.is_empty() {
800            continue;
801        }
802        if contents.starts_with('*') {
803            if SPINE_PATHS.contains(&contents.as_str()) {
804                continue;
805            }
806            match contents.strip_suffix(':') {
807                Some(named) => key = Key::from_tonic(&named[1..])?,
808                // Anything else is kept as it stands, `*staff` among it,
809                // which is what says whose harmony this is.
810                None => {
811                    append(&mut stream, *line, 0, Thing::Tandem(contents.clone()));
812                }
813            }
814            continue;
815        }
816        if contents.starts_with('=') || contents.starts_with('!') {
817            continue;
818        }
819        let Some(figure) = harm::convert_harm_to_roman(contents) else {
820            continue;
821        };
822        let numeral = RomanNumeral::with_minor_defaults(
823            &figure,
824            key.clone(),
825            Minor67Default::Flat,
826            Minor67Default::Sharp,
827        )?;
828        let chord = numeral.to_chord()?.with_numeral(numeral);
829        append(&mut stream, *line, 0, Thing::Element(chord.into()));
830    }
831    Ok(stream)
832}
833
834/// music21's `createHumdrumSpines`: the spines a table's columns run
835/// through, splitting, joining and changing places as the table says.
836fn spines_of(lines: &[(usize, Vec<String>)], width: usize) -> Result<Vec<Spine>> {
837    let mut spines: Vec<Spine> = Vec::new();
838    let mut current: Vec<Option<usize>> = Vec::new();
839    for (line, cells) in lines {
840        if current.len() < width {
841            current.resize(width, None);
842        }
843        for (column, cell) in cells.iter().enumerate().take(width) {
844            let spine = match current[column] {
845                Some(spine) => spine,
846                None => {
847                    spines.push(Spine::default());
848                    current[column] = Some(spines.len() - 1);
849                    spines.len() - 1
850                }
851            };
852            spines[spine].events.push((cell.clone(), *line));
853        }
854        if !cells
855            .iter()
856            .any(|cell| SPINE_PATHS.contains(&cell.as_str()))
857        {
858            continue;
859        }
860        let mut next: Vec<Option<usize>> = Vec::new();
861        let mut merging = false;
862        let mut merge_parent: Option<usize> = None;
863        let mut exchange: Option<usize> = None;
864        for (column, spine) in current.clone().into_iter().enumerate().take(width) {
865            let Some(cell) = cells.get(column) else {
866                if spine.is_some() {
867                    next.push(spine);
868                }
869                continue;
870            };
871            let Some(spine) = spine else {
872                return Err(humdrum_error(format!(
873                    "spine-path token at line {line} has no active spine"
874                )));
875            };
876            match cell.as_str() {
877                "*-" => {}
878                "*^" => {
879                    let mut voices = Vec::new();
880                    for first in [true, false] {
881                        spines.push(Spine {
882                            parent: Some(spine),
883                            first_voice: first,
884                            ..Spine::default()
885                        });
886                        voices.push(spines.len() - 1);
887                        next.push(Some(spines.len() - 1));
888                    }
889                    spines[spine].splits.insert(*line, voices);
890                }
891                "*v" => {
892                    if merging {
893                        let parent = spines[spine].parent.or(merge_parent);
894                        match parent {
895                            Some(parent) => next.push(Some(parent)),
896                            None => {
897                                spines.push(Spine::default());
898                                next.push(Some(spines.len() - 1));
899                            }
900                        }
901                        merging = false;
902                        merge_parent = None;
903                    } else {
904                        merging = true;
905                        merge_parent = spines[spine].parent;
906                    }
907                }
908                "*x" => match exchange.take() {
909                    None => exchange = Some(spine),
910                    Some(waiting) => {
911                        next.push(Some(spine));
912                        next.push(Some(waiting));
913                    }
914                },
915                _ => next.push(Some(spine)),
916            }
917        }
918        if exchange.is_some() {
919            return Err(humdrum_error(format!(
920                "ProtoSpine found with unpaired exchange instruction at line {line}"
921            )));
922        }
923        current = next;
924    }
925    Ok(spines)
926}
927
928/// The kind of a spine: what its `**` heading says, or its parent's.
929fn kind_of(spines: &[Spine], index: usize) -> Result<String> {
930    let mut at = Some(index);
931    while let Some(spine) = at {
932        if let Some(kind) = spines[spine]
933            .events
934            .iter()
935            .find_map(|(contents, _)| contents.strip_prefix("**"))
936        {
937            return Ok(kind.to_string());
938        }
939        at = spines[spine].parent;
940    }
941    Err(humdrum_error(format!(
942        "Could not determine spineType for spine with id {index}"
943    )))
944}
945
946/// music21's `performInsertions`: the voices a spine split into, put back
947/// into it where it split.
948fn insert_voices(spines: &mut [Spine], index: usize) {
949    let mut points: Vec<usize> = spines[index].splits.keys().copied().collect();
950    if points.is_empty() {
951        return;
952    }
953    let old = std::mem::take(&mut spines[index].stream);
954    let mut new: Vec<Placed> = Vec::new();
955    let insert = |new: &mut Vec<Placed>, spines: &[Spine], point: usize| {
956        let start = highest(new);
957        for (number, child) in spines[index].splits[&point].iter().enumerate() {
958            for thing in &spines[*child].stream {
959                if !spines[*child].first_voice && matches!(thing.thing, Thing::Measure(_)) {
960                    continue;
961                }
962                let mut thing = thing.clone();
963                thing.offset += start;
964                thing.voice.get_or_insert(number + 1);
965                new.push(thing);
966            }
967        }
968    };
969    let mut last: Option<usize> = None;
970    for thing in old {
971        // Python walks the list it is taking things out of, so the point
972        // after one that is used is passed over until the next element.
973        let mut at = 0;
974        while at < points.len() {
975            let point = points[at];
976            if last.is_some_and(|last| last > point) || thing.line < point {
977                at += 1;
978                continue;
979            }
980            insert(&mut new, spines, point);
981            points.remove(at);
982            at += 1;
983        }
984        last = Some(thing.line);
985        let offset = highest(&new);
986        new.push(Placed { offset, ..thing });
987    }
988    for point in points {
989        insert(&mut new, spines, point);
990    }
991    spines[index].stream = new;
992}
993
994/// A measure while elements are gathered into it.
995struct Gathered {
996    measure: Measure,
997    offset: FloatType,
998    things: Vec<Placed>,
999}
1000
1001/// music21's `moveElementsIntoMeasures`: the barlines of a spine as the
1002/// measures its notes are in.
1003fn into_measures(mut stream: Vec<Placed>) -> (Vec<Gathered>, Vec<Placed>) {
1004    sort(&mut stream);
1005    let mut measures: Vec<Gathered> = Vec::new();
1006    let mut loose: Vec<Placed> = Vec::new();
1007    let mut current = Gathered {
1008        measure: Measure::default(),
1009        offset: 0.0,
1010        things: Vec::new(),
1011    };
1012    let mut number = 0;
1013    let mut has_one = false;
1014    for thing in stream {
1015        match thing.thing {
1016            Thing::Measure(measure) => {
1017                if number != 0 || !current.things.is_empty() {
1018                    measures.push(current);
1019                }
1020                number = measure.number;
1021                has_one |= number == 1;
1022                current = Gathered {
1023                    measure,
1024                    offset: thing.offset,
1025                    things: Vec::new(),
1026                };
1027            }
1028            _ => {
1029                if number != 0 || thing.length() != 0.0 {
1030                    let offset = thing.offset - current.offset;
1031                    current.things.push(Placed { offset, ..thing });
1032                } else {
1033                    loose.push(thing);
1034                }
1035            }
1036        }
1037    }
1038    if !current.things.is_empty() {
1039        measures.push(current);
1040    }
1041    if let Some(first) = measures.first_mut() {
1042        if !has_one {
1043            first.measure.number = 1;
1044        }
1045        // What stands at the very start outside any measure goes into the
1046        // first, but for the instructions that name nothing.
1047        let (moved, kept): (Vec<Placed>, Vec<Placed>) = loose
1048            .into_iter()
1049            .partition(|thing| thing.offset == 0.0 && matches!(thing.thing, Thing::Element(_)));
1050        first.things.extend(moved);
1051        loose = kept;
1052    }
1053    (measures, loose)
1054}
1055
1056/// music21's `TupletFixer`: a run of tuplet notes that does not fill its
1057/// bracket is written in the longest or the shortest value among them.
1058fn fix_tuplets(things: &mut [Placed]) {
1059    let tuplets = |thing: &Placed| match &thing.thing {
1060        Thing::Element(
1061            element @ (StreamElement::Note(_) | StreamElement::Chord(_) | StreamElement::Rest(_)),
1062        ) => Some(
1063            element
1064                .duration()
1065                .map(Duration::tuplets)
1066                .unwrap_or_default(),
1067        ),
1068        _ => None,
1069    };
1070    let mut groups: Vec<Vec<usize>> = Vec::new();
1071    let mut current: Vec<usize> = Vec::new();
1072    for (index, thing) in things.iter().enumerate() {
1073        let Some(held) = tuplets(thing) else {
1074            continue;
1075        };
1076        let Some(first) = held.first() else {
1077            if !current.is_empty() {
1078                groups.push(std::mem::take(&mut current));
1079            }
1080            continue;
1081        };
1082        current.push(index);
1083        if first.tuplet_type() == Some(TupletType::Stop) {
1084            groups.push(std::mem::take(&mut current));
1085        }
1086    }
1087    if !current.is_empty() {
1088        groups.push(current);
1089    }
1090    let ordinal = |kind: DurationType| DurationType::ALL.iter().position(|held| *held == kind);
1091    for group in groups {
1092        let Some(first) = tuplets(&things[group[0]]).and_then(|held| held.into_iter().next())
1093        else {
1094            continue;
1095        };
1096        let total = op_frac(first.total_tuplet_length());
1097        let mut length = 0.0;
1098        let mut smallest: Option<usize> = None;
1099        let mut largest: Option<usize> = None;
1100        for index in &group {
1101            length = op_frac(length + things[*index].length());
1102            let Some(kind) = tuplets(&things[*index])
1103                .and_then(|held| held.into_iter().next())
1104                .and_then(|tuplet| ordinal(tuplet.duration_type()))
1105            else {
1106                continue;
1107            };
1108            smallest = Some(smallest.map_or(kind, |held| held.max(kind)));
1109            largest = Some(largest.map_or(kind, |held| held.min(kind)));
1110        }
1111        if length == total || total == 0.0 {
1112            continue;
1113        }
1114        let excess = op_frac(length / total);
1115        let inverse = op_frac(1.0 / excess);
1116        let chosen = if excess.fract() == 0.0 {
1117            largest
1118        } else if inverse.fract() == 0.0 {
1119            smallest
1120        } else {
1121            None
1122        };
1123        let Some(kind) = chosen.map(|index| DurationType::ALL[index]) else {
1124            continue;
1125        };
1126        for index in group {
1127            if let Thing::Element(element) = &mut things[index].thing {
1128                with_duration(element, |duration| {
1129                    let mut held = duration.tuplets();
1130                    if let Some(first) = held.first_mut() {
1131                        let normal = first.duration_normal().map_or(0, |(_, dots)| dots);
1132                        let actual = first.duration_actual().map_or(0, |(_, dots)| dots);
1133                        first.set_duration_normal(Some((kind, normal)));
1134                        first.set_duration_actual(Some((kind, actual)));
1135                    }
1136                    duration.set_tuplets(held);
1137                });
1138            }
1139        }
1140    }
1141}
1142
1143/// music21's `makeVoices`: the things of a split spine, in the voices they
1144/// came from.
1145fn measure_stream(gathered: Gathered) -> Stream {
1146    let mut things = gathered.things;
1147    sort(&mut things);
1148    fix_tuplets(&mut things);
1149    let lowest = things
1150        .iter()
1151        .find(|thing| thing.voice == Some(1))
1152        .map(|thing| thing.offset);
1153    let mut voices: BTreeMap<usize, Vec<Placed>> = BTreeMap::new();
1154    let mut own: Vec<Placed> = Vec::new();
1155    for thing in things {
1156        match (lowest, thing.voice) {
1157            (Some(lowest), Some(voice)) => voices.entry(voice).or_default().push(Placed {
1158                offset: thing.offset - lowest,
1159                ..thing
1160            }),
1161            _ => own.push(thing),
1162        }
1163    }
1164    let events = |things: Vec<Placed>| -> Vec<(FloatType, usize, i32, StreamElement)> {
1165        things
1166            .into_iter()
1167            .filter_map(|thing| match thing.thing {
1168                Thing::Element(element) => Some((
1169                    thing.offset,
1170                    thing.priority,
1171                    element.class_sort_order(),
1172                    element,
1173                )),
1174                _ => None,
1175            })
1176            .collect()
1177    };
1178    let mut held = events(own);
1179    for (_, things) in voices {
1180        let voice = Stream::with_kind(StreamKind::Voice).with_events(
1181            events(things)
1182                .into_iter()
1183                .map(|(offset, _, _, element)| StreamEvent::new(offset, element))
1184                .collect(),
1185        );
1186        held.push((lowest.unwrap_or(0.0), 0, 5, voice.into()));
1187    }
1188    held.sort_by(|left, right| {
1189        left.0
1190            .total_cmp(&right.0)
1191            .then(left.1.cmp(&right.1))
1192            .then(left.2.cmp(&right.2))
1193    });
1194    let mut measure = Stream::with_kind(StreamKind::Measure).with_events(
1195        held.into_iter()
1196            .map(|(offset, _, _, element)| StreamEvent::new(offset, element))
1197            .collect(),
1198    );
1199    measure.set_number(gathered.measure.number);
1200    measure.set_number_suffix(gathered.measure.suffix);
1201    measure.set_left_barline(gathered.measure.left);
1202    measure.set_right_barline(gathered.measure.right);
1203    measure
1204}
1205
1206/// The staff a spine says it belongs to, or the staves.
1207fn staves_of(things: &[Placed]) -> Vec<IntegerType> {
1208    things
1209        .iter()
1210        .find_map(|thing| match &thing.thing {
1211            Thing::Tandem(tandem) => tandem.strip_prefix("*staff"),
1212            _ => None,
1213        })
1214        .map(|staves| {
1215            staves
1216                .split('/')
1217                .filter_map(|staff| staff.parse().ok())
1218                .collect()
1219        })
1220        .unwrap_or_default()
1221}
1222
1223/// music21's `GlobalReference.updateMetadata`: a `!!!` line as a fact about
1224/// the piece.
1225fn reference(metadata: &mut Metadata, line: &str) -> Result<()> {
1226    let text = line.trim_start_matches('!');
1227    let (code, value) = text
1228        .split_once(':')
1229        .ok_or_else(|| humdrum_error(format!("a reference line with no code: {line}")))?;
1230    let code = code.trim().replace("@@", "@");
1231    let code = code.split('@').next().unwrap_or("");
1232    let value = value.trim();
1233    match REFERENCE_NAMES.iter().find(|(held, _)| *held == code) {
1234        Some((_, "")) => metadata.add_text(format!("humdrum:{code}"), value),
1235        Some((_, name)) if is_contributor_unique_name(name) => {
1236            metadata.add_contributor(*name, value);
1237        }
1238        Some((_, name)) => metadata.add_text(*name, value),
1239        None => metadata.add_text(code, value),
1240    }
1241    Ok(())
1242}
1243
1244/// music21's `parseNonOpus`: one table as a score.
1245fn score_of(lines: &[&str]) -> Result<Stream> {
1246    let mut metadata = Metadata::new();
1247    let mut rows: Vec<(usize, Vec<String>)> = Vec::new();
1248    let mut width = 0;
1249    for (index, line) in lines.iter().enumerate() {
1250        let line = line.trim_end();
1251        if line.is_empty() {
1252            continue;
1253        }
1254        if line.starts_with("!!!") {
1255            reference(&mut metadata, line)?;
1256        } else if line.starts_with("!!") {
1257            continue;
1258        } else {
1259            let cells: Vec<String> = line
1260                .split('\t')
1261                .enumerate()
1262                .filter(|(place, cell)| *place == 0 || !cell.is_empty())
1263                .map(|(_, cell)| cell.to_string())
1264                .collect();
1265            width = width.max(cells.len());
1266            rows.push((index + 1, cells));
1267        }
1268    }
1269    let mut spines = spines_of(&rows, width)?;
1270    for index in 0..spines.len() {
1271        spines[index].kind = kind_of(&spines, index)?;
1272    }
1273    for spine in &mut spines {
1274        spine.stream = match spine.kind.as_str() {
1275            "kern" => parse_kern(&spine.events),
1276            "dynam" => parse_other(&spine.events, true),
1277            "harm" => parse_harm(&spine.events)?,
1278            _ => parse_other(&spine.events, false),
1279        };
1280    }
1281    for index in 0..spines.len() {
1282        if spines[index].parent.is_none() {
1283            insert_voices(&mut spines, index);
1284        }
1285    }
1286
1287    // The top spines as measures, with what stands outside them.
1288    let mut parts: Vec<(usize, Vec<Gathered>, Vec<Placed>)> = Vec::new();
1289    for (index, spine) in spines.iter().enumerate() {
1290        if spine.parent.is_none() && spine.kind == "kern" {
1291            let (measures, loose) = into_measures(spine.stream.clone());
1292            parts.push((index, measures, loose));
1293        }
1294    }
1295    // Which part each staff is: the first `*staff` a spine leaves standing
1296    // outside its measures.
1297    let staff_of = |loose: &[Placed]| -> Option<IntegerType> {
1298        loose.iter().find_map(|thing| match &thing.thing {
1299            Thing::Tandem(tandem) => tandem
1300                .strip_prefix("*staff")
1301                .and_then(|staff| staff.parse().ok()),
1302            _ => None,
1303        })
1304    };
1305    for spine in spines
1306        .iter()
1307        .filter(|spine| spine.parent.is_none() && spine.kind != "kern")
1308    {
1309        let staves = staves_of(&spine.stream);
1310        let is_words = matches!(spine.kind.as_str(), "lyrics" | "text");
1311        let is_marks = matches!(spine.kind.as_str(), "dynam" | "harm");
1312        if !is_marks && !is_words {
1313            continue;
1314        }
1315        for staff in staves {
1316            let Some((_, measures, _)) = parts
1317                .iter_mut()
1318                .find(|(_, _, loose)| staff_of(loose) == Some(staff))
1319            else {
1320                if is_marks {
1321                    return Err(humdrum_error(format!(
1322                        "no staff {staff} for the {} spine",
1323                        spine.kind
1324                    )));
1325                }
1326                continue;
1327            };
1328            for thing in &spine.stream {
1329                for measure in measures.iter_mut() {
1330                    let found = measure
1331                        .things
1332                        .iter()
1333                        .position(|held| held.priority == thing.line);
1334                    match (&thing.thing, found) {
1335                        (
1336                            Thing::Element(
1337                                mark @ (StreamElement::Dynamic(_) | StreamElement::Chord(_)),
1338                            ),
1339                            Some(found),
1340                        ) => {
1341                            let offset = measure.things[found].offset;
1342                            // music21's `HarmSpine` gives its numerals the
1343                            // line they were written on as their priority,
1344                            // which sorts them after the meter and the key
1345                            // standing at the same offset; its `DynamSpine`
1346                            // gives a dynamic none.
1347                            let priority = match mark {
1348                                StreamElement::Chord(_) => thing.line,
1349                                _ => 0,
1350                            };
1351                            measure.things.push(Placed {
1352                                offset,
1353                                line: thing.line,
1354                                priority,
1355                                voice: None,
1356                                thing: Thing::Element(mark.clone()),
1357                            });
1358                            break;
1359                        }
1360                        (Thing::Word(word), Some(_)) => {
1361                            for held in &mut measure.things {
1362                                if held.priority != thing.line {
1363                                    continue;
1364                                }
1365                                match &mut held.thing {
1366                                    Thing::Element(StreamElement::Note(note)) => {
1367                                        note.set_lyric(Some(word))?;
1368                                    }
1369                                    Thing::Element(StreamElement::Rest(rest)) => {
1370                                        rest.set_lyric(Some(word));
1371                                    }
1372                                    Thing::Element(StreamElement::Chord(chord)) => {
1373                                        if let Some(singer) = chord.notes_mut().first_mut() {
1374                                            singer.set_lyric(Some(word))?;
1375                                        }
1376                                    }
1377                                    _ => {}
1378                                }
1379                            }
1380                        }
1381                        _ => {}
1382                    }
1383                }
1384            }
1385        }
1386    }
1387
1388    // The parts come out in the order opposite to the file's, which is
1389    // written lowest part first.
1390    let mut events = Vec::new();
1391    for (_, measures, _) in parts.into_iter().rev() {
1392        let mut held = Vec::new();
1393        let mut offset = 0.0;
1394        for gathered in measures {
1395            let measure = measure_stream(gathered);
1396            let length = measure.end_offset();
1397            held.push(StreamEvent::new(offset, measure));
1398            offset = op_frac(offset + length);
1399        }
1400        events.push(StreamEvent::new(
1401            0.0,
1402            Stream::with_kind(StreamKind::Part).with_events(held),
1403        ));
1404    }
1405    let mut score = Stream::with_kind(StreamKind::Score).with_events(events);
1406    score.set_metadata(Some(metadata));
1407    Ok(score)
1408}
1409
1410/// Reads a Humdrum file into a score: music21's `converter.parse` of one.
1411///
1412/// Every `**kern` spine is a part, the last in the file first; its barlines
1413/// are the measures, its instructions the clefs, keys, meters, tempos and
1414/// instruments, and a spine split in two is two voices in the measures where
1415/// it is split. `**dynam` and `**text` spines put their dynamics and words
1416/// on the notes of the staff they name. A cell that cannot be read is passed
1417/// over, as music21 passes over it.
1418///
1419/// A file holding several tables, each closed before the next opens, comes
1420/// back as an opus of scores. `**harm` spines, and a voice that splits
1421/// again, are not read.
1422///
1423/// ```
1424/// use music21_rs::humdrum::from_humdrum;
1425///
1426/// let score = from_humdrum("**kern\n*clefG2\n*M2/4\n=1\n4c\n4d\n=2\n2e\n==\n*-\n")?;
1427/// let part = score.parts()[0];
1428/// assert_eq!(part.measures().len(), 2);
1429/// assert_eq!(part.measures()[1].pitches()[0].name_with_octave(), "E4");
1430/// # Ok::<(), music21_rs::Error>(())
1431/// ```
1432pub fn from_humdrum(text: &str) -> Result<Stream> {
1433    let lines: Vec<&str> = text.lines().collect();
1434    if lines.is_empty() {
1435        return Err(humdrum_error("Need a list of lines to parse!"));
1436    }
1437    // music21's `determineIfDataStreamIsOpus`: a line of nothing but `**`
1438    // headings opens a table, one of nothing but `*-` closes it.
1439    let all = |line: &str, test: fn(&str) -> bool| {
1440        let line = line.trim_end();
1441        !line.is_empty() && line.split('\t').all(test)
1442    };
1443    let heading: fn(&str) -> bool = |cell| {
1444        cell.strip_prefix("**").is_some_and(|name| {
1445            !name.is_empty() && name.chars().all(|c| c.is_alphanumeric() || c == '_')
1446        })
1447    };
1448    let starts: Vec<usize> = (0..lines.len())
1449        .filter(|index| all(lines[*index], heading))
1450        .collect();
1451    let mut ends: Vec<usize> = (0..lines.len())
1452        .filter(|index| all(lines[*index], |cell| cell == "*-"))
1453        .collect();
1454    if starts.len() < 2 {
1455        return score_of(&lines);
1456    }
1457    if ends.last().copied().unwrap_or(0) <= starts[starts.len() - 1] {
1458        ends.push(lines.len());
1459    }
1460    let mut pieces: Vec<&[&str]> = Vec::new();
1461    for (index, end) in ends.iter().enumerate() {
1462        let start = if index == 0 { 0 } else { ends[index - 1] + 1 };
1463        let stop = if index + 1 == ends.len() {
1464            lines.len()
1465        } else {
1466            (*end + 1).min(lines.len())
1467        };
1468        pieces.push(&lines[start.min(stop)..stop]);
1469    }
1470    if pieces.len() < 2 {
1471        return Err(humdrum_error(
1472            "Malformed Humdrum data: possibly multiple **tags without closing information.",
1473        ));
1474    }
1475    let mut events = Vec::new();
1476    let mut offset = 0.0;
1477    for (index, piece) in pieces.iter().enumerate() {
1478        let mut score = score_of(piece)?;
1479        let mut metadata = score.metadata().cloned().unwrap_or_default();
1480        metadata.set(
1481            "number",
1482            vec![crate::metadata::MetadataValue::new((index + 1).to_string())],
1483        );
1484        score.set_metadata(Some(metadata));
1485        let length = score.end_offset();
1486        events.push(StreamEvent::new(offset, score));
1487        offset = op_frac(offset + length);
1488    }
1489    Ok(Stream::with_kind(StreamKind::Opus).with_events(events))
1490}
1491
1492#[cfg(test)]
1493mod tests {
1494    use super::*;
1495
1496    #[test]
1497    fn a_length_is_a_number_and_its_dots() {
1498        assert_eq!(duration_of("4c").unwrap().quarter_length(), 1.0);
1499        assert_eq!(duration_of("8.d").unwrap().quarter_length(), 0.75);
1500        assert_eq!(duration_of("0r").unwrap().quarter_length(), 8.0);
1501        // Twelfths are eighths, three in the time of two.
1502        let triplet = duration_of("12e").unwrap();
1503        assert!((triplet.quarter_length() - 1.0 / 3.0).abs() < 1e-9);
1504        assert_eq!(triplet.tuplets()[0].actual(), 3);
1505        assert!(duration_of("qf").unwrap().is_grace());
1506        assert!(duration_of("c").is_err());
1507    }
1508
1509    #[test]
1510    fn letters_say_the_octave() {
1511        let name = |token: &str| match note_of(token, None).unwrap() {
1512            StreamElement::Note(note) => note.pitch().name_with_octave(),
1513            _ => "rest".to_string(),
1514        };
1515        assert_eq!(name("4c"), "C4");
1516        assert_eq!(name("4cc#"), "C#5");
1517        assert_eq!(name("4C"), "C3");
1518        assert_eq!(name("4BB-"), "Bb2");
1519        assert_eq!(name("4r"), "rest");
1520    }
1521
1522    #[test]
1523    fn a_barline_says_how_the_measure_before_ends() {
1524        let (measure, right) = measure_of("=12a:|!", true);
1525        assert_eq!(measure.number, 12);
1526        assert_eq!(measure.suffix.as_deref(), Some("a"));
1527        assert_eq!(right.repeat_direction(), Some(RepeatDirection::End));
1528        let (_, double) = measure_of("==", true);
1529        assert_eq!(double.bar_type(), BarlineType::Double);
1530    }
1531
1532    #[test]
1533    fn spines_are_parts_and_the_last_comes_first() {
1534        let score = from_humdrum(
1535            "**kern\t**kern\n*clefF4\t*clefG2\n*M2/4\t*M2/4\n=1\t=1\n2C\t4c\n.\t4d\n=2\t=2\n\
1536             2D\t2e\n==\t==\n*-\t*-\n",
1537        )
1538        .unwrap();
1539        let parts = score.parts();
1540        assert_eq!(parts.len(), 2);
1541        assert_eq!(parts[0].measures()[0].pitches().len(), 2);
1542        assert_eq!(parts[1].measures()[0].pitches()[0].name_with_octave(), "C3");
1543    }
1544
1545    #[test]
1546    fn a_split_spine_is_two_voices() {
1547        let score = from_humdrum("**kern\n*M2/4\n=1\n*^\n4c\t4e\n4d\t4f\n*v\t*v\n=2\n2g\n==\n*-\n")
1548            .unwrap();
1549        let part = score.parts()[0];
1550        let measures = part.measures();
1551        assert_eq!(measures[0].voices().len(), 2);
1552        assert!(measures[1].voices().is_empty());
1553    }
1554}