Skip to main content

music21_rs/noteworthy/
mod.rs

1//! NoteWorthy Composer's text format, `.nwctxt`: music21's
2//! `noteworthy.translate`.
3//!
4//! A file is a line per object, each `|Command|Name:value|Name:value`, and
5//! [`from_noteworthy`] reads it into a score of a part per staff. The binary
6//! `.nwc` format is read by [`from_nwc`], which writes it out as those lines
7//! first, as music21 does.
8
9mod binary;
10
11pub use binary::{from_nwc, nwc_lines};
12
13use crate::bar::{Barline, BarlineType, Ending, RepeatDirection};
14use crate::chord::Chord;
15use crate::clef::{Clef, ClefKind};
16use crate::defaults::{FloatType, IntegerType};
17use crate::duration::{Duration, DurationType, Tuplet};
18use crate::dynamics::Dynamic;
19use crate::error::{Error, Result};
20use crate::expressions::TextExpression;
21use crate::instrument::Instrument;
22use crate::interval::Interval;
23use crate::key::KeySignature;
24use crate::metadata::{Metadata, MetadataValue};
25use crate::notation::{Placement, Tie, TieType};
26use crate::note::Note;
27use crate::percussion::Unpitched;
28use crate::pitch::Pitch;
29use crate::pitch::accidental::Accidental;
30use crate::repeat::{RepeatExpression, RepeatExpressionKind};
31use crate::rest::Rest;
32use crate::spanner::Spanner;
33use crate::stream::{Stream, StreamElement, StreamKind};
34use crate::tempo::MetronomeMark;
35
36fn error(message: impl Into<String>) -> Error {
37    Error::Noteworthy(message.into())
38}
39
40/// Reads the text of a NoteWorthy Composer `.nwctxt` file into a score, as
41/// music21's `converter.parse` reads one.
42///
43/// Each staff is a part, every one standing at the start of the score. Its
44/// notes, chords and rests are spelled from their places on the staff under
45/// the clef in force, with the key signature's accidentals and any written
46/// earlier in the measure; slurs, ties, triplets, grace notes and the
47/// staff's first verse of lyrics are read, as are barlines and repeats,
48/// endings, clefs, keys, meters, tempos, dynamics, words, *Coda*, *Segno*,
49/// *Fine* and the jumps to them, each staff's instrument by its MIDI program,
50/// and the song's title. A percussion clef's notes are unpitched strokes.
51///
52/// music21's own habits are kept: its first two measures are both numbered
53/// nought, a staff's name keeps the quotation marks the file writes round
54/// it, words are placed above the staff where the file puts them below, and
55/// a chord with a rest beside it puts what the measure holds so far into a
56/// voice. What music21 passes over -- fonts, page setup, crescendo and
57/// diminuendo marks, performance styles, a second verse -- is passed over.
58///
59/// ```
60/// use music21_rs::noteworthy::from_noteworthy;
61///
62/// let score = from_noteworthy(
63///     "!NoteWorthyComposer(2.0)\n|AddStaff|\n|Clef|Type:Bass\n|TimeSig|Signature:4/4\n|Note|Dur:Whole|Pos:1\n",
64/// )?;
65/// let pitches = score.pitches();
66/// assert_eq!(pitches[0].name_with_octave(), "E3");
67/// # Ok::<(), music21_rs::Error>(())
68/// ```
69///
70/// # Errors
71///
72/// A file with no staff, a line music21 cannot read -- an attribute with no
73/// value, a clef, barline style, flow mark or note length it does not know,
74/// an octave shift on an alto or tenor clef -- or a number that is not one.
75pub fn from_noteworthy(text: &str) -> Result<Stream> {
76    from_lines(text.lines())
77}
78
79/// [`from_noteworthy`] over lines already split, as music21's `parseList`
80/// takes them.
81fn from_lines<'a>(lines: impl IntoIterator<Item = &'a str>) -> Result<Stream> {
82    let mut reader = Reader::new();
83    for line in lines {
84        let line = line.trim_end();
85        if !line.starts_with('|') {
86            continue;
87        }
88        let sections: Vec<&str> = line.split('|').collect();
89        let command = sections.get(1).copied().unwrap_or("");
90        let mut attributes = Attributes::default();
91        for attribute in sections.iter().skip(2) {
92            match attribute.split_once(':') {
93                Some((name, value)) => attributes.add(
94                    name,
95                    value,
96                    command == "Chord" && matches!(name, "Dur" | "Pos"),
97                ),
98                None if attribute.trim().is_empty() => {}
99                None => {
100                    return Err(error(format!(
101                        "Cannot unpack value from {attribute} in {line}"
102                    )));
103                }
104            }
105        }
106        reader.command(command, &attributes)?;
107    }
108    reader.finish()
109}
110
111/// A line's attributes, in the order written; `Dur` and `Pos` of a chord
112/// keep every value given.
113#[derive(Default)]
114struct Attributes {
115    values: Vec<(String, Vec<String>)>,
116}
117
118impl Attributes {
119    fn add(&mut self, name: &str, value: &str, listed: bool) {
120        match self.values.iter_mut().find(|(known, _)| known == name) {
121            Some((_, values)) if listed => values.push(value.to_string()),
122            Some((_, values)) => *values = vec![value.to_string()],
123            None => self
124                .values
125                .push((name.to_string(), vec![value.to_string()])),
126        }
127    }
128
129    fn get(&self, name: &str) -> Option<&str> {
130        self.values
131            .iter()
132            .find(|(known, _)| known == name)
133            .and_then(|(_, values)| values.last())
134            .map(String::as_str)
135    }
136
137    fn list(&self, name: &str) -> Vec<String> {
138        self.values
139            .iter()
140            .find(|(known, _)| known == name)
141            .map(|(_, values)| values.clone())
142            .unwrap_or_default()
143    }
144
145    fn required(&self, name: &str) -> Result<&str> {
146        self.get(name)
147            .ok_or_else(|| error(format!("a line with no {name}")))
148    }
149}
150
151/// The clef notes are read under, as music21's reader names it.
152#[derive(Clone, Copy, PartialEq, Eq)]
153enum ClefState {
154    Treble,
155    TrebleDown,
156    TrebleUp,
157    Bass,
158    BassDown,
159    BassUp,
160    Alto,
161    Tenor,
162    Percussion,
163}
164
165impl ClefState {
166    /// music21's `getStepAndOctaveFromPosition` tables: the octave of the
167    /// lowest of seven positions, that position, and the steps from it.
168    fn table(self) -> (IntegerType, IntegerType, [char; 7]) {
169        const TREBLE: [char; 7] = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
170        match self {
171            Self::TrebleDown => (4, 1, TREBLE),
172            Self::TrebleUp => (6, 1, TREBLE),
173            Self::Bass | Self::Percussion => (3, -1, TREBLE),
174            Self::BassDown => (2, -1, TREBLE),
175            Self::BassUp => (4, -1, TREBLE),
176            Self::Alto => (4, 0, TREBLE),
177            Self::Tenor => (3, -5, TREBLE),
178            Self::Treble => (5, 1, TREBLE),
179        }
180    }
181}
182
183/// Something standing in a measure or a voice while the file is read.
184struct Item {
185    offset: FloatType,
186    content: Content,
187    /// The order it was put where it stands.
188    inserted: usize,
189}
190
191enum Content {
192    /// An element, with its number among the notes, chords and rests read
193    /// where it is one.
194    Element(Box<StreamElement>, Option<usize>),
195    Voice(VoiceBuilt),
196}
197
198struct VoiceBuilt {
199    id: usize,
200    items: Vec<Item>,
201}
202
203fn is_general_note(element: &StreamElement) -> bool {
204    matches!(
205        element,
206        StreamElement::Note(_)
207            | StreamElement::Chord(_)
208            | StreamElement::Rest(_)
209            | StreamElement::Unpitched(_)
210    )
211}
212
213/// The end of the last thing in a list: music21's `highestTime`.
214fn highest_time(items: &[Item]) -> FloatType {
215    items
216        .iter()
217        .map(|item| {
218            item.offset
219                + match &item.content {
220                    Content::Element(element, _) => element.quarter_length(),
221                    Content::Voice(voice) => highest_time(&voice.items),
222                }
223        })
224        .fold(0.0, FloatType::max)
225}
226
227struct MeasureBuilt {
228    number: IntegerType,
229    left: Option<Barline>,
230    right: Option<Barline>,
231    items: Vec<Item>,
232    inserted: usize,
233}
234
235impl MeasureBuilt {
236    fn new(number: IntegerType) -> Self {
237        Self {
238            number,
239            left: None,
240            right: None,
241            items: Vec::new(),
242            inserted: 0,
243        }
244    }
245
246    fn insert(&mut self, offset: FloatType, content: Content) {
247        self.items.push(Item {
248            offset,
249            content,
250            inserted: self.inserted,
251        });
252        self.inserted += 1;
253    }
254
255    /// music21's `append`.
256    fn append(&mut self, element: StreamElement, serial: Option<usize>) {
257        let at = highest_time(&self.items);
258        self.insert(at, Content::Element(Box::new(element), serial));
259    }
260
261    fn has_voices(&self) -> bool {
262        self.items
263            .iter()
264            .any(|item| matches!(item.content, Content::Voice(_)))
265    }
266
267    /// music21's truth of a stream: whether it holds anything, barlines
268    /// included.
269    fn holds_anything(&self) -> bool {
270        !self.items.is_empty() || self.left.is_some() || self.right.is_some()
271    }
272
273    /// Where music21's `getShortestStream` appends: the measure, or the
274    /// shortest of its voices, the first of those equally short.
275    fn append_shortest(&mut self, element: StreamElement, serial: Option<usize>) {
276        if !self.has_voices() {
277            self.append(element, serial);
278            return;
279        }
280        let shortest = sorted_places(&self.items)
281            .into_iter()
282            .filter(|place| matches!(self.items[*place].content, Content::Voice(_)))
283            .min_by(|left, right| {
284                voice_length(&self.items[*left]).total_cmp(&voice_length(&self.items[*right]))
285            });
286        if let Some(place) = shortest
287            && let Content::Voice(voice) = &mut self.items[place].content
288        {
289            append_to_voice(voice, element, serial);
290        }
291    }
292
293    /// Elements starting together where music21's `appendSimultaneous`
294    /// puts them: where the shortest voice ends, each in the next voice
295    /// ending there or a new one -- the notes the measure holds becoming
296    /// its first voice where it has none.
297    fn append_in_voices(&mut self, elements: Vec<(StreamElement, usize)>) {
298        let offset = if self.has_voices() {
299            self.items
300                .iter()
301                .filter(|item| matches!(item.content, Content::Voice(_)))
302                .map(voice_length)
303                .fold(FloatType::INFINITY, FloatType::min)
304        } else {
305            highest_time(&self.items)
306        };
307        if !self.has_voices() {
308            let mut voice = VoiceBuilt {
309                id: 0,
310                items: Vec::new(),
311            };
312            let notes: Vec<usize> = sorted_places(&self.items)
313                .into_iter()
314                .filter(|place| {
315                    matches!(&self.items[*place].content,
316                        Content::Element(element, _) if is_general_note(element))
317                })
318                .collect();
319            for place in &notes {
320                if let Content::Element(element, serial) = &self.items[*place].content {
321                    append_to_voice(&mut voice, (**element).clone(), *serial);
322                }
323            }
324            let mut index = 0;
325            self.items.retain(|_| {
326                let keep = !notes.contains(&index);
327                index += 1;
328                keep
329            });
330            self.insert(0.0, Content::Voice(voice));
331        }
332        let voices: Vec<usize> = sorted_places(&self.items)
333            .into_iter()
334            .filter(|place| matches!(self.items[*place].content, Content::Voice(_)))
335            .collect();
336        let mut count = voices.len();
337        let mut free: std::collections::VecDeque<usize> = voices
338            .into_iter()
339            .filter(|place| voice_length(&self.items[*place]) == offset)
340            .collect();
341        for (element, serial) in elements {
342            let place = free.pop_front().unwrap_or_else(|| {
343                self.insert(
344                    0.0,
345                    Content::Voice(VoiceBuilt {
346                        id: count,
347                        items: Vec::new(),
348                    }),
349                );
350                count += 1;
351                self.items.len() - 1
352            });
353            if let Content::Voice(voice) = &mut self.items[place].content {
354                let inserted = voice.items.len();
355                voice.items.push(Item {
356                    offset,
357                    content: Content::Element(Box::new(element), Some(serial)),
358                    inserted,
359                });
360            }
361        }
362    }
363}
364
365fn voice_length(item: &Item) -> FloatType {
366    match &item.content {
367        Content::Voice(voice) => highest_time(&voice.items),
368        Content::Element(element, _) => element.quarter_length(),
369    }
370}
371
372fn append_to_voice(voice: &mut VoiceBuilt, element: StreamElement, serial: Option<usize>) {
373    let at = highest_time(&voice.items);
374    let inserted = voice.items.len();
375    voice.items.push(Item {
376        offset: at,
377        content: Content::Element(Box::new(element), serial),
378        inserted,
379    });
380}
381
382/// The places of a list's items in music21's order: offset, then class,
383/// grace notes ahead of the rest, then the order they were put there.
384fn sorted_places(items: &[Item]) -> Vec<usize> {
385    let mut places: Vec<usize> = (0..items.len()).collect();
386    places.sort_by(|left, right| {
387        let key = |item: &Item| {
388            let (order, grace) = match &item.content {
389                Content::Element(element, _) => (
390                    element.class_sort_order(),
391                    element.duration().is_some_and(Duration::is_grace),
392                ),
393                Content::Voice(_) => (5, false),
394            };
395            (item.offset, order, !grace, item.inserted)
396        };
397        let (left, right) = (key(&items[*left]), key(&items[*right]));
398        left.0
399            .total_cmp(&right.0)
400            .then(left.1.cmp(&right.1))
401            .then(left.2.cmp(&right.2))
402            .then(left.3.cmp(&right.3))
403    });
404    places
405}
406
407/// What a part holds while it is read: its measures and instruments in the
408/// order they were appended, each where the part ended at the time.
409enum PartItem {
410    Measure(MeasureBuilt),
411    Instrument(Box<Instrument>),
412}
413
414struct PartBuilt {
415    name: Option<String>,
416    items: Vec<(FloatType, PartItem)>,
417    /// Slurs, by the numbers of the notes they join.
418    slurs: Vec<(usize, usize)>,
419}
420
421impl PartBuilt {
422    fn measures(&self) -> usize {
423        self.items
424            .iter()
425            .filter(|(_, item)| matches!(item, PartItem::Measure(_)))
426            .count()
427    }
428
429    fn end(&self) -> FloatType {
430        self.items
431            .iter()
432            .map(|(offset, item)| {
433                offset
434                    + match item {
435                        PartItem::Measure(measure) => highest_time(&measure.items),
436                        PartItem::Instrument(_) => 0.0,
437                    }
438            })
439            .fold(0.0, FloatType::max)
440    }
441
442    fn append(&mut self, item: PartItem) {
443        let at = self.end();
444        self.items.push((at, item));
445    }
446}
447
448struct Reader {
449    parts: Vec<PartBuilt>,
450    part: Option<PartBuilt>,
451    measure: MeasureBuilt,
452    measure_number: IntegerType,
453    ending: u32,
454    /// The measures an ending spans so far, the one being read included.
455    /// music21 keeps the list from one staff to the next, so an ending left
456    /// open at the end of a staff goes on into the next.
457    repeated: Vec<Place>,
458    /// Endings, as the measures they span and the ending's number.
459    endings: Vec<(Vec<Place>, u32)>,
460    metadata: Option<Metadata>,
461    clef: ClefState,
462    key: KeySignature,
463    within_slur: bool,
464    slur_start: Option<usize>,
465    within_tie: bool,
466    lyric_position: usize,
467    lyrics: Vec<String>,
468    /// Accidentals written so far in the measure, by the step and octave
469    /// they were written on.
470    accidentals: Vec<(String, &'static str)>,
471    /// How many notes, chords and rests have been read.
472    serials: usize,
473}
474
475impl Reader {
476    fn new() -> Self {
477        Self {
478            parts: Vec::new(),
479            part: None,
480            measure: MeasureBuilt::new(0),
481            measure_number: 0,
482            ending: 1,
483            repeated: Vec::new(),
484            endings: Vec::new(),
485            metadata: None,
486            clef: ClefState::Treble,
487            key: KeySignature::new(0),
488            within_slur: false,
489            slur_start: None,
490            within_tie: false,
491            lyric_position: 0,
492            lyrics: Vec::new(),
493            accidentals: Vec::new(),
494            serials: 0,
495        }
496    }
497
498    fn part(&mut self) -> Result<&mut PartBuilt> {
499        self.part
500            .as_mut()
501            .ok_or_else(|| error("an object before the first staff"))
502    }
503
504    fn command(&mut self, command: &str, attributes: &Attributes) -> Result<()> {
505        match command {
506            "AddStaff" => self.add_staff(attributes),
507            "Bar" => self.bar(attributes)?,
508            "Chord" => {
509                self.chord(attributes)?;
510                self.lyric_position += 1;
511            }
512            "Clef" => self.clef(attributes)?,
513            "Dynamic" => {
514                if let Some(style) = attributes.get("Style") {
515                    self.measure
516                        .append(StreamElement::Dynamic(Dynamic::new(style)), None);
517                }
518            }
519            "Ending" => self.ending(attributes)?,
520            "Flow" => self.flow(attributes)?,
521            "Key" => self.key(attributes)?,
522            "Lyric1" => self.lyrics = lyrics(attributes.required("Text")?),
523            "Note" => {
524                self.note(attributes)?;
525                self.lyric_position += 1;
526            }
527            "RestChord" => {
528                self.rest_chord(attributes)?;
529                self.lyric_position += 1;
530            }
531            "Rest" => {
532                let mut rest = Rest::new(Duration::quarter());
533                let serial = self.serial();
534                let duration = self.duration(attributes.required("Dur")?, serial)?;
535                rest.set_duration(duration);
536                self.measure
537                    .append_shortest(StreamElement::Rest(rest), Some(serial));
538            }
539            "SongInfo" => {
540                let mut metadata = Metadata::new();
541                if let Some(title) = attributes.get("Title") {
542                    metadata.set("title", vec![MetadataValue::new(title)]);
543                }
544                self.metadata = Some(metadata);
545            }
546            "StaffInstrument" => self.staff_instrument(attributes)?,
547            "Tempo" => {
548                let tempo: IntegerType = number(attributes.required("Tempo")?)?;
549                self.measure.insert(
550                    0.0,
551                    Content::Element(
552                        Box::new(StreamElement::MetronomeMark(MetronomeMark::new(
553                            FloatType::from(tempo),
554                        ))),
555                        None,
556                    ),
557                );
558            }
559            "Text" => {
560                let mut words = TextExpression::new(attributes.required("Text")?);
561                let position: IntegerType = number(attributes.required("Pos")?)?;
562                words.set_placement(Some(if position < 0 {
563                    Placement::Above
564                } else {
565                    Placement::Below
566                }));
567                self.measure
568                    .append(StreamElement::TextExpression(words), None);
569            }
570            "TimeSig" => {
571                let signature = match attributes.required("Signature")? {
572                    "AllaBreve" => "2/2",
573                    "Common" => "4/4",
574                    other => other,
575                };
576                let meter = crate::meter::TimeSignature::from_ratio_string(signature)?;
577                self.measure
578                    .append(StreamElement::TimeSignature(meter), None);
579            }
580            _ => {}
581        }
582        Ok(())
583    }
584
585    /// Where the measure being read will stand once it is closed.
586    fn place(&self) -> Result<Place> {
587        let part = self
588            .part
589            .as_ref()
590            .ok_or_else(|| error("an object before the first staff"))?;
591        Ok(Place {
592            part: self.parts.len(),
593            measure: part.measures(),
594        })
595    }
596
597    fn serial(&mut self) -> usize {
598        self.serials += 1;
599        self.serials - 1
600    }
601
602    fn add_staff(&mut self, attributes: &Attributes) {
603        self.new_part();
604        self.key = KeySignature::new(0);
605        self.accidentals.clear();
606        self.lyrics.clear();
607        self.lyric_position = 0;
608        if let Some(part) = &mut self.part {
609            part.name = attributes.get("Name").map(str::to_string);
610        }
611    }
612
613    /// music21's `createPart`: the part being read is closed with the
614    /// measure it is in, however empty, and a new one begun.
615    fn new_part(&mut self) {
616        let measure = std::mem::replace(&mut self.measure, MeasureBuilt::new(0));
617        if let Some(mut part) = self.part.take() {
618            part.append(PartItem::Measure(measure));
619            self.parts.push(part);
620        }
621        self.part = Some(PartBuilt {
622            name: None,
623            items: Vec::new(),
624            slurs: Vec::new(),
625        });
626        self.measure_number = 0;
627    }
628
629    /// Closes the measure being read and begins another numbered `number`.
630    fn close_measure(&mut self, number: IntegerType) -> Result<()> {
631        let measure = std::mem::replace(&mut self.measure, MeasureBuilt::new(number));
632        self.part()?.append(PartItem::Measure(measure));
633        Ok(())
634    }
635
636    fn bar(&mut self, attributes: &Attributes) -> Result<()> {
637        self.accidentals.clear();
638        let Some(style) = attributes.get("Style") else {
639            self.close_measure(self.measure_number)?;
640            if !self.repeated.is_empty() {
641                let next = self.place()?;
642                self.repeated.push(next);
643            }
644            self.measure_number += 1;
645            return Ok(());
646        };
647        if !self.repeated.is_empty() {
648            let spanned = std::mem::take(&mut self.repeated);
649            self.endings.push((spanned, self.ending));
650        }
651        match style {
652            "MasterRepeatOpen" | "LocalRepeatOpen" => {
653                self.close_measure(0)?;
654                self.measure.left = Some(Barline::repeat(RepeatDirection::Start, None));
655            }
656            "MasterRepeatClose" | "LocalRepeatClose" => {
657                self.measure.right = Some(Barline::repeat(RepeatDirection::End, None));
658                self.close_measure(0)?;
659            }
660            "Double" | "SectionOpen" | "SectionClose" => {
661                self.measure.right = Some(Barline::new(match style {
662                    "Double" => BarlineType::Double,
663                    "SectionOpen" => BarlineType::HeavyLight,
664                    _ => BarlineType::Final,
665                }));
666                self.close_measure(0)?;
667            }
668            other => return Err(error(format!("cannot find a style {other} in our list"))),
669        }
670        self.measure.number = self.measure_number;
671        self.measure_number += 1;
672        Ok(())
673    }
674
675    fn ending(&mut self, attributes: &Attributes) -> Result<()> {
676        let endings = attributes.required("Endings")?;
677        if self.measure.left.is_none() {
678            self.measure.left = Some(Barline::new(BarlineType::Regular));
679        }
680        self.repeated = vec![self.place()?];
681        let first = endings.split(',').next().unwrap_or("");
682        self.ending = number(first)?;
683        Ok(())
684    }
685
686    fn flow(&mut self, attributes: &Attributes) -> Result<()> {
687        let kind = match attributes.required("Style")? {
688            "DCalFine" => RepeatExpressionKind::DaCapoAlFine,
689            "Coda" | "ToCoda" => RepeatExpressionKind::Coda,
690            "Segno" => RepeatExpressionKind::Segno,
691            "DSalCoda" => RepeatExpressionKind::DalSegnoAlCoda,
692            "Fine" => RepeatExpressionKind::Fine,
693            _ => return Err(error("Cannot get style from a flow mark")),
694        };
695        self.measure.append(
696            StreamElement::RepeatExpression(RepeatExpression::new(kind)),
697            None,
698        );
699        Ok(())
700    }
701
702    fn clef(&mut self, attributes: &Attributes) -> Result<()> {
703        let shift = match attributes.get("OctaveShift") {
704            None => 0,
705            Some("Octave Down") => -1,
706            Some("Octave Up") => 1,
707            Some(other) => {
708                return Err(error(format!(
709                    "Did not get a proper octave shift from {other}"
710                )));
711            }
712        };
713        let kind = attributes.required("Type")?;
714        let (clef, state) = match (kind, shift) {
715            ("Treble", 0) => (ClefKind::TrebleClef, ClefState::Treble),
716            ("Treble", -1) => (ClefKind::Treble8vbClef, ClefState::TrebleDown),
717            ("Treble", _) => (ClefKind::Treble8vaClef, ClefState::TrebleUp),
718            ("Bass", 0) => (ClefKind::BassClef, ClefState::Bass),
719            ("Bass", -1) => (ClefKind::Bass8vbClef, ClefState::BassDown),
720            ("Bass", _) => (ClefKind::Bass8vaClef, ClefState::BassUp),
721            ("Alto", 0) => (ClefKind::AltoClef, ClefState::Alto),
722            ("Alto", _) => return Err(error("cannot shift octaves on an alto clef")),
723            ("Tenor", 0) => (ClefKind::TenorClef, ClefState::Tenor),
724            ("Tenor", _) => return Err(error("cannot shift octaves on a tenor clef")),
725            ("Percussion", _) => (ClefKind::PercussionClef, ClefState::Percussion),
726            (other, _) => {
727                return Err(error(format!(
728                    "Did not find a proper clef in type, {other}"
729                )));
730            }
731        };
732        let mut clef = Clef::of_kind(clef);
733        if state == ClefState::Percussion {
734            clef.set_line(Some(2));
735        }
736        self.measure.append(StreamElement::Clef(clef), None);
737        self.clef = state;
738        Ok(())
739    }
740
741    fn key(&mut self, attributes: &Attributes) -> Result<()> {
742        let signature = attributes.required("Signature")?;
743        let sharps = signature.chars().fold(0, |sharps, mark| match mark {
744            '#' => sharps + 1,
745            'b' => sharps - 1,
746            _ => sharps,
747        });
748        self.key = KeySignature::new(sharps);
749        self.measure
750            .append(StreamElement::KeySignature(self.key.clone()), None);
751        Ok(())
752    }
753
754    fn staff_instrument(&mut self, attributes: &Attributes) -> Result<()> {
755        let patch: IntegerType = attributes.get("Patch").map_or(Ok(0), number)?;
756        let program = u8::try_from(patch).map_err(|_| error(format!("no MIDI program {patch}")))?;
757        let mut instrument = Instrument::from_midi_program(program)?;
758        let transposition: IntegerType = attributes.get("Trans").map_or(Ok(0), number)?;
759        instrument.set_transposition(Some(Interval::from_semitones(transposition)?));
760        self.part()?
761            .append(PartItem::Instrument(Box::new(instrument)));
762        Ok(())
763    }
764
765    /// music21's `setDurationForObject`: the length a `Dur` value gives,
766    /// and the slur it begins or ends.
767    fn duration(&mut self, written: &str, serial: usize) -> Result<Duration> {
768        let mut parts = written.split(',');
769        let length = match parts.next().unwrap_or("") {
770            "Whole" => 4.0,
771            "Half" => 2.0,
772            "4th" => 1.0,
773            "8th" => 0.5,
774            "16th" => 0.25,
775            "32nd" => 0.125,
776            "64th" => 0.0625,
777            other => return Err(error(format!("no note length {other}"))),
778        };
779        let mut duration = Duration::new(length)?;
780        let mut slurred = false;
781        for part in written.split(',') {
782            match part {
783                "Grace" => duration = duration.grace_duration(),
784                "Slur" => {
785                    if !self.within_slur {
786                        self.slur_start = Some(serial);
787                    }
788                    slurred = true;
789                }
790                "Dotted" => duration = with_dots(&duration, 1),
791                "DblDotted" => duration = with_dots(&duration, 2),
792                "Triplet" | "Triplet=First" | "Triplet=End" => {
793                    let value = duration
794                        .type_and_dots()
795                        .map_or(DurationType::Quarter, |(value, _)| value);
796                    duration.append_tuplet(Tuplet::new(3, 2, value, 0));
797                }
798                _ => {}
799            }
800        }
801        if self.within_slur && !slurred {
802            if let (Some(start), Some(part)) = (self.slur_start, self.part.as_mut()) {
803                part.slurs.push((start, serial));
804            }
805            self.within_slur = false;
806        } else {
807            self.within_slur = slurred;
808        }
809        Ok(duration)
810    }
811
812    /// music21's `setTieFromPitchInfo`: whether a note begins or ends a tie.
813    fn tie(&mut self, position: &str) -> Option<Tie> {
814        if position.ends_with('^') {
815            let begins = !self.within_tie;
816            self.within_tie = true;
817            return begins.then(|| Tie::new(TieType::Start));
818        }
819        if self.within_tie {
820            self.within_tie = false;
821            return Some(Tie::new(TieType::Stop));
822        }
823        None
824    }
825
826    /// music21's `getPitchFromPositionInfo`: a pitch from one `Pos` value,
827    /// its tie and stem marks dropped.
828    fn pitch(&mut self, position: &str) -> Result<Pitch> {
829        let position = position
830            .trim_end_matches('^')
831            .trim_end_matches('x')
832            .trim_end_matches('X')
833            .trim_end_matches('z');
834        let (accidental, place) = match position.chars().next() {
835            Some('n') => (Some("natural"), &position[1..]),
836            Some('b') => (Some("flat"), &position[1..]),
837            Some('#') => (Some("sharp"), &position[1..]),
838            Some('x') => (Some("double-sharp"), &position[1..]),
839            Some('v') => (Some("double-flat"), &position[1..]),
840            _ => (None, position),
841        };
842        let place: IntegerType = number(place)?;
843        let (mut octave, lowest, steps) = self.clef.table();
844        let mut place = place;
845        while place < lowest || place > lowest + 6 {
846            if place < lowest {
847                place += 7;
848                octave -= 1;
849            }
850            if place > lowest + 6 {
851                place -= 7;
852                octave += 1;
853            }
854        }
855        let step = steps[usize::try_from(place - lowest).unwrap_or(0)];
856        let name = format!("{step}{octave}");
857        let mut pitch = Pitch::from_name(&name)?;
858        if let Some(accidental) = accidental {
859            pitch.set_accidental(Accidental::new(accidental)?);
860            self.accidentals.retain(|(written, _)| written != &name);
861            self.accidentals.push((name, accidental));
862        } else if let Some((_, accidental)) = self
863            .accidentals
864            .iter()
865            .find(|(written, _)| written == &name)
866        {
867            pitch.set_accidental(Accidental::new(*accidental)?);
868        } else if let Some(accidental) = self.key.accidental_by_step(step)? {
869            pitch.set_accidental(accidental);
870        }
871        Ok(pitch)
872    }
873
874    fn lyric(&self) -> Option<&str> {
875        self.lyrics.get(self.lyric_position).map(String::as_str)
876    }
877
878    fn note(&mut self, attributes: &Attributes) -> Result<()> {
879        let written = attributes.required("Dur")?;
880        let position = attributes.required("Pos")?;
881        let pitch = self.pitch(position)?;
882        let serial = self.serial();
883        let duration = self.duration(written, serial)?;
884        let tie = self.tie(position);
885        let lyric = self.lyric().map(str::to_string);
886        let element = if self.clef == ClefState::Percussion {
887            let octave = pitch.octave().unwrap_or(4);
888            let step = pitch.step().as_char();
889            let mut stroke = Unpitched::displayed_at(step, octave)?;
890            let note = stroke.written_mut();
891            note.set_duration(duration);
892            if tie.is_some() {
893                note.set_tie(tie);
894            }
895            if let Some(lyric) = lyric {
896                note.add_lyric(&lyric, None, false)?;
897            }
898            StreamElement::Unpitched(stroke)
899        } else {
900            let mut note = Note::from_pitch(pitch).with_duration(duration);
901            if tie.is_some() {
902                note.set_tie(tie);
903            }
904            if let Some(lyric) = lyric {
905                note.add_lyric(&lyric, None, false)?;
906            }
907            StreamElement::Note(note)
908        };
909        self.measure.append_shortest(element, Some(serial));
910        Ok(())
911    }
912
913    /// music21's `translateChord`: a chord for each `Dur` and the `Pos`
914    /// beside it, and one more for `Dur2` and `Pos2`, all starting together.
915    fn chord(&mut self, attributes: &Attributes) -> Result<()> {
916        attributes.required("Dur")?;
917        attributes.required("Pos")?;
918        let mut groups: Vec<(String, String)> = attributes
919            .list("Dur")
920            .into_iter()
921            .zip(attributes.list("Pos"))
922            .collect();
923        if let Some(written) = attributes.get("Dur2") {
924            groups.push((
925                written.to_string(),
926                attributes.required("Pos2")?.to_string(),
927            ));
928        }
929        let mut chords = Vec::new();
930        for (written, position) in &groups {
931            chords.push(self.chord_of(written, position)?);
932        }
933        self.simultaneous(chords)
934    }
935
936    /// music21's `translateRestChord`: the chord of `Dur2` and `Pos2`, and a
937    /// rest of `Dur` sounding with it.
938    fn rest_chord(&mut self, attributes: &Attributes) -> Result<()> {
939        let chord = self.chord_of(attributes.required("Dur2")?, attributes.required("Pos2")?)?;
940        let serial = self.serial();
941        let mut rest = Rest::new(Duration::quarter());
942        rest.set_duration(self.duration(attributes.required("Dur")?, serial)?);
943        self.simultaneous(vec![chord, (StreamElement::Rest(rest), serial)])
944    }
945
946    /// music21's `createChord`: one group of a chord's notes.
947    fn chord_of(&mut self, written: &str, position: &str) -> Result<(StreamElement, usize)> {
948        let serial = self.serial();
949        let duration = self.duration(written, serial)?;
950        let mut pitches = Vec::new();
951        for one in position.split(',') {
952            pitches.push(self.pitch(one)?);
953        }
954        let notes: Vec<Note> = pitches.into_iter().map(Note::from_pitch).collect();
955        let mut chord = Chord::new(notes)?.with_duration(duration);
956        if let Some(tie) = self.tie(position) {
957            for note in chord.notes_mut() {
958                note.set_tie(Some(tie.clone()));
959            }
960        }
961        Ok((StreamElement::Chord(chord), serial))
962    }
963
964    /// music21's `appendSimultaneous`: the first takes the lyric sung now,
965    /// and each goes in a voice of its own where there are more than one.
966    fn simultaneous(&mut self, mut elements: Vec<(StreamElement, usize)>) -> Result<()> {
967        if let Some(lyric) = self.lyric().map(str::to_string) {
968            // The first is always a chord.
969            if let StreamElement::Chord(chord) = &mut elements[0].0 {
970                chord.add_lyric(&lyric, None, false)?;
971            }
972        }
973        if elements.len() == 1 {
974            let (element, serial) = elements.remove(0);
975            self.measure.append_shortest(element, Some(serial));
976        } else {
977            self.measure.append_in_voices(elements);
978        }
979        Ok(())
980    }
981
982    fn finish(mut self) -> Result<Stream> {
983        let measure = std::mem::replace(&mut self.measure, MeasureBuilt::new(0));
984        let mut part = self
985            .part
986            .take()
987            .ok_or_else(|| error("a file with no staff"))?;
988        if measure.holds_anything() {
989            part.append(PartItem::Measure(measure));
990        }
991        self.parts.push(part);
992
993        let mut score = Stream::with_kind(StreamKind::Score);
994        if let Some(metadata) = self.metadata {
995            score.set_metadata(Some(metadata));
996        }
997        for (index, part) in self.parts.into_iter().enumerate() {
998            score.insert(0.0, assemble(part, index, &self.endings));
999        }
1000        Ok(score)
1001    }
1002}
1003
1004/// A value rebuilt with dots, as music21's `dots` setter rebuilds its one
1005/// component: the grace and tuplets it had kept.
1006fn with_dots(duration: &Duration, dots: u32) -> Duration {
1007    let Some((value, _)) = duration.type_and_dots() else {
1008        return duration.clone();
1009    };
1010    let mut dotted = Duration::from_type_with_dots(value, dots);
1011    dotted.set_tuplets(duration.tuplets());
1012    dotted.set_grace(duration.grace().cloned());
1013    dotted.set_expression_is_inferred(duration.expression_is_inferred());
1014    dotted
1015}
1016
1017/// The parts of `Dur` and `Pos` that are numbers, read as Python's `int`.
1018fn number<T: std::str::FromStr>(text: &str) -> Result<T> {
1019    text.trim()
1020        .parse()
1021        .map_err(|_| error(format!("{text} is not a number")))
1022}
1023
1024/// music21's `createLyrics`: a verse split into the syllables sung to each
1025/// note, a syllable after a hyphen written with one before it.
1026fn lyrics(text: &str) -> Vec<String> {
1027    let text = text
1028        .trim_matches('"')
1029        .replace("\r\n", " ")
1030        .replace(['\r', '\n'], " ");
1031    let mut syllables = Vec::new();
1032    for word in text.split(' ') {
1033        let mut first = true;
1034        for part in word.split('-') {
1035            for piece in part.split('\n') {
1036                let syllable = if piece.is_empty() {
1037                    " - ".to_string()
1038                } else if first {
1039                    piece.to_string()
1040                } else {
1041                    format!(" -{piece}")
1042                };
1043                first = false;
1044                syllables.push(syllable);
1045            }
1046        }
1047    }
1048    syllables
1049}
1050
1051/// A measure by the part it is in and its number among that part's
1052/// measures, both counted from nought.
1053#[derive(Clone, Copy, PartialEq, Eq)]
1054struct Place {
1055    part: usize,
1056    measure: usize,
1057}
1058
1059/// A part read, put together as a stream: its measures and instruments where
1060/// they stood, each measure's elements in music21's order, the endings
1061/// opening or closing on its measures, and its slurs.
1062fn assemble(part: PartBuilt, index: usize, endings: &[(Vec<Place>, u32)]) -> Stream {
1063    let mut stream = Stream::with_kind(StreamKind::Part);
1064    stream.set_name(part.name.clone());
1065    stream.set_abbreviation(part.name.clone());
1066    // Each note read, by its number, at its place among the part's leaves.
1067    let mut places: Vec<(usize, usize)> = Vec::new();
1068    let mut leaves = 0;
1069    let mut measures = 0;
1070    for (offset, item) in part.items {
1071        match item {
1072            PartItem::Instrument(instrument) => {
1073                stream.insert(offset, StreamElement::Instrument(instrument));
1074                leaves += 1;
1075            }
1076            PartItem::Measure(built) => {
1077                let mut measure = Stream::with_kind(StreamKind::Measure);
1078                measure.set_number(built.number);
1079                measure.set_left_barline(built.left);
1080                measure.set_right_barline(built.right);
1081                // A bracket is marked where it opens and where it closes.
1082                let here = Place {
1083                    part: index,
1084                    measure: measures,
1085                };
1086                if let Some((spanned, number)) = endings.iter().find(|(spanned, _)| {
1087                    spanned.first() == Some(&here) || spanned.last() == Some(&here)
1088                }) {
1089                    measure.set_ending(Some(Ending::new(
1090                        vec![*number],
1091                        spanned.first() == Some(&here),
1092                        spanned.last() == Some(&here),
1093                    )));
1094                }
1095                for place in sorted_places(&built.items) {
1096                    let item = &built.items[place];
1097                    match &item.content {
1098                        Content::Element(element, serial) => {
1099                            if let Some(serial) = serial {
1100                                places.push((*serial, leaves));
1101                            }
1102                            leaves += 1;
1103                            measure.insert(item.offset, (**element).clone());
1104                        }
1105                        Content::Voice(voice) => {
1106                            let mut inner = Stream::with_kind(StreamKind::Voice);
1107                            inner.set_id(Some(voice.id.to_string()));
1108                            for place in sorted_places(&voice.items) {
1109                                let held = &voice.items[place];
1110                                if let Content::Element(element, serial) = &held.content {
1111                                    if let Some(serial) = serial {
1112                                        places.push((*serial, leaves));
1113                                    }
1114                                    leaves += 1;
1115                                    inner.insert(held.offset, (**element).clone());
1116                                }
1117                            }
1118                            measure.insert(item.offset, inner);
1119                        }
1120                    }
1121                }
1122                stream.insert(offset, measure);
1123                measures += 1;
1124            }
1125        }
1126    }
1127    let place_of = |serial: usize| {
1128        places
1129            .iter()
1130            .find(|(read, _)| *read == serial)
1131            .map(|(_, place)| *place)
1132    };
1133    for (start, end) in part.slurs {
1134        stream.add_spanner(Spanner::with_unplaced(
1135            crate::spanner::SpannerKind::Slur,
1136            vec![place_of(start), place_of(end)],
1137        ));
1138    }
1139    stream
1140}
1141
1142#[cfg(test)]
1143mod tests {
1144    use super::*;
1145
1146    fn names(score: &Stream) -> Vec<String> {
1147        score
1148            .pitches()
1149            .iter()
1150            .map(|pitch| pitch.name_with_octave())
1151            .collect()
1152    }
1153
1154    #[test]
1155    fn a_note_is_spelled_from_its_place_under_the_clef() {
1156        let score = from_noteworthy(
1157            "|AddStaff|\n|Clef|Type:Bass\n|Note|Dur:4th|Pos:b3\n|Note|Dur:4th|Pos:3\n|Bar\n|Note|Dur:4th|Pos:3\n",
1158        )
1159        .unwrap();
1160        // An accidental lasts to the barline.
1161        assert_eq!(names(&score), ["Gb3", "Gb3", "G3"]);
1162    }
1163
1164    #[test]
1165    fn a_key_signature_spells_the_notes_after_it() {
1166        let score = from_noteworthy(
1167            "|AddStaff|\n|Key|Signature:F#,C#,G#,D#\n|Chord|Dur:Half|Pos:1,3,#5\n|Chord|Dur:Half|Pos:1,3,5\n",
1168        )
1169        .unwrap();
1170        assert_eq!(names(&score), ["C#5", "E5", "G#5", "C#5", "E5", "G#5"]);
1171    }
1172
1173    #[test]
1174    fn the_first_two_measures_are_both_numbered_nought() {
1175        let score =
1176            from_noteworthy("|AddStaff|\n|Note|Dur:Whole|Pos:1\n|Bar\n|Note|Dur:Whole|Pos:1\n|Bar\n|Note|Dur:Whole|Pos:1\n")
1177                .unwrap();
1178        let numbers: Vec<IntegerType> = score.parts()[0]
1179            .measures()
1180            .iter()
1181            .map(|measure| measure.number())
1182            .collect();
1183        assert_eq!(numbers, [0, 0, 1]);
1184    }
1185
1186    #[test]
1187    fn a_verse_is_split_into_syllables() {
1188        assert_eq!(lyrics("\"Hello world\""), ["Hello", "world"]);
1189        assert_eq!(
1190            lyrics("\"A-ve Ma-ri-a\""),
1191            ["A", " -ve", "Ma", " -ri", " -a"]
1192        );
1193    }
1194
1195    #[test]
1196    fn a_line_with_an_attribute_and_no_value_is_refused() {
1197        assert!(from_noteworthy("|AddStaff|\n|Note|Dur\n").is_err());
1198        assert!(from_noteworthy("|Note|Dur:4th|Pos:1\n").is_err());
1199    }
1200
1201    /// The notes, chords and rests of the first measure: where each starts
1202    /// in it, how long it is and what it sounds.
1203    fn first_measure(lines: &str) -> Vec<(FloatType, FloatType, String)> {
1204        let score = from_noteworthy(&format!("|AddStaff|\n|Clef|Type:Treble\n{lines}")).unwrap();
1205        let mut out: Vec<(FloatType, FloatType, String)> = score.parts()[0].measures()[0]
1206            .leaves()
1207            .into_iter()
1208            .filter_map(|(offset, element)| {
1209                let sounds = match element {
1210                    StreamElement::Note(note) => note.pitch().name_with_octave(),
1211                    StreamElement::Chord(chord) => chord
1212                        .notes()
1213                        .iter()
1214                        .map(|note| note.pitch().name_with_octave())
1215                        .collect::<Vec<_>>()
1216                        .join(" "),
1217                    StreamElement::Rest(_) => "rest".to_string(),
1218                    _ => return None,
1219                };
1220                Some((offset, element.quarter_length(), sounds))
1221            })
1222            .collect();
1223        out.sort_by(|left, right| left.partial_cmp(right).unwrap());
1224        out
1225    }
1226
1227    #[test]
1228    fn a_chord_of_two_lengths_is_two_chords_starting_together() {
1229        assert_eq!(
1230            first_measure(
1231                "|Note|Dur:4th|Pos:0\n\
1232                 |Chord|Dur:8th|Pos:-4,n-3,b-2,#-1,x0,v1,2x|Opts:Stem=Down|Dur2:8th,DblDotted|Pos2:3x\n\
1233                 |Note|Dur:4th|Pos:-5\n"
1234            ),
1235            [
1236                (0.0, 1.0, "B4".to_string()),
1237                (1.0, 0.5, "E4 F4 Gb4 A#4 B##4 Cbb5 D5".to_string()),
1238                (1.0, 0.875, "E5".to_string()),
1239                (1.5, 1.0, "D4".to_string()),
1240            ]
1241        );
1242    }
1243
1244    #[test]
1245    fn a_rest_chord_is_a_rest_and_a_chord_starting_together() {
1246        assert_eq!(
1247            first_measure(
1248                "|Note|Dur:4th|Pos:0\n\
1249                 |RestChord|Dur:Half|Opts:Stem=Up|Dur2:4th|Pos2:-1,1\n\
1250                 |RestChord|Dur:4th|Dur2:4th|Pos2:-5\n\
1251                 |Note|Dur:4th|Pos:0\n"
1252            ),
1253            [
1254                (0.0, 1.0, "B4".to_string()),
1255                (1.0, 1.0, "A4 C5".to_string()),
1256                (1.0, 2.0, "rest".to_string()),
1257                (2.0, 1.0, "D4".to_string()),
1258                (2.0, 1.0, "rest".to_string()),
1259                (3.0, 1.0, "B4".to_string()),
1260            ]
1261        );
1262    }
1263}