1pub mod base12_26;
11pub mod base40;
12
13use std::collections::HashMap;
14
15use crate::articulations::{Articulation, ArticulationKind};
16use crate::bar::{Barline, BarlineType, RepeatDirection};
17use crate::chord::Chord;
18use crate::clef::{Clef, ClefKind};
19use crate::defaults::{FloatType, IntegerType};
20use crate::duration::Duration;
21use crate::dynamics::Dynamic;
22use crate::error::{Error, Position, Result};
23use crate::expressions::{Expression, Fermata, Ornament, OrnamentKind};
24use crate::interval::Interval;
25use crate::key::KeySignature;
26use crate::makenotation::{make_accidentals, make_beams, op_frac};
27use crate::metadata::Metadata;
28use crate::meter::TimeSignature;
29use crate::notation::{BeamDirection, BeamType, Beams, Tie, TieType};
30use crate::note::Note;
31use crate::pitch::Pitch;
32use crate::pitch::accidental::Accidental;
33use crate::rest::Rest;
34use crate::spanner::Spanner;
35use crate::stream::{Stream, StreamElement, StreamEvent, StreamKind};
36use crate::tempo::TempoText;
37
38fn error(message: impl Into<String>) -> Error {
39 Error::muse_data(message.into())
40}
41
42pub fn from_musedata(files: &[&str]) -> Result<Stream> {
82 let mut parts = Vec::new();
83 for text in files {
84 parts.extend(split_parts(text)?);
85 }
86 let first = parts
87 .first()
88 .ok_or_else(|| error("a MuseData work with no parts"))?;
89 let mut metadata = Metadata::new();
90 if let Some(title) = first.work_title() {
91 metadata.add_text("title", title);
92 }
93 if let Some(number) = first.movement_number() {
94 metadata.add_text("movementNumber", number);
95 }
96 if let Some(name) = first.movement_title() {
97 metadata.add_text("movementName", name);
98 }
99 if let Some(number) = first.work_number() {
100 metadata.add_text("number", number);
101 }
102 let mut score = Stream::with_kind(StreamKind::Score);
103 score.set_metadata(Some(metadata));
104 let mut slurs = Vec::new();
105 for part in &parts {
106 let (made, joined) = part.to_part()?;
107 score.insert(0.0, made);
108 slurs.push(joined);
109 }
110 let mut places: Vec<Vec<usize>> = vec![Vec::new(); slurs.len()];
113 for (position, (top, _, element)) in score.leaves_under_top().into_iter().enumerate() {
114 if matches!(element, StreamElement::Note(_) | StreamElement::Chord(_))
115 && let Some(read) = places.get_mut(top)
116 {
117 read.push(position);
118 }
119 }
120 for (joined, read) in slurs.iter().zip(&places) {
121 for (start, stop) in joined {
122 if let (Some(first), Some(last)) = (read.get(*start), read.get(*stop)) {
123 score.add_spanner(Spanner::slur(vec![*first, *last]));
124 }
125 }
126 }
127 Ok(score)
128}
129
130#[derive(Default)]
135struct Slurs {
136 open: [Option<usize>; 4],
137 joined: Vec<(usize, usize)>,
138 read: usize,
139}
140
141impl Slurs {
142 const STARTS: [char; 4] = ['(', '[', '{', 'z'];
143 const STOPS: [char; 4] = [')', ']', '}', 'x'];
144
145 fn add(&mut self, marks: &str) {
148 let at = self.read;
149 self.read += 1;
150 for mark in marks.chars() {
151 if let Some(number) = Self::STOPS.iter().position(|stop| *stop == mark)
152 && let Some(start) = self.open[number].take()
153 {
154 self.joined.push((start, at));
155 }
156 }
157 for mark in marks.chars() {
158 if let Some(number) = Self::STARTS.iter().position(|start| *start == mark) {
159 self.open[number] = Some(at);
160 }
161 }
162 }
163}
164
165fn split_parts(text: &str) -> Result<Vec<Part>> {
168 let mut parts = Vec::new();
169 let mut lines: Vec<String> = Vec::new();
170 let mut numbers: Vec<usize> = Vec::new();
172 let mut comment = false;
173 for (index, line) in text.replace("\r\n", "\n").split('\n').enumerate() {
174 if line.starts_with('&') {
175 comment = !comment;
176 continue;
177 }
178 if comment || line.starts_with('@') {
179 continue;
180 }
181 if line.starts_with("/END") || line.starts_with("END") {
182 if lines.len() <= 1 {
183 lines.clear();
184 numbers.clear();
185 continue;
186 }
187 let at = numbers.first().copied().unwrap_or(index + 1);
188 let part = Part::new(std::mem::take(&mut lines), std::mem::take(&mut numbers))
189 .map_err(|error| error.placed(|| Position::line(at)))?;
190 parts.push(part);
191 } else if !line.starts_with("/eof") {
192 lines.push(line.to_string());
193 numbers.push(index + 1);
194 }
195 }
196 Ok(parts)
197}
198
199fn digits(text: &str) -> String {
201 text.chars().filter(char::is_ascii_digit).collect()
202}
203
204#[derive(Clone, Copy, PartialEq, Eq)]
207enum Stage {
208 One,
209 Two,
210}
211
212struct Part {
214 lines: Vec<String>,
215 numbers: Vec<usize>,
217 stage: Stage,
218}
219
220impl Part {
221 fn new(lines: Vec<String>, numbers: Vec<usize>) -> Result<Self> {
224 if lines.iter().any(|line| line.starts_with('$')) {
225 return Ok(Self {
226 lines,
227 numbers,
228 stage: Stage::Two,
229 });
230 }
231 if lines.len() <= 1 {
232 return Err(error("cannot scrub empty source"));
233 }
234 let (lines, numbers) = lines
235 .into_iter()
236 .zip(numbers)
237 .skip_while(|(line, _)| line.trim().is_empty())
238 .unzip();
239 Ok(Self {
240 lines,
241 numbers,
242 stage: Stage::One,
243 })
244 }
245
246 fn place(&self, index: usize) -> Position {
248 let position = Position {
249 line: self.numbers.get(index).copied(),
250 ..Position::default()
251 };
252 match self.part_name() {
253 Some(name) => position.in_part(name),
254 None => position,
255 }
256 }
257
258 fn attributes_place(&self) -> Position {
261 let index = match self.stage {
262 Stage::One => Some(6),
263 Stage::Two => self
264 .lines
265 .iter()
266 .skip(11)
267 .position(|line| line.starts_with('$'))
268 .map(|index| index + 11),
269 };
270 match index {
271 Some(index) => self.place(index),
272 None => self.place(usize::MAX),
273 }
274 }
275
276 fn line(&self, index: usize) -> Result<&str> {
277 self.lines
278 .get(index)
279 .map(String::as_str)
280 .ok_or_else(|| error("a header shorter than MuseData's"))
281 }
282
283 fn digits_after(line: &str, tag: &str) -> String {
286 let Some(at) = line.find(tag) else {
287 return String::new();
288 };
289 let mut out = String::new();
290 for c in line[at + tag.len()..].chars() {
291 if c.is_ascii_digit() || c == '-' || c == '/' {
292 out.push(c);
293 } else if !c.is_whitespace() {
294 break;
295 }
296 }
297 out
298 }
299
300 fn words_after(line: &str, tag: &str) -> String {
303 let Some(at) = line.find(tag) else {
304 return String::new();
305 };
306 line[at + tag.len()..]
307 .chars()
308 .take_while(|c| c.is_alphabetic() || c.is_whitespace() || *c == ',')
309 .collect()
310 }
311
312 fn work_number(&self) -> Option<String> {
313 if self.stage == Stage::One {
314 let data: String = self.lines.get(1)?.chars().take(6).collect();
315 let data = data.trim();
316 Some(if data.contains(',') {
317 data.split(',').nth(1).unwrap_or("").to_string()
318 } else {
319 data.to_string()
320 })
321 } else {
322 Some(Self::digits_after(self.lines.get(4)?, "WK#:"))
323 }
324 }
325
326 fn movement_number(&self) -> Option<String> {
327 if self.stage == Stage::One {
328 Some(
329 self.lines
330 .get(1)?
331 .chars()
332 .skip(6)
333 .collect::<String>()
334 .trim()
335 .to_string(),
336 )
337 } else {
338 Some(Self::digits_after(self.lines.get(4)?, "MV#:"))
339 }
340 }
341
342 fn work_title(&self) -> Option<String> {
343 if self.stage == Stage::One {
344 Some(self.lines.first()?.trim().to_string())
345 } else {
346 self.lines.get(6).cloned()
347 }
348 }
349
350 fn movement_title(&self) -> Option<String> {
351 if self.stage == Stage::One {
352 None
353 } else {
354 self.lines.get(7).cloned()
355 }
356 }
357
358 fn part_name(&self) -> Option<String> {
359 if self.stage == Stage::One {
360 None
361 } else {
362 self.lines.get(8).map(|name| name.trim().to_string())
363 }
364 }
365
366 fn attributes(&self) -> Result<String> {
368 if self.stage == Stage::One {
369 return Ok(format!("{} {}", self.line(6)?.trim(), self.line(7)?.trim()));
370 }
371 self.lines
372 .iter()
373 .skip(11)
374 .find(|line| line.starts_with('$'))
375 .cloned()
376 .ok_or_else(|| error("a part with no attribute record"))
377 }
378
379 fn stage_one_field(&self, index: usize) -> Result<String> {
381 self.attributes()?
382 .split(' ')
383 .nth(index)
384 .map(str::to_string)
385 .ok_or_else(|| error("a stage-1 attribute record too short"))
386 }
387
388 fn integer(text: &str) -> Result<IntegerType> {
389 text.trim()
390 .parse()
391 .map_err(|_| error(format!("{text:?} is not a number")))
392 }
393
394 fn key_signature(&self) -> Result<KeySignature> {
395 let sharps = if self.stage == Stage::One {
396 Self::integer(&self.stage_one_field(1)?)?
397 } else {
398 Self::integer(&Self::digits_after(&self.attributes()?, "K:"))?
399 };
400 Ok(KeySignature::new(sharps))
401 }
402
403 fn time_signature(&self) -> Result<TimeSignature> {
404 let (numerator, denominator) = if self.stage == Stage::One {
405 (
406 Self::integer(&self.stage_one_field(4)?)?,
407 Self::integer(&self.stage_one_field(5)?)?,
408 )
409 } else {
410 let written = Self::digits_after(&self.attributes()?, "T:");
411 let (numerator, denominator) = written
412 .split_once('/')
413 .filter(|(_, denominator)| !denominator.contains('/'))
414 .ok_or_else(|| error(format!("no meter in {written:?}")))?;
415 (Self::integer(numerator)?, Self::integer(denominator)?)
416 };
417 let ratio = if (numerator == 1 && denominator == 1) || denominator == 0 {
418 "4/4".to_string()
419 } else {
420 format!("{numerator}/{denominator}")
421 };
422 TimeSignature::from_ratio_string(&ratio)
423 }
424
425 fn clef(&self) -> Result<Option<Clef>> {
426 if self.stage == Stage::One {
427 return Ok(None);
428 }
429 let line = self.attributes()?;
430 let mut written = Self::digits_after(&line, "C:");
431 if written.is_empty() {
432 let staves = match Self::digits_after(&line, "S:").as_str() {
433 "" => 1,
434 raw => Self::integer(raw)?,
435 };
436 written = (1..=staves)
437 .map(|staff| Self::digits_after(&line, &format!("C{staff}:")))
438 .find(|raw| !raw.is_empty())
439 .ok_or_else(|| error("a part with no clef"))?;
440 }
441 let kind = match Self::integer(&written)?.to_string().as_str() {
442 "5" => ClefKind::FrenchViolinClef,
443 "4" => ClefKind::TrebleClef,
444 "34" => ClefKind::Treble8vbClef,
445 "64" => ClefKind::Treble8vaClef,
446 "3" => ClefKind::GSopranoClef,
447 "11" => ClefKind::CBaritoneClef,
448 "12" => ClefKind::TenorClef,
449 "13" => ClefKind::AltoClef,
450 "14" => ClefKind::MezzoSopranoClef,
451 "15" => ClefKind::SopranoClef,
452 "22" => ClefKind::BassClef,
453 "23" => ClefKind::FBaritoneClef,
454 "52" => ClefKind::Bass8vbClef,
455 "82" => ClefKind::Bass8vaClef,
456 other => return Err(error(format!("cannot determine clef from: {other}"))),
457 };
458 Ok(Some(Clef::of_kind(kind)))
459 }
460
461 fn directive(&self) -> Result<Option<String>> {
462 if self.stage == Stage::One {
463 return Ok(None);
464 }
465 let words = Self::words_after(&self.attributes()?, "D:");
466 Ok((!words.is_empty()).then(|| words.trim().to_string()))
467 }
468
469 fn transposition(&self) -> Result<Option<Interval>> {
471 if self.stage == Stage::One {
472 return Ok(None);
473 }
474 match Self::digits_after(&self.attributes()?, "X:").as_str() {
475 "" => Ok(None),
476 raw => base40_interval(Self::integer(raw)?).map(Some),
477 }
478 }
479
480 fn divisions(&self) -> Result<FloatType> {
483 if self.stage == Stage::One {
484 let bar = Self::integer(&self.stage_one_field(2)?)?;
485 Ok(FloatType::from(bar) / self.time_signature()?.bar_duration().quarter_length())
486 } else {
487 Ok(FloatType::from(Self::integer(&Self::digits_after(
488 &self.attributes()?,
489 "Q:",
490 ))?))
491 }
492 }
493
494 fn measure_bounds(&self) -> Result<Vec<(usize, usize)>> {
497 let lines = &self.lines;
498 let end = lines.len();
499 let mut marks = Vec::new();
500 let mut first_after: Option<usize> = None;
501 let mut attributes = 0;
502 for (index, line) in lines.iter().enumerate() {
503 if self.stage == Stage::Two {
504 if line.starts_with('$') {
505 attributes += 1;
506 continue;
507 }
508 if attributes > 0 && first_after.is_none() {
509 first_after = Some(index);
510 }
511 }
512 if line.starts_with('m') {
513 marks.push(index);
514 }
515 }
516 let first_mark = *marks
517 .first()
518 .ok_or_else(|| error("a part with no measure records"))?;
519 if self.stage == Stage::One {
520 first_after = Some(first_mark);
521 }
522 let mut bounds = Vec::new();
523 let start = if Some(first_mark) == first_after {
524 if marks.len() == 1 {
525 bounds.push((first_mark, end));
526 None
527 } else {
528 bounds.push((first_mark, marks[1]));
529 Some(1)
530 }
531 } else {
532 let first_after =
533 first_after.ok_or_else(|| error("a part with nothing after its attributes"))?;
534 bounds.push((first_after, first_mark));
535 Some(0)
536 };
537 if let Some(start) = start {
538 for index in start..marks.len() {
539 bounds.push((marks[index], marks.get(index + 1).copied().unwrap_or(end)));
540 }
541 }
542 Ok(bounds)
543 }
544
545 fn to_part(&self) -> Result<(Stream, Vec<(usize, usize)>)> {
547 let mut part = Stream::with_kind(StreamKind::Part);
548 let name = self.part_name();
549 part.set_id(name.clone());
550 part.set_name(name);
551 let header = |error: Error| error.placed(|| self.attributes_place());
552 let divisions = self.divisions().map_err(header)?;
553 let bounds = self.measure_bounds()?;
554 let measures: Vec<&[String]> = bounds
555 .iter()
556 .map(|(start, end)| self.lines.get(*start..*end).unwrap_or(&[]))
557 .collect();
558 let mut last_by_voice: HashMap<usize, Option<Option<TieType>>> = HashMap::new();
561 let mut slurs = Slurs::default();
562 let mut bars = 0;
563 for (index, lines) in measures.iter().enumerate() {
564 if !has_notes(lines) {
565 continue;
566 }
567 let first = bounds[index].0;
570 let here = |error: Error| {
571 error.placed(|| {
572 let mut position = self.place(first);
573 position.measure = measure_number(lines).ok();
574 position
575 })
576 };
577 let mut measure = measure_object(lines).map_err(here)?;
578 if let Some(next) = measures.get(index + 1)
579 && !has_notes(next)
580 {
581 let after = bounds[index + 1].0;
582 let barline = bar_object(next).map_err(|error| error.placed(|| self.place(after)));
583 measure.set_right_barline(Some(barline.map_err(here)?));
584 }
585 if bars == 0 {
586 if let Some(clef) = self.clef().map_err(header)? {
587 append_at(&mut measure, 0.0, StreamElement::Clef(clef));
588 }
589 append_at(
590 &mut measure,
591 0.0,
592 StreamElement::TimeSignature(self.time_signature().map_err(header)?),
593 );
594 append_at(
595 &mut measure,
596 0.0,
597 StreamElement::KeySignature(self.key_signature().map_err(header)?),
598 );
599 if let Some(directive) = self.directive().map_err(header)? {
600 let words = TempoText::new(directive);
601 if words.is_common_tempo_text() {
602 append_at(
603 &mut measure,
604 0.0,
605 StreamElement::MetronomeMark(words.metronome_mark()),
606 );
607 }
608 }
609 }
610 self.fill(
611 &mut measure,
612 lines,
613 first,
614 divisions,
615 &mut last_by_voice,
616 &mut slurs,
617 )
618 .map_err(here)?;
619 if bars == 0 {
620 let bar = self.time_signature()?.bar_duration().quarter_length();
621 let length = measure.end_offset();
622 if length / bar < 1.0 {
623 measure.set_padding_left(bar - length);
624 }
625 }
626 part.push(measure);
627 bars += 1;
628 }
629 if let Some(interval) = self.transposition().map_err(header)? {
630 let mut failure = None;
631 part.for_each_mut(&mut |_, element| {
632 let moved = match element {
633 StreamElement::Note(note) => note.transpose(&interval).map(StreamElement::Note),
634 StreamElement::Chord(chord) => {
635 chord.transpose(&interval).map(StreamElement::Chord)
636 }
637 StreamElement::KeySignature(signature) => signature
638 .transpose(&interval)
639 .map(StreamElement::KeySignature),
640 _ => return,
641 };
642 match moved {
643 Ok(moved) => *element = moved,
644 Err(error) => failure = Some(error),
645 }
646 });
647 if let Some(error) = failure {
648 return Err(error);
649 }
650 make_accidentals(&mut part);
651 }
652 if self.stage == Stage::One {
653 let clef = Clef::best_for(&part.pitches(), false);
655 if let Some(StreamElement::Stream(first)) = part
656 .events_mut()
657 .iter_mut()
658 .map(StreamEvent::element_mut)
659 .find(|element| {
660 matches!(element, StreamElement::Stream(inner) if inner.kind() == StreamKind::Measure)
661 })
662 {
663 append_at(first, 0.0, StreamElement::Clef(clef));
664 }
665 let _ = make_beams(&mut part);
667 make_accidentals(&mut part);
668 }
669 Ok((part, slurs.joined))
670 }
671
672 fn fill(
676 &self,
677 measure: &mut Stream,
678 lines: &[String],
679 first: usize,
680 divisions: FloatType,
681 last_by_voice: &mut HashMap<usize, Option<Option<TieType>>>,
682 slurs: &mut Slurs,
683 ) -> Result<()> {
684 let voices = lines.iter().any(|line| line.starts_with("back"));
685 let mut voice = voices.then(|| Stream::with_kind(StreamKind::Voice));
686 let mut pending: Vec<Record<'_>> = Vec::new();
687 let mut number = 0;
688 let mut last = last_by_voice.get(&number).copied().flatten();
689 let previous = &mut last;
690 for (index, line) in lines.iter().enumerate() {
691 let record = Record {
692 line,
693 stage: self.stage,
694 number: self.numbers.get(first + index).copied(),
695 };
696 if record.is_back() {
697 self.flush(
698 &mut pending,
699 measure,
700 voice.as_mut(),
701 divisions,
702 previous,
703 slurs,
704 )?;
705 if let Some(done) = voice.replace(Stream::with_kind(StreamKind::Voice)) {
706 append_at(measure, 0.0, StreamElement::Stream(Box::new(done)));
707 }
708 last_by_voice.insert(number, *previous);
709 number += 1;
710 *previous = last_by_voice.get(&number).copied().flatten();
711 }
712 if record.is_rest() {
713 self.flush(
714 &mut pending,
715 measure,
716 voice.as_mut(),
717 divisions,
718 previous,
719 slurs,
720 )?;
721 let length = record
722 .quarter_length(divisions)
723 .and_then(Duration::new)
724 .map_err(|error| record.placed(error))?;
725 let rest = Rest::new(length);
726 let target = voice.as_mut().unwrap_or(&mut *measure);
727 append(target, StreamElement::Rest(rest));
728 *previous = Some(None);
729 } else if record.is_chord() {
730 pending.push(record);
731 } else if record.is_note() {
732 self.flush(
733 &mut pending,
734 measure,
735 voice.as_mut(),
736 divisions,
737 previous,
738 slurs,
739 )?;
740 pending.push(record);
741 }
742 }
743 self.flush(
744 &mut pending,
745 measure,
746 voice.as_mut(),
747 divisions,
748 previous,
749 slurs,
750 )?;
751 last_by_voice.insert(number, *previous);
752 if let Some(voice) = voice
753 && !voice.is_empty()
754 {
755 append_at(measure, 0.0, StreamElement::Stream(Box::new(voice)));
756 }
757 Ok(())
758 }
759
760 fn flush(
763 &self,
764 pending: &mut Vec<Record<'_>>,
765 measure: &mut Stream,
766 voice: Option<&mut Stream>,
767 divisions: FloatType,
768 previous: &mut Option<Option<TieType>>,
769 slurs: &mut Slurs,
770 ) -> Result<()> {
771 if pending.is_empty() {
772 return Ok(());
773 }
774 let records = std::mem::take(pending);
775 slurs.add(&records[0].slur_marks());
776 let (element, dynamics, tie) = note_or_chord(&records, divisions, *previous)
777 .map_err(|error| records[0].placed(error))?;
778 *previous = Some(tie);
779 let target = voice.unwrap_or(measure);
780 let at = target.end_offset();
781 append_at(target, at, element);
782 for dynamic in dynamics {
783 append_at(target, at, StreamElement::Dynamic(dynamic));
784 }
785 Ok(())
786 }
787}
788
789fn append(stream: &mut Stream, element: StreamElement) {
792 let at = stream.end_offset();
793 append_at(stream, at, element);
794}
795
796fn append_at(stream: &mut Stream, offset: FloatType, element: StreamElement) {
798 stream.insert_sorted(vec![StreamEvent::new(offset, element)]);
799}
800
801fn has_notes(lines: &[String]) -> bool {
803 lines.iter().any(|line| {
804 line.chars()
805 .next()
806 .is_some_and(|c| "ABCDEFGrgc".contains(c))
807 })
808}
809
810fn measure_line(lines: &[String]) -> Result<String> {
813 let line = lines
814 .first()
815 .ok_or_else(|| error("a measure with no records"))?
816 .trim()
817 .to_string();
818 Ok(if line.starts_with('m') {
819 line
820 } else {
821 "measure".to_string()
822 })
823}
824
825fn bar_object(lines: &[String]) -> Result<Barline> {
827 let line = measure_line(lines)?;
828 let kind: String = line.chars().skip(1).take(6).collect();
829 let bar_type = match kind.as_str() {
830 "easure" => BarlineType::Regular,
831 "dotted" => BarlineType::Dotted,
832 "double" => BarlineType::Double,
833 "heavy1" | "heavy" => BarlineType::Heavy,
834 "heavy2" => BarlineType::Final,
835 "heavy3" => BarlineType::HeavyLight,
836 "heavy4" | "heave4" => BarlineType::HeavyHeavy,
837 other => {
838 return Err(error(format!(
839 "cannot process bar data definition: {other}"
840 )));
841 }
842 };
843 let flag: String = line.chars().skip(16).collect();
844 let flag = flag.trim();
845 let mut barline = Barline::new(bar_type);
846 if line.chars().count() > 16 && !flag.is_empty() {
847 let direction = if flag.contains(":|") {
848 Some(RepeatDirection::End)
849 } else if flag.contains("|:") {
850 Some(RepeatDirection::Start)
851 } else {
852 None
853 };
854 if let Some(direction) = direction {
855 barline = Barline::repeat(direction, None);
856 barline.set_bar_type(bar_type);
857 }
858 }
859 Ok(barline)
860}
861
862fn measure_number(lines: &[String]) -> Result<String> {
866 let line = measure_line(lines)?;
867 let rest: String = line.chars().skip(8).collect();
868 let opens_with_record = lines
869 .first()
870 .is_some_and(|first| first.trim().starts_with('m'));
871 Ok(if line.chars().count() >= 9 && !rest.trim().is_empty() {
872 digits(&line)
873 } else if opens_with_record {
874 "1".to_string()
875 } else {
876 "0".to_string()
877 })
878}
879
880fn measure_object(lines: &[String]) -> Result<Stream> {
883 let number = measure_number(lines)?;
884 let mut measure = Stream::with_kind(StreamKind::Measure);
885 measure.set_left_barline(Some(bar_object(lines)?));
886 if !number.is_empty() {
887 measure.set_number(
888 number
889 .parse()
890 .map_err(|_| error(format!("measure number {number}")))?,
891 );
892 }
893 Ok(measure)
894}
895
896#[derive(Clone, Copy)]
898struct Record<'a> {
899 line: &'a str,
900 stage: Stage,
901 number: Option<usize>,
903}
904
905impl Record<'_> {
906 fn placed(&self, error: Error) -> Error {
908 match self.number {
909 Some(number) => error.placed(|| Position::line(number)),
910 None => error,
911 }
912 }
913
914 fn first(&self) -> Option<char> {
915 self.line.chars().next()
916 }
917
918 fn at(&self, column: usize) -> Option<char> {
920 self.line.chars().nth(column)
921 }
922
923 fn columns(&self, from: usize, to: usize) -> String {
925 self.line
926 .chars()
927 .skip(from)
928 .take(to.saturating_sub(from))
929 .collect()
930 }
931
932 fn is_rest(&self) -> bool {
933 self.first() == Some('r')
934 }
935
936 fn is_note(&self) -> bool {
937 self.first().is_some_and(|c| "ABCDEFG".contains(c))
938 }
939
940 fn is_chord(&self) -> bool {
941 self.first() == Some(' ') && self.at(1).is_some_and(|c| "ABCDEFG".contains(c))
942 }
943
944 fn is_back(&self) -> bool {
945 self.line.starts_with("back")
946 }
947
948 fn is_tied(&self) -> bool {
949 let column = if self.stage == Stage::One { 7 } else { 8 };
950 self.at(column) == Some('-')
951 }
952
953 fn pitch(&self) -> Result<Pitch> {
955 let token = if self.is_note() {
956 self.line
957 } else {
958 self.line.get(1..).unwrap_or("")
959 };
960 let data = token.split_whitespace().next().unwrap_or("");
961 let step = data
962 .chars()
963 .next()
964 .ok_or_else(|| error("a note with no pitch"))?;
965 let accidental = if data.contains('#') {
967 "#"
968 } else if data.contains('f') {
969 "-"
970 } else {
971 ""
972 };
973 let mut pitch = Pitch::from_name(format!("{step}{accidental}{}", digits(data)))?;
974 if self.stage == Stage::One {
975 return Ok(pitch);
976 }
977 if let Some(accidental) = pitch.written_accidental_mut() {
978 accidental.set_display_status(Some(false));
979 }
980 if let Some(shown) = self.shown_accidental()? {
981 pitch.set_accidental(shown);
982 if let Some(accidental) = pitch.written_accidental_mut() {
983 accidental.set_display_type("always")?;
984 accidental.set_display_status(Some(true));
985 }
986 }
987 if self
988 .notations()
989 .is_some_and(|notations| notations.contains('+'))
990 && let Some(accidental) = pitch.written_accidental_mut()
991 {
992 accidental.set_display_status(Some(true));
993 }
994 Ok(pitch)
995 }
996
997 fn shown_accidental(&self) -> Result<Option<Accidental>> {
999 if self.line.chars().count() <= 18 {
1000 return Ok(None);
1001 }
1002 let name = match self.at(18) {
1003 Some('#' | 'S') => "sharp",
1004 Some('n') => "natural",
1005 Some('f' | 'F') => "flat",
1006 Some('x' | 'X') => "double-sharp",
1007 Some('&') => "double-flat",
1008 _ => return Ok(None),
1009 };
1010 Accidental::new(name).map(Some)
1011 }
1012
1013 fn quarter_length(&self, divisions: FloatType) -> Result<FloatType> {
1015 let mut count = if self.stage == Stage::One {
1016 Part::integer(&self.columns(5, 7))?
1017 } else {
1018 Part::integer(&self.columns(5, 8))?
1019 };
1020 if let Some(blank) = self.at(4)
1021 && blank != ' '
1022 {
1023 let hundreds = blank.to_digit(10).ok_or_else(|| {
1024 error(format!(
1025 "Error in parsing: {}\n Column 5 must be blank.",
1026 self.line
1027 ))
1028 })?;
1029 count += 100 * IntegerType::try_from(hundreds).unwrap_or(0);
1030 }
1031 Ok(op_frac(FloatType::from(count) / divisions))
1032 }
1033
1034 fn lyrics(&self) -> Option<Vec<String>> {
1035 if self.stage == Stage::One || self.line.chars().count() < 44 {
1036 return None;
1037 }
1038 Some(
1039 self.line
1040 .chars()
1041 .skip(43)
1042 .collect::<String>()
1043 .split('|')
1044 .map(|lyric| lyric.trim().to_string())
1045 .collect(),
1046 )
1047 }
1048
1049 fn beams(&self) -> Option<String> {
1050 if self.stage == Stage::One {
1051 return None;
1052 }
1053 let data = self.columns(25, 31);
1054 (!data.is_empty()).then(|| data.trim_end().to_string())
1055 }
1056
1057 fn slur_marks(&self) -> String {
1060 if self.stage == Stage::One {
1061 return String::new();
1062 }
1063 self.notations()
1064 .unwrap_or_default()
1065 .chars()
1066 .filter(|mark| "()[]{}zx".contains(*mark))
1067 .collect()
1068 }
1069
1070 fn notations(&self) -> Option<String> {
1072 if self.line.chars().count() < 31 {
1073 return None;
1074 }
1075 Some(self.columns(31, 43).trim().to_string())
1076 }
1077
1078 fn articulations(&self) -> Vec<Articulation> {
1079 let Some(notations) = self.notations() else {
1080 return Vec::new();
1081 };
1082 notations
1083 .chars()
1084 .filter_map(|c| {
1085 let kind = match c {
1086 'A' | 'V' => ArticulationKind::StrongAccent,
1087 '>' => ArticulationKind::Accent,
1088 '.' => ArticulationKind::Staccato,
1089 '_' => ArticulationKind::Tenuto,
1090 '=' => ArticulationKind::DetachedLegato,
1091 'i' => ArticulationKind::Spiccato,
1092 ',' => ArticulationKind::BreathMark,
1093 _ => return None,
1094 };
1095 Some(Articulation::of_kind(kind))
1096 })
1097 .collect()
1098 }
1099
1100 fn expressions(&self) -> Vec<Expression> {
1101 let Some(notations) = self.notations() else {
1102 return Vec::new();
1103 };
1104 notations
1105 .chars()
1106 .filter_map(|c| match c {
1107 'F' | 'E' => Some(Expression::from(Fermata::new())),
1108 't' => Some(Expression::from(Ornament::of_kind(OrnamentKind::Trill))),
1109 'r' => Some(Expression::from(Ornament::of_kind(OrnamentKind::Turn))),
1110 'M' => Some(Expression::from(Ornament::of_kind(OrnamentKind::Mordent))),
1111 _ => None,
1112 })
1113 .collect()
1114 }
1115
1116 fn dynamics(&self) -> Vec<Dynamic> {
1119 let Some(mut data) = self.notations() else {
1120 return Vec::new();
1121 };
1122 let mut out = Vec::new();
1123 for target in [
1124 "ppp", "fff", "pp", "ff", "fp", "mp", "mf", "p", "f", "m", "Z", "Zp", "R",
1125 ] {
1126 let Some(at) = data.find(target) else {
1127 continue;
1128 };
1129 data.replace_range(at..at + target.len(), "");
1130 let value = match target {
1131 "m" => "mp",
1132 "Z" | "Zp" | "R" => "sf",
1133 other => other,
1134 };
1135 out.push(Dynamic::new(value));
1136 }
1137 out
1138 }
1139}
1140
1141fn beams_of(written: &str) -> Beams {
1143 let mut beams = Beams::default();
1144 for c in written.chars() {
1145 let (kind, direction) = match c {
1146 '[' => (BeamType::Start, None),
1147 ']' => (BeamType::Stop, None),
1148 '=' => (BeamType::Continue, None),
1149 '/' => (BeamType::PartialBeam, Some(BeamDirection::Right)),
1150 '\\' => (BeamType::PartialBeam, Some(BeamDirection::Left)),
1151 _ => continue,
1152 };
1153 beams.append(kind, direction);
1154 }
1155 beams
1156}
1157
1158fn note_or_chord(
1162 records: &[Record<'_>],
1163 divisions: FloatType,
1164 previous: Option<Option<TieType>>,
1165) -> Result<(StreamElement, Vec<Dynamic>, Option<TieType>)> {
1166 let first = records[0];
1167 let duration = Duration::new(first.quarter_length(divisions)?)?;
1168 let held = matches!(previous, Some(Some(TieType::Start | TieType::Continue)));
1169 let tie = if first.is_tied() {
1170 Some(if held {
1171 TieType::Continue
1172 } else {
1173 TieType::Start
1174 })
1175 } else if held {
1176 Some(TieType::Stop)
1177 } else {
1178 None
1179 };
1180 let mut pitches = Vec::new();
1181 for record in records {
1182 pitches.push(record.pitch()?);
1183 }
1184 let lyrics = first.lyrics();
1185 let beams = first.beams().map(|written| beams_of(&written));
1186 let element = if pitches.len() == 1 {
1187 let mut note = Note::from_pitch(pitches.remove(0)).with_duration(duration);
1188 for lyric in lyrics.iter().flatten() {
1189 note.add_lyric(lyric, None, false)?;
1190 }
1191 if let Some(beams) = beams {
1192 note.set_beams(beams);
1193 }
1194 note.articulations_mut().extend(first.articulations());
1195 note.expressions_mut().extend(first.expressions());
1196 note.set_tie(tie.map(Tie::new));
1197 StreamElement::Note(note)
1198 } else {
1199 let notes: Vec<Note> = pitches.into_iter().map(Note::from_pitch).collect();
1200 let mut chord = Chord::new(notes)?.with_duration(duration);
1201 for lyric in lyrics.iter().flatten() {
1202 chord.add_lyric(lyric, None, false)?;
1203 }
1204 if let Some(beams) = beams {
1205 chord.set_beams(beams);
1206 }
1207 chord.articulations_mut().extend(first.articulations());
1208 chord.expressions_mut().extend(first.expressions());
1209 for note in chord.notes_mut() {
1210 note.set_tie(tie.map(Tie::new));
1211 }
1212 StreamElement::Chord(chord)
1213 };
1214 Ok((element, first.dynamics(), tie))
1215}
1216
1217fn base40_interval(delta: i32) -> Result<Interval> {
1219 base40::delta_to_interval(delta).map_err(|refused| error(refused.message()))
1220}
1221
1222#[cfg(test)]
1223mod tests {
1224 use super::*;
1225
1226 #[test]
1227 fn a_base40_distance_is_an_interval_with_its_octaves() {
1228 assert_eq!(base40_interval(-11).unwrap().directed_name(), "m-3");
1229 assert_eq!(base40_interval(23).unwrap().directed_name(), "P5");
1230 assert_eq!(base40_interval(63).unwrap().directed_name(), "P12");
1231 assert!(base40_interval(2).is_err());
1232 }
1233
1234 #[test]
1235 fn a_measure_number_is_every_digit_of_its_record_and_a_pickup_is_0() {
1236 let lines = ["mheavy2 12".to_string(), "C4 1 q".to_string()];
1237 assert_eq!(measure_object(&lines).unwrap().number(), 212);
1238 let lines = ["measure".to_string(), "C4 1 q".to_string()];
1239 assert_eq!(measure_object(&lines).unwrap().number(), 1);
1240 let lines = ["C4 1 q".to_string()];
1241 assert_eq!(measure_object(&lines).unwrap().number(), 0);
1242 }
1243
1244 #[test]
1245 fn a_refusal_says_its_line_measure_and_part() {
1246 let part = |attributes: &str, record: &str| {
1247 format!(
1248 "\n\n\n01/01/01 x\nWK#:1 MV#:1\nsource\nWork\nMovement\nFlute\n\
1249 1 0\nGroup memberships: score\nscore: part 1 of 1\n\
1250 & a comment\n&\n{attributes}\nC5 2 q u\n\
1251 D5 2 q u\nmeasure 2\n{record}\n/END\n"
1252 )
1253 };
1254 let error =
1255 from_musedata(&[&part("$ K:0 Q:2 T:2/4 C:4", "E5 x h")]).unwrap_err();
1256 assert_eq!(error.to_string(), "MuseData error: \" x\" is not a number");
1257 let mut wanted = Position::line(19).in_part("Flute");
1258 wanted.measure = Some("2".to_string());
1259 assert_eq!(error.position(), Some(&wanted));
1260
1261 let error =
1262 from_musedata(&[&part("$ K:0 Q:2 T:x/4 C:4", "E5 4 h")]).unwrap_err();
1263 assert_eq!(error.position(), Some(&Position::line(15).in_part("Flute")));
1264 }
1265
1266 #[test]
1267 fn beams_are_read_from_their_column() {
1268 let beams = beams_of("[[/");
1269 assert_eq!(beams.len(), 3);
1270 }
1271
1272 #[test]
1273 fn a_dynamic_is_taken_out_before_the_next_is_looked_for() {
1274 let record = Record {
1275 line: "C5 3 e d [ pp.",
1276 stage: Stage::Two,
1277 number: None,
1278 };
1279 let values: Vec<String> = record
1280 .dynamics()
1281 .iter()
1282 .map(|dynamic| dynamic.value().to_string())
1283 .collect();
1284 assert_eq!(values, ["pp"]);
1285 }
1286
1287 #[test]
1288 fn ties_stay_in_their_voice_across_a_back() {
1289 let part = "\n\n\n04/16/93 E. Correia\nWK#:581 MV#:3c\n\
1290 Breitkopf & Hartel, Vol. 13\nClarinet Quintet\nTrio II\nPiano\n1 0\n\
1291 Group memberships: score\nscore: part 1 of 1\n\
1292 $ K:0 Q:1 T:2/4 C:4\nmeasure 1\nC5 1 q u\n\
1293 E5 1- q u\nback 2\nC4 1 q d\n\
1294 G3 1- q d\nmeasure 2\nE5 2 h u\n\
1295 back 2\nG3 1 q d\nA3 1 q d\n\
1296 measure 3\nF5 2 h\n/END\n";
1297 let score = from_musedata(&[part]).expect("the part is read");
1298 let ties = |stream: &Stream| -> Vec<Option<&'static str>> {
1299 stream
1300 .notes()
1301 .iter()
1302 .map(|(_, element)| match element {
1303 StreamElement::Note(note) => note.tie().map(|tie| tie.tie_type().as_str()),
1304 _ => None,
1305 })
1306 .collect()
1307 };
1308 let parts = score.parts();
1309 let measures = parts[0].measures();
1310 let first = measures[0].voices();
1311 let second = measures[1].voices();
1312 assert_eq!(ties(first[0]), [None, Some("start")]);
1313 assert_eq!(ties(first[1]), [None, Some("start")]);
1314 assert_eq!(ties(second[0]), [Some("stop")]);
1315 assert_eq!(ties(second[1]), [Some("stop"), None]);
1316 assert_eq!(ties(measures[2]), [None]);
1317 }
1318
1319 #[test]
1320 fn windows_line_endings_are_read_as_unix_ones() {
1321 let unix = "Title\nSource\n$ K:0 Q:4 T:4/4 C:4\nC4 4 q\n/END\n";
1322 let windows = unix.replace('\n', "\r\n");
1323 let lines = |text: &str| -> Vec<Vec<String>> {
1324 split_parts(text)
1325 .expect("the parts are read")
1326 .into_iter()
1327 .map(|part| part.lines)
1328 .collect()
1329 };
1330 assert_eq!(lines(&windows), lines(unix));
1331 assert!(
1332 lines(&windows)
1333 .iter()
1334 .flatten()
1335 .all(|line| !line.contains('\r'))
1336 );
1337 }
1338}