1use std::collections::HashMap;
8
9use crate::bar::{Barline, BarlineType, RepeatDirection};
10use crate::chord::Chord;
11use crate::clef::{Clef, ClefKind};
12use crate::defaults::{FloatType, IntegerType};
13use crate::duration::{Duration, Tuplet};
14use crate::error::{Error, Position, Result};
15use crate::key::KeySignature;
16use crate::makenotation::{make_measures, op_frac};
17use crate::meter::TimeSignature;
18use crate::notation::{Lyric, Syllabic, Tie, TieType};
19use crate::note::Note;
20use crate::pitch::Pitch;
21use crate::pitch::accidental::Accidental;
22use crate::rest::Rest;
23use crate::stream::{Stream, StreamElement, StreamEvent, StreamKind};
24use crate::xml::Xml;
25
26fn error(message: impl Into<String>) -> Error {
27 Error::capella(message.into())
28}
29
30type Placed = Vec<(FloatType, StreamElement)>;
32
33type Staff = (String, Placed);
35
36pub fn from_capella(document: &str) -> Result<Stream> {
73 let document = document.strip_prefix('\u{feff}').unwrap_or(document);
74 let root = without_namespaces(Xml::parse(document)?);
75
76 let mut systems_found = children(&root, "systems");
77 let systems = match (systems_found.next(), systems_found.next()) {
78 (Some(systems), None) => systems,
79 (None, _) => return Err(error("Cannot find a <systems> tag in the <score> object")),
80 (Some(_), Some(_)) => {
81 return Err(error(
82 "Found more than one <systems> tag in the <score> object, what does this mean?",
83 ));
84 }
85 };
86 let systems: Vec<&Xml> = children(systems, "system").collect();
87 if systems.is_empty() {
88 return Err(error(
89 "Cannot find any <system> tags in the <systems> tag in the <score> object",
90 ));
91 }
92
93 let mut parts: Vec<(String, usize, Placed)> = Vec::new();
96 let mut system_offset = 0.0;
97 let mut barlines_read = HashMap::new();
98 for system in systems {
99 let staves = read_system(system, &mut barlines_read)?;
100 let length = staves
101 .iter()
102 .map(|(_, elements)| end_of(elements))
103 .fold(0.0, FloatType::max);
104 for (place, (id, elements)) in staves.into_iter().enumerate() {
105 let index = match parts.iter().position(|(known, _, _)| *known == id) {
106 Some(index) => index,
107 None => {
108 parts.push((id, place, Vec::new()));
109 parts.len() - 1
110 }
111 };
112 for (offset, element) in elements {
113 parts[index]
114 .2
115 .push((op_frac(offset + system_offset), element));
116 }
117 }
118 system_offset += length;
119 }
120
121 let mut slots: Vec<Option<Staff>> = (0..parts.len()).map(|_| None).collect();
124 for (id, place, elements) in parts {
125 let slot = slots
126 .get_mut(place)
127 .ok_or_else(|| error("part entries do not match partDict!"))?;
128 *slot = Some((id, elements));
129 }
130 let mut score = Stream::with_kind(StreamKind::Score);
131 for (id, elements) in slots.into_iter().flatten() {
132 let mut part = Stream::with_kind(StreamKind::Part);
133 for (offset, element) in elements {
134 part.insert_sorted(vec![StreamEvent::new(offset, element)]);
135 }
136 let part = without_restatements(part);
137 let mut measured = make_measures(&part)?;
138 measured.set_id(Some(id));
139 score.insert(0.0, measured);
140 }
141 Ok(score)
142}
143
144fn without_namespaces(mut element: Xml) -> Xml {
147 if let Some((_, local)) = element.tag.rsplit_once(':') {
148 element.tag = local.to_string();
149 }
150 element.children = element
151 .children
152 .into_iter()
153 .map(without_namespaces)
154 .collect();
155 element
156}
157
158fn children<'a>(element: &'a Xml, tag: &'a str) -> impl Iterator<Item = &'a Xml> {
159 element
160 .children
161 .iter()
162 .filter(move |child| child.tag == tag)
163}
164
165fn only<'a>(element: &'a Xml, tag: &'a str, missing: &str, several: &str) -> Result<&'a Xml> {
167 let mut found = children(element, tag);
168 match (found.next(), found.next()) {
169 (Some(child), None) => Ok(child),
170 (None, _) => Err(error(missing)),
171 (Some(_), Some(_)) => Err(error(several)),
172 }
173}
174
175fn end_of(elements: &[(FloatType, StreamElement)]) -> FloatType {
176 elements
177 .iter()
178 .map(|(offset, element)| offset + element.quarter_length())
179 .fold(0.0, FloatType::max)
180}
181
182fn read_system(system: &Xml, barlines_read: &mut HashMap<String, usize>) -> Result<Vec<Staff>> {
188 let staves = only(
189 system,
190 "staves",
191 "No <staves> tag found in this <system> element",
192 "More than one <staves> tag found in this <system> element",
193 )?;
194 let mut out = Vec::new();
195 for staff in children(staves, "staff") {
196 let id = staff.get("layout").unwrap_or("UnknownPart").to_string();
197 let in_part = |error: Error| error.placed(|| Position::default().in_part(&id));
198 let voices = children(staff, "voices").next().ok_or_else(|| {
199 in_part(error(
200 "No <voices> tag found in the <staff> tag for the <staves> element for this \
201 <system> element",
202 ))
203 })?;
204 let voice_list: Vec<&Xml> = children(voices, "voice").collect();
205 if voice_list.is_empty() {
206 return Err(in_part(error(
207 "No <voice> tag found in the <voices> tag for the <staff> tag for the <staves> \
208 element for this <system> element",
209 )));
210 }
211 let mut elements = Vec::new();
212 if let [voice] = voice_list.as_slice() {
213 let objects = only(
214 voice,
215 "noteObjects",
216 "No <noteObjects> tag found in the <voice> tag",
217 "More than one <noteObjects> tag found in the <voice> tag",
218 )
219 .map_err(in_part)?;
220 let barlines = barlines_read.entry(id.clone()).or_insert(0);
221 for object in &objects.children {
222 let measure = *barlines + 1;
223 let read = note_object(object).map_err(|error| {
224 error.placed(|| Position::measure(measure.to_string()).in_part(&id))
225 })?;
226 if object.tag == "barline" {
227 *barlines += 1;
228 }
229 for element in read {
230 let at = end_of(&elements);
231 elements.push((at, element));
232 }
233 }
234 }
235 out.push((id, elements));
236 }
237 Ok(out)
238}
239
240fn note_object(object: &Xml) -> Result<Vec<StreamElement>> {
242 Ok(match object.tag.as_str() {
243 "clefSign" => clef(object)?.map(StreamElement::Clef).into_iter().collect(),
244 "keySign" => match object.get("fifths") {
245 Some(fifths) => vec![StreamElement::KeySignature(KeySignature::new(integer(
246 fifths,
247 )?))],
248 None => Vec::new(),
249 },
250 "timeSign" => match object.get("time") {
251 Some(time) if time != "infinite" => {
252 vec![StreamElement::TimeSignature(
253 TimeSignature::from_ratio_string(time)?,
254 )]
255 }
256 _ => Vec::new(),
257 },
258 "rest" => {
259 let written = children(object, "duration")
260 .next()
261 .ok_or_else(|| error("a rest with no duration"))?;
262 vec![StreamElement::Rest(Rest::new(duration(written)?))]
263 }
264 "chord" => vec![chord(object)?],
265 "barline" => barlines(object)?,
266 _ => Vec::new(),
267 })
268}
269
270fn integer(text: &str) -> Result<IntegerType> {
271 text.trim()
272 .parse()
273 .map_err(|_| error(format!("{text:?} is not a number")))
274}
275
276fn clef(object: &Xml) -> Result<Option<Clef>> {
278 let Some(value) = object.get("clef") else {
279 return Ok(None);
280 };
281 let kind = match value {
282 "treble" => Some(ClefKind::TrebleClef),
283 "bass" => Some(ClefKind::BassClef),
284 "alto" => Some(ClefKind::AltoClef),
285 "tenor" => Some(ClefKind::TenorClef),
286 "G2-" => Some(ClefKind::Treble8vbClef),
287 _ => None,
288 };
289 if let Some(kind) = kind {
290 return Ok(Some(Clef::of_kind(kind)));
291 }
292 if value.starts_with('p') {
293 return Ok(Some(Clef::of_kind(ClefKind::PercussionClef)));
294 }
295 let letters: Vec<char> = value.chars().collect();
296 if letters.len() > 1 {
297 let shift = match letters.get(2) {
298 Some('+') => 1,
299 Some('-') => -1,
300 _ => 0,
301 };
302 let sign_and_line: String = letters[..2].iter().collect();
303 return Clef::from_string(&sign_and_line, shift).map(Some);
304 }
305 Ok(None)
306}
307
308fn chord(object: &Xml) -> Result<StreamElement> {
310 let durations: Vec<&Xml> = children(object, "duration").collect();
311 let heads: Vec<&Xml> = children(object, "heads").collect();
312 let ([written], [heads]) = (durations.as_slice(), heads.as_slice()) else {
313 return Err(error("Malformed chord!"));
314 };
315 let mut notes = Vec::new();
316 for head in children(heads, "head") {
317 notes.push(note(head)?);
318 }
319 if notes.is_empty() {
320 return Err(error("Malformed chord!"));
321 }
322 let duration = duration(written)?;
323 let lyrics: Vec<Lyric> = match children(object, "lyric").next() {
324 Some(lyric) => children(lyric, "verse")
325 .map(verse)
326 .collect::<Result<Vec<_>>>()?
327 .into_iter()
328 .flatten()
329 .collect(),
330 None => Vec::new(),
331 };
332 Ok(if notes.len() == 1 {
333 let mut note = notes.remove(0);
334 note.set_duration(duration);
335 *note.lyrics_mut() = lyrics;
336 StreamElement::Note(note)
337 } else {
338 let mut chord = Chord::new(notes)?.with_duration(duration);
339 if let Some(first) = chord.notes_mut().first_mut() {
340 *first.lyrics_mut() = lyrics;
341 }
342 StreamElement::Chord(chord)
343 })
344}
345
346fn note(head: &Xml) -> Result<Note> {
348 let written = head
349 .get("pitch")
350 .ok_or_else(|| error("Cannot deal with <head> element without pitch!"))?;
351 let mut pitch = Pitch::from_name(written)?;
352 let octave = pitch.octave().unwrap_or(4);
353 pitch.set_octave(Some(octave - 1));
354 let alters: Vec<&Xml> = children(head, "alter").collect();
355 match alters.as_slice() {
356 [] => {}
357 [alter] => pitch.set_accidental(accidental(alter)?),
358 _ => return Err(error("Cannot deal with multiple <alter> elements!")),
359 }
360 let mut note = Note::from_pitch(pitch);
361 let ties: Vec<&Xml> = children(head, "tie").collect();
362 match ties.as_slice() {
363 [] => {}
364 [tie] => note.set_tie(tie_of(tie)),
365 _ => return Err(error("Cannot deal with multiple <tie> elements!")),
366 }
367 Ok(note)
368}
369
370fn accidental(alter: &Xml) -> Result<Accidental> {
372 let steps = match alter.get("step") {
373 Some(step) => integer(step)?,
374 None => 0,
375 };
376 let name = match steps {
377 0 => "natural",
378 1 => "sharp",
379 -1 => "flat",
380 2 => "double-sharp",
381 -2 => "double-flat",
382 3 => "triple-sharp",
383 -3 => "triple-flat",
384 4 => "quadruple-sharp",
385 -4 => "quadruple-flat",
386 other => return Err(error(format!("no accidental of {other} steps"))),
387 };
388 let mut accidental = Accidental::new(name)?;
389 if alter.get("display") == Some("suppress") {
390 accidental.set_display_type("never")?;
391 }
392 Ok(accidental)
393}
394
395fn tie_of(tie: &Xml) -> Option<Tie> {
397 let begins = tie.get("begin") == Some("true");
398 let ends = tie.get("end") == Some("true");
399 let kind = match (begins, ends) {
400 (true, true) => TieType::Continue,
401 (true, false) => TieType::Start,
402 (false, true) => TieType::Stop,
403 (false, false) => return None,
404 };
405 Some(Tie::new(kind))
406}
407
408fn verse(verse: &Xml) -> Result<Option<Lyric>> {
411 let text = match verse.text() {
412 Some(text) if !text.is_empty() => text,
413 _ => return Ok(None),
414 };
415 let mut lyric = Lyric::new(text);
416 if let Some(index) = verse.get("i") {
417 let number = integer(index)?
418 .checked_add(1)
419 .ok_or_else(|| error(format!("verse {index} is past the last a lyric can be")))?;
420 lyric.set_number(number);
421 }
422 if verse.get("hyphen") == Some("true") {
423 lyric.set_syllabic(Syllabic::Begin);
424 }
425 Ok(Some(lyric))
426}
427
428fn duration(written: &Xml) -> Result<Duration> {
431 let mut length = 0.0;
432 if let Some(base) = written.get("base")
433 && let Some((numerator, denominator)) = base.split_once('/')
434 {
435 length =
436 4.0 * FloatType::from(integer(numerator)?) / FloatType::from(integer(denominator)?);
437 }
438 let mut duration = Duration::new(length)?;
439 if let Some(dots) = written.get("dots") {
440 let dots = u32::try_from(integer(dots)?).map_err(|_| error("a negative count of dots"))?;
441 if let Some((value, _)) = duration.type_and_dots() {
442 duration = Duration::from_type_with_dots(value, dots);
443 duration.set_expression_is_inferred(true);
444 }
445 }
446 for tuplet in children(written, "tuplet") {
447 duration.append_tuplet(tuplet_of(tuplet)?);
448 }
449 Ok(duration)
450}
451
452fn tuplet_of(tuplet: &Xml) -> Result<Tuplet> {
455 let mut actual = 1;
456 let mut normal = 1;
457 if let Some(count) = tuplet.get("count") {
458 actual = u32::try_from(integer(count)?).map_err(|_| error("a negative tuplet"))?;
459 while actual > normal * 2 {
460 normal *= 2;
461 }
462 }
463 if tuplet.get("prolong") == Some("true") {
464 normal *= 2;
465 }
466 Ok(Tuplet::ratio(actual, normal))
467}
468
469fn barlines(object: &Xml) -> Result<Vec<StreamElement>> {
471 let Some(kind) = object.get("type") else {
472 return Ok(Vec::new());
473 };
474 let mut out = Vec::new();
475 if kind.starts_with("rep") {
476 let (end, start) = match kind {
477 "repEnd" => (true, false),
478 "repBegin" => (false, true),
479 "repEndBegin" => (true, true),
480 _ => (false, false),
481 };
482 if end {
483 out.push(StreamElement::Barline(Barline::repeat(
484 RepeatDirection::End,
485 None,
486 )));
487 }
488 if start {
489 out.push(StreamElement::Barline(Barline::repeat(
490 RepeatDirection::Start,
491 None,
492 )));
493 }
494 } else {
495 let bar_type = match kind {
496 "single" => BarlineType::Regular,
497 "double" => BarlineType::Double,
498 "end" => BarlineType::Final,
499 _ => return Ok(Vec::new()),
500 };
501 out.push(StreamElement::Barline(Barline::new(bar_type)));
502 }
503 Ok(out)
504}
505
506fn without_restatements(part: Stream) -> Stream {
509 let mut last_clef: Option<Clef> = None;
510 let mut last_key: Option<KeySignature> = None;
511 let mut kept = Vec::new();
512 for event in part.events() {
513 match event.element() {
514 StreamElement::Clef(clef) => {
515 if last_clef.as_ref().is_some_and(|last| same_clef(last, clef)) {
516 continue;
517 }
518 last_clef = Some(clef.clone());
519 }
520 StreamElement::KeySignature(signature) => {
521 if last_key
523 .as_ref()
524 .is_some_and(|last| last.sharps() == signature.sharps())
525 {
526 continue;
527 }
528 last_key = Some(signature.clone());
529 }
530 _ => {}
531 }
532 kept.push(event.clone());
533 }
534 part.with_events(kept)
535}
536
537fn same_clef(left: &Clef, right: &Clef) -> bool {
539 left.kind() == right.kind()
540 && left.sign() == right.sign()
541 && left.line() == right.line()
542 && left.octave_change() == right.octave_change()
543}
544
545#[cfg(test)]
546mod tests {
547 use super::*;
548
549 fn staff(objects: &str) -> String {
550 format!(
551 "<score xmlns=\"http://www.capella.de/CapXML/2.0\"><systems><system><staves>\
552 <staff layout=\"S\"><voices><voice><noteObjects>{objects}</noteObjects></voice>\
553 </voices></staff></staves></system></systems></score>"
554 )
555 }
556
557 #[test]
558 fn a_capella_octave_is_one_higher_than_music21_s() {
559 let score = from_capella(&staff(
560 "<chord><duration base=\"1/2\"/><heads><head pitch=\"G5\"><alter step=\"1\"/>\
561 </head></heads></chord>",
562 ))
563 .unwrap();
564 assert_eq!(score.pitches()[0].name_with_octave(), "G#4");
565 }
566
567 #[test]
568 fn a_tuplet_count_takes_the_power_of_two_below_it() {
569 let tuplet = Xml::parse("<tuplet count=\"5\"/>").unwrap();
570 assert_eq!(tuplet_of(&tuplet).unwrap(), Tuplet::ratio(5, 4));
571 let prolonged = Xml::parse("<tuplet count=\"3\" prolong=\"true\"/>").unwrap();
572 assert_eq!(tuplet_of(&prolonged).unwrap(), Tuplet::ratio(3, 4));
573 let plain = Xml::parse("<tuplet count=\"3\" prolong=\"false\"/>").unwrap();
574 assert_eq!(tuplet_of(&plain).unwrap(), Tuplet::ratio(3, 2));
575 }
576
577 #[test]
578 fn a_single_barline_is_a_regular_one() {
579 assert!(from_capella(&staff("<barline type=\"single\"/>")).is_ok());
580 assert!(from_capella(&staff("<barline/>")).is_ok());
581 }
582
583 #[test]
584 fn a_refusal_says_its_part_and_measure() {
585 let chord = "<chord><duration base=\"1/4\"/><heads><head pitch=\"C5\"/></heads></chord>";
586 let objects = format!(
587 "{chord}<barline type=\"single\"/>{chord}<barline type=\"single\"/>\
588 <chord><duration base=\"1/x\"/><heads><head pitch=\"C5\"/></heads></chord>"
589 );
590 let error = from_capella(&staff(&objects)).unwrap_err();
591 assert_eq!(error.to_string(), "Capella error: \"x\" is not a number");
592 assert_eq!(error.position(), Some(&Position::measure("3").in_part("S")));
593
594 let error =
595 from_capella(&staff("<chord><heads><head pitch=\"H9\"/></heads></chord>")).unwrap_err();
596 assert_eq!(error.position(), Some(&Position::measure("1").in_part("S")));
597 }
598
599 #[test]
600 fn a_document_with_no_systems_is_refused() {
601 assert!(from_capella("<score/>").is_err());
602 }
603
604 #[test]
605 fn a_verse_numbered_past_the_largest_integer_is_refused() {
606 let score = from_capella(&staff(
607 "<chord><duration base=\"1/4\"/><lyric><verse i=\"2147483647\">la\
608 </verse></lyric><heads><head pitch=\"C5\"/></heads></chord>",
609 ));
610 assert!(score.is_err());
611 }
612}