1mod 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
40pub fn from_noteworthy(text: &str) -> Result<Stream> {
76 from_lines(text.lines())
77}
78
79fn 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#[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#[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 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
183struct Item {
185 offset: FloatType,
186 content: Content,
187 inserted: usize,
189}
190
191enum Content {
192 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
213fn 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 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 fn holds_anything(&self) -> bool {
270 !self.items.is_empty() || self.left.is_some() || self.right.is_some()
271 }
272
273 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 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 ¬es {
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
382fn 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
407enum PartItem {
410 Measure(MeasureBuilt),
411 Instrument(Box<Instrument>),
412}
413
414struct PartBuilt {
415 name: Option<String>,
416 items: Vec<(FloatType, PartItem)>,
417 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 repeated: Vec<Place>,
458 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: Vec<(String, &'static str)>,
471 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 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 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 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 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 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 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 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 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 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 fn simultaneous(&mut self, mut elements: Vec<(StreamElement, usize)>) -> Result<()> {
967 if let Some(lyric) = self.lyric().map(str::to_string) {
968 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
1004fn 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
1017fn 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
1024fn 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#[derive(Clone, Copy, PartialEq, Eq)]
1054struct Place {
1055 part: usize,
1056 measure: usize,
1057}
1058
1059fn 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 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 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 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 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}