Skip to main content

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