1use crate::bar::BarlineType;
23use crate::chord::Chord;
24use crate::clef::Clef;
25use crate::defaults::FloatType;
26use crate::duration::Duration;
27use crate::error::{Error, Result};
28use crate::expressions::Expression;
29use crate::key::Key;
30use crate::metadata::Metadata;
31use crate::meter::TimeSignature;
32use crate::notation::{Beams, StemDirection, Syllabic, TieType};
33use crate::note::Note;
34use crate::pitch::Pitch;
35use crate::rest::Rest;
36use crate::stream::{Stream, StreamElement, StreamKind};
37use crate::tempo::MetronomeMark;
38
39#[derive(Clone, Debug, PartialEq, Eq)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42pub struct LilyOptions {
43 pub version: (u32, u32),
46}
47
48impl Default for LilyOptions {
49 fn default() -> Self {
50 Self { version: (2, 24) }
51 }
52}
53
54fn lily_error(message: impl Into<String>) -> Error {
55 Error::Notation(message.into())
56}
57
58#[derive(Clone, Debug)]
62enum Item {
63 Node(usize),
64 Text(String),
65}
66
67#[derive(Clone, Debug)]
69enum Kind {
70 Top {
71 contents: Vec<Item>,
72 },
73 Header {
74 body: Option<usize>,
75 },
76 HeaderBody {
77 assignments: Vec<Item>,
78 },
79 Assignment {
80 id: String,
81 value: String,
82 },
83 Embedded(String),
84 ScoreBlock {
85 body: usize,
86 },
87 ScoreBody {
88 music: usize,
89 },
90 OutputDef,
91 Layout,
92 MusicList {
93 contents: Vec<Item>,
94 },
95 Music {
96 composite: usize,
97 },
98 Sequential {
99 list: usize,
100 start_staff: bool,
101 },
102 Simultaneous {
103 list: usize,
104 },
105 SimpleMusic {
106 chord: usize,
107 },
108 ContextModification {
109 list: Vec<String>,
110 },
111 Composite {
112 prefix: Option<usize>,
113 grouped: Option<usize>,
114 lyrics: Option<usize>,
115 },
116 Grouped {
117 simultaneous: Option<usize>,
118 sequential: Option<usize>,
119 },
120 OptionalId(String),
121 Prefix {
122 kind: &'static str,
123 context: String,
124 id: Option<usize>,
125 modification: Option<usize>,
126 music: usize,
127 fraction: String,
128 },
129 NewLyrics {
130 lists: Vec<usize>,
131 },
132 PropertySet {
133 property: String,
134 value: String,
135 },
136 EventChord {
137 simple: Option<usize>,
138 post: Vec<String>,
139 note_chord: Option<usize>,
140 },
141 NoteChordElement {
142 body: usize,
143 duration: usize,
144 post: Vec<String>,
145 },
146 ChordBody {
147 elements: Vec<usize>,
148 },
149 ChordBodyElement {
150 parts: Vec<Item>,
151 },
152 Pitch {
153 name: String,
154 quotes: String,
155 },
156 Duration {
157 number: String,
158 dots: u32,
159 },
160 SimpleElement {
161 parts: Vec<Item>,
162 },
163 LyricElement(String),
164}
165
166#[derive(Clone, Debug)]
167struct Node {
168 parent: Option<usize>,
169 kind: Kind,
170}
171
172#[derive(Default)]
173struct Tree {
174 nodes: Vec<Node>,
175}
176
177impl Tree {
178 fn add(&mut self, kind: Kind) -> usize {
182 let index = self.nodes.len();
183 let (attributes, listed) = children(&kind);
184 self.nodes.push(Node { parent: None, kind });
185 for child in attributes {
186 self.nodes[child].parent = Some(index);
187 }
188 for child in listed {
189 self.adopt(index, child);
190 }
191 index
192 }
193
194 fn set_parent(&mut self, child: usize, parent: usize) {
196 self.nodes[child].parent = Some(parent);
197 }
198
199 fn adopt(&mut self, parent: usize, child: usize) {
202 if self.nodes[child].parent.is_none() {
203 self.nodes[child].parent = Some(parent);
204 }
205 }
206
207 fn contents_mut(&mut self, node: usize) -> Result<&mut Vec<Item>> {
208 match &mut self.nodes[node].kind {
209 Kind::MusicList { contents } | Kind::Top { contents } => Ok(contents),
210 _ => Err(lily_error(
211 "Cannot get a currentMusicList from contextObject",
212 )),
213 }
214 }
215
216 fn newline_indent(&self, node: usize) -> String {
218 let mut depth = 0;
219 let mut current = self.nodes[node].parent;
220 while let Some(parent) = current {
221 depth += 1;
222 current = self.nodes[parent].parent;
223 }
224 format!("\n{}", " ".repeat(depth))
225 }
226
227 fn str_of(&self, node: usize) -> String {
229 self.string_output(node).replace("\n\n", "\n")
230 }
231
232 fn item(&self, item: &Item) -> String {
233 match item {
234 Item::Node(node) => self.str_of(*node),
235 Item::Text(text) => text.clone(),
236 }
237 }
238
239 fn newline_separated(&self, node: usize, items: &[Item]) -> String {
241 let indent = self.newline_indent(node);
242 items.iter().map(|item| self.item(item) + &indent).collect()
243 }
244
245 fn enclose(&self, node: usize, inner: Option<String>) -> String {
247 let Some(inner) = inner else {
248 return " { } ".to_string();
249 };
250 let indent = self.newline_indent(node);
251 format!(" {{ {indent}{inner}{indent} }} {indent}")
252 }
253
254 fn string_output(&self, node: usize) -> String {
255 match &self.nodes[node].kind {
256 Kind::Top { contents } => self.newline_separated(node, contents),
257 Kind::Header { body } => {
258 format!(
259 "\\header{}",
260 self.enclose(node, body.map(|body| self.str_of(body)))
261 )
262 }
263 Kind::HeaderBody { assignments } => self.newline_separated(node, assignments),
264 Kind::Assignment { id, value } => [id.as_str(), "=", "e(value), " "].join(" "),
265 Kind::Embedded(content) => content.clone(),
266 Kind::ScoreBlock { body } => {
267 format!("\\score {}", self.enclose(node, Some(self.str_of(*body))))
268 }
269 Kind::ScoreBody { music } | Kind::Music { composite: music } => {
270 self.string_output(*music)
271 }
272 Kind::OutputDef => "\\paper { }".to_string(),
273 Kind::Layout => {
274 let lines = [
275 "\\layout {",
276 " \\context {",
277 " \\RemoveEmptyStaves",
278 " \\override VerticalAxisGroup.remove-first = ##t",
279 " }",
280 "}",
281 ];
282 let indent = self.newline_indent(node);
283 lines.iter().map(|line| format!("{line}{indent}")).collect()
284 }
285 Kind::MusicList { contents } => self.newline_separated(node, contents),
286 Kind::Sequential { list, start_staff } => format!(
287 "{{ {}{} }} {}",
288 if *start_staff { "\\startStaff " } else { "" },
289 self.string_output(*list),
290 self.newline_indent(node)
291 ),
292 Kind::Simultaneous { list } => {
293 let indent = self.newline_indent(node);
294 format!("{indent}<< {} >>{indent}", self.string_output(*list))
295 }
296 Kind::SimpleMusic { chord } => self.string_output(*chord),
297 Kind::ContextModification { list } => {
298 let indent = self.newline_indent(node);
299 format!(
300 "\\with {{ {indent}{}{indent} }} {indent}",
301 list.join(&indent)
302 )
303 }
304 Kind::Composite {
305 prefix,
306 grouped,
307 lyrics,
308 } => {
309 let lyrics = lyrics.map(|lyrics| self.str_of(lyrics)).unwrap_or_default();
310 let music = prefix.or(*grouped).expect("composite music holds music");
311 format!("{}\n{lyrics}", self.str_of(music))
312 }
313 Kind::Grouped {
314 simultaneous,
315 sequential,
316 } => self.str_of(
317 simultaneous
318 .or(*sequential)
319 .expect("grouped music holds music"),
320 ),
321 Kind::OptionalId(content) => format!(" = {content}"),
322 Kind::Prefix {
323 kind,
324 context,
325 id,
326 modification,
327 music,
328 fraction,
329 } => {
330 if *kind == "tuplet" {
331 return format!("\\tuplet {fraction} {} ", self.str_of(*music));
332 }
333 let mut out = format!("\\{kind} {context} ");
334 if let Some(id) = id {
335 out += &(self.str_of(*id) + " ");
336 }
337 if let Some(modification) = modification {
338 out += &(self.str_of(*modification) + " ");
339 }
340 out + &self.str_of(*music) + " "
341 }
342 Kind::NewLyrics { lists } => lists
343 .iter()
344 .map(|list| format!("\\addlyrics {}", self.string_output(*list)))
345 .collect(),
346 Kind::PropertySet { property, value } => format!("\\set {property} = {value} "),
347 Kind::EventChord {
348 simple,
349 post,
350 note_chord,
351 } => {
352 if let Some(note_chord) = note_chord {
353 return self.str_of(*note_chord) + " ";
354 }
355 let mut out = simple.map(|simple| self.str_of(simple)).unwrap_or_default();
356 for event in post {
357 out += &format!(" {event}");
358 }
359 out + " "
360 }
361 Kind::NoteChordElement {
362 body,
363 duration,
364 post,
365 } => {
366 let mut out = self.str_of(*body);
367 out += &(self.str_of(*duration) + " ");
368 for event in post {
369 out += &format!("{event} ");
370 }
371 out
372 }
373 Kind::ChordBody { elements } => {
374 let inner: Vec<String> = elements
375 .iter()
376 .map(|element| self.str_of(*element))
377 .collect();
378 ["<", &inner.join(" "), "> "].join(" ")
379 }
380 Kind::ChordBodyElement { parts } => parts
381 .iter()
382 .map(|part| self.item(part))
383 .collect::<Vec<_>>()
384 .join(" "),
385 Kind::Pitch { name, quotes } => format!("{name}{quotes} "),
386 Kind::Duration { number, dots } => {
387 format!("{number}{} ", ".".repeat(*dots as usize))
388 }
389 Kind::SimpleElement { parts } => parts.iter().map(|part| self.item(part)).collect(),
390 Kind::LyricElement(text) => format!("{text} "),
391 }
392 }
393}
394
395fn children(kind: &Kind) -> (Vec<usize>, Vec<usize>) {
397 let nodes = |items: &[Item]| -> Vec<usize> {
398 items
399 .iter()
400 .filter_map(|item| match item {
401 Item::Node(node) => Some(*node),
402 Item::Text(_) => None,
403 })
404 .collect()
405 };
406 match kind {
407 Kind::Top { contents } | Kind::MusicList { contents } => (Vec::new(), nodes(contents)),
408 Kind::HeaderBody { assignments } => (Vec::new(), nodes(assignments)),
409 Kind::Header { body } => (body.iter().copied().collect(), Vec::new()),
410 Kind::ScoreBlock { body } => (vec![*body], Vec::new()),
411 Kind::ScoreBody { music } | Kind::Music { composite: music } => (vec![*music], Vec::new()),
412 Kind::Sequential { list, .. } | Kind::Simultaneous { list } => (vec![*list], Vec::new()),
413 Kind::SimpleMusic { chord } => (vec![*chord], Vec::new()),
414 Kind::Composite {
415 prefix,
416 grouped,
417 lyrics,
418 } => (
419 [*prefix, *grouped, *lyrics].into_iter().flatten().collect(),
420 Vec::new(),
421 ),
422 Kind::Grouped {
423 simultaneous,
424 sequential,
425 } => (
426 [*simultaneous, *sequential].into_iter().flatten().collect(),
427 Vec::new(),
428 ),
429 Kind::Prefix {
430 id,
431 modification,
432 music,
433 ..
434 } => (
435 [*id, *modification, Some(*music)]
436 .into_iter()
437 .flatten()
438 .collect(),
439 Vec::new(),
440 ),
441 Kind::NewLyrics { lists } => (Vec::new(), lists.clone()),
442 Kind::EventChord {
443 simple, note_chord, ..
444 } => (
445 [*simple, *note_chord].into_iter().flatten().collect(),
446 Vec::new(),
447 ),
448 Kind::NoteChordElement { body, duration, .. } => (vec![*body, *duration], Vec::new()),
449 Kind::ChordBody { elements } => (Vec::new(), elements.clone()),
450 Kind::ChordBodyElement { parts } | Kind::SimpleElement { parts } => {
451 (Vec::new(), nodes(parts))
452 }
453 _ => (Vec::new(), Vec::new()),
454 }
455}
456
457fn quote(text: &str) -> String {
459 format!("\"{}\" ", text.replace('"', "\\\""))
460}
461
462fn letters_only(text: &str) -> String {
464 text.chars()
465 .map(|c| {
466 if c.is_alphabetic() {
467 c
468 } else {
469 char::from_u32(c as u32 % 26 + 97).unwrap_or('a')
470 }
471 })
472 .collect()
473}
474
475const COLOR_DEFINITION: &str = "color = #(define-music-function (color) (string?) #{\n \\once \\override NoteHead.color = #(x11-color color)\n \\once \\override Stem.color = #(x11-color color)\n \\once \\override Rest.color = #(x11-color color)\n \\once \\override Beam.color = #(x11-color color)\n #})\n ";
476const BOOK_HEADER: &str = "\\include \"lilypond-book-preamble.ly\"\n ";
477
478struct Converter<'a> {
482 tree: Tree,
483 top: usize,
484 context: usize,
485 stored: Vec<usize>,
486 current_measure: Option<&'a Stream>,
487 in_word: bool,
488 meter: Option<TimeSignature>,
491 unnamed: usize,
494}
495
496fn stream_id(stream: &Stream, unnamed: &mut usize) -> String {
498 match stream.id() {
499 Some(id) => id.to_string(),
500 None => {
501 *unnamed += 1;
502 format!("{}", 1_000_000 + *unnamed)
503 }
504 }
505}
506
507fn is_part(stream: &Stream) -> bool {
508 matches!(stream.kind(), StreamKind::Part | StreamKind::PartStaff)
509}
510
511impl<'a> Converter<'a> {
512 fn new() -> Self {
513 let mut tree = Tree::default();
514 let top = tree.add(Kind::Top {
515 contents: Vec::new(),
516 });
517 Self {
518 tree,
519 top,
520 context: top,
521 stored: Vec::new(),
522 current_measure: None,
523 in_word: false,
524 meter: None,
525 unnamed: 0,
526 }
527 }
528
529 fn new_context(&mut self, context: usize) {
530 self.stored.push(self.context);
531 self.context = context;
532 }
533
534 fn restore_context(&mut self) {
535 self.context = self.stored.pop().unwrap_or(self.top);
536 }
537
538 fn append_to_context(&mut self, node: usize) -> Result<()> {
541 let context = self.context;
542 self.tree.contents_mut(context)?.push(Item::Node(node));
543 self.tree.set_parent(node, context);
544 Ok(())
545 }
546
547 fn embedded(&mut self, content: impl Into<String>) -> usize {
548 self.tree.add(Kind::Embedded(content.into()))
549 }
550
551 fn load_score(&mut self, score: &'a Stream, options: &LilyOptions) -> Result<()> {
553 let version = self.embedded(format!(
554 "\\version {}",
555 quote(&format!("{}.{}", options.version.0, options.version.1))
556 ));
557 let book = self.embedded(BOOK_HEADER);
558 let colors = self.embedded(COLOR_DEFINITION);
559 let header = self.tree.add(Kind::Header { body: None });
560 let score_block = self.score_block(score)?;
561 let output = self.tree.add(Kind::OutputDef);
562 let layout = self.tree.add(Kind::Layout);
563 let contents: Vec<Item> = [version, book, colors, header, score_block, output, layout]
564 .into_iter()
565 .map(Item::Node)
566 .collect();
567 if let Some(metadata) = score.metadata() {
568 self.set_header(metadata, header);
569 }
570 let context = self.context;
573 if matches!(self.tree.nodes[context].kind, Kind::Composite { .. }) {
574 return Err(lily_error(
575 "AttributeError: property 'contents' of 'LyCompositeMusic' object has no setter",
576 ));
577 }
578 for item in &contents {
579 if let Item::Node(node) = item {
580 self.tree.adopt(context, *node);
581 }
582 }
583 *self.tree.contents_mut(context)? = contents;
584 Ok(())
585 }
586
587 fn set_header(&mut self, metadata: &Metadata, header: usize) {
589 let body = self.tree.add(Kind::HeaderBody {
590 assignments: Vec::new(),
591 });
592 if let Kind::Header { body: held } = &mut self.tree.nodes[header].kind {
593 *held = Some(body);
594 }
595 self.tree.set_parent(body, header);
596 let best = ["title", "popularTitle", "alternativeTitle", "movementName"]
597 .iter()
598 .find_map(|name| {
599 metadata
600 .string_value(name)
601 .filter(|title| !title.is_empty())
602 });
603 for (id, value) in [
604 ("title", best),
605 (
606 "subtitle",
607 metadata
608 .string_value("alternativeTitle")
609 .filter(|title| !title.is_empty()),
610 ),
611 ] {
612 let Some(value) = value else {
613 continue;
614 };
615 let assignment = self.tree.add(Kind::Assignment {
616 id: id.to_string(),
617 value,
618 });
619 if let Kind::HeaderBody { assignments } = &mut self.tree.nodes[body].kind {
620 assignments.push(Item::Node(assignment));
621 }
622 self.tree.set_parent(assignment, body);
623 }
624 }
625
626 fn score_block(&mut self, score: &'a Stream) -> Result<usize> {
628 let composite = self.tree.add(Kind::Composite {
629 prefix: None,
630 grouped: None,
631 lyrics: None,
632 });
633 self.new_context(composite);
634 let parts: Vec<&'a Stream> = score
635 .events()
636 .iter()
637 .filter_map(|event| match event.element() {
638 StreamElement::Stream(inner) if is_part(inner) => Some(&**inner),
639 _ => None,
640 })
641 .collect();
642 if parts.is_empty() {
643 let prefix = self.prefix_from_stream(score, None, None, false)?;
644 if let Kind::Composite { prefix: held, .. } = &mut self.tree.nodes[composite].kind {
645 *held = Some(prefix);
646 }
647 self.tree.set_parent(prefix, composite);
648 } else {
649 let grouped = self.grouped_from_parts(&parts)?;
650 if let Kind::Composite { grouped: held, .. } = &mut self.tree.nodes[composite].kind {
651 *held = Some(grouped);
652 }
653 self.tree.set_parent(grouped, composite);
654 }
655 let music = self.tree.add(Kind::Music { composite });
656 let body = self.tree.add(Kind::ScoreBody { music });
657 let block = self.tree.add(Kind::ScoreBlock { body });
658 self.restore_context();
659 Ok(block)
660 }
661
662 fn grouped_from_parts(&mut self, parts: &[&'a Stream]) -> Result<usize> {
664 let grouped = self.tree.add(Kind::Grouped {
665 simultaneous: None,
666 sequential: None,
667 });
668 let list = self.tree.add(Kind::MusicList {
669 contents: Vec::new(),
670 });
671 let simultaneous = self.tree.add(Kind::Simultaneous { list });
672 if let Kind::Grouped {
673 simultaneous: held, ..
674 } = &mut self.tree.nodes[grouped].kind
675 {
676 *held = Some(simultaneous);
677 }
678 self.tree.set_parent(simultaneous, grouped);
679 self.new_context(list);
680 let mut composites = Vec::new();
681 for part in parts {
682 composites.push(self.prefix_from_stream(part, None, None, false)?);
683 }
684 self.restore_context();
685 for composite in &composites {
686 self.tree.adopt(list, *composite);
687 }
688 if let Kind::MusicList { contents } = &mut self.tree.nodes[list].kind {
689 *contents = composites.into_iter().map(Item::Node).collect();
690 }
691 Ok(grouped)
692 }
693
694 fn prefix_from_stream(
696 &mut self,
697 stream: &'a Stream,
698 context_type: Option<&str>,
699 kind: Option<&'static str>,
700 start_staff: bool,
701 ) -> Result<usize> {
702 let id_text = stream_id(stream, &mut self.unnamed);
703 let (context, id) = match context_type {
704 Some(context) => (
705 context.to_string(),
706 Some(self.tree.add(Kind::OptionalId(letters_only(&id_text)))),
707 ),
708 None if is_part(stream) => (
709 "Staff".to_string(),
710 Some(self.tree.add(Kind::OptionalId(letters_only(&id_text)))),
711 ),
712 None => ("Voice".to_string(), None),
713 };
714 let mut modifications = Vec::new();
715 if crate::makenotation::beams_made(stream) {
716 modifications.push("\\autoBeamOff ".to_string());
717 }
718 let lines = staff_lines(stream);
719 if lines != 5 {
720 modifications.push(format!("\\override StaffSymbol.line-count = #{lines}"));
721 }
722 let lyrics = self.new_lyrics(stream, &letters_only(&id_text));
723 let sequential = self.sequential_from_stream(stream, start_staff)?;
724 let grouped = self.tree.add(Kind::Grouped {
725 simultaneous: None,
726 sequential: Some(sequential),
727 });
728 let composite = self.tree.add(Kind::Composite {
729 prefix: None,
730 grouped: Some(grouped),
731 lyrics: Some(lyrics),
732 });
733 let music = self.tree.add(Kind::Music { composite });
734 let modification = (!modifications.is_empty()).then(|| {
735 self.tree.add(Kind::ContextModification {
736 list: modifications,
737 })
738 });
739 Ok(self.tree.add(Kind::Prefix {
740 kind: kind.unwrap_or("new"),
741 context,
742 id,
743 modification,
744 music,
745 fraction: String::new(),
746 }))
747 }
748
749 fn new_lyrics(&mut self, stream: &Stream, id: &str) -> usize {
751 let aligned = format!("#{}", quote(id));
752 let mut lists = Vec::new();
753 for (_, lyrics) in stream_lyrics(stream) {
754 let mut items = vec![Item::Node(self.tree.add(Kind::PropertySet {
755 property: "alignBelowContext".to_string(),
756 value: aligned.clone(),
757 }))];
758 self.in_word = false;
759 for lyric in lyrics {
760 let text = self.lyric_text(lyric);
761 items.push(Item::Node(self.tree.add(Kind::LyricElement(text))));
762 }
763 self.in_word = false;
764 let list = self.tree.add(Kind::MusicList { contents: items });
765 let sequential = self.tree.add(Kind::Sequential {
766 list,
767 start_staff: false,
768 });
769 lists.push(self.tree.add(Kind::Grouped {
770 simultaneous: None,
771 sequential: Some(sequential),
772 }));
773 }
774 self.tree.add(Kind::NewLyrics { lists })
775 }
776
777 fn lyric_text(&mut self, lyric: Option<&crate::notation::Lyric>) -> String {
779 let mut in_word = self.in_word;
780 let text = match lyric {
781 None => " _ ".to_string(),
782 Some(lyric) if lyric.text().is_empty() => " _ ".to_string(),
783 Some(lyric) => {
784 let mut text = format!("\"{}\"", lyric.text());
785 match lyric.syllabic() {
786 Syllabic::End => {
787 text += "__";
788 in_word = false;
789 }
790 Syllabic::Begin | Syllabic::Middle => {
791 text += " --";
792 in_word = true;
793 }
794 _ => {}
795 }
796 text
797 }
798 };
799 self.in_word = in_word;
800 text
801 }
802
803 fn sequential_from_stream(&mut self, stream: &'a Stream, start_staff: bool) -> Result<usize> {
805 let list = self.tree.add(Kind::MusicList {
806 contents: Vec::new(),
807 });
808 let sequential = self.tree.add(Kind::Sequential { list, start_staff });
809 self.new_context(list);
810 self.append_stream(stream)?;
811 if let Some(bar) = self.close_measure() {
812 self.tree.contents_mut(list)?.push(Item::Node(bar));
814 }
815 self.restore_context();
816 Ok(sequential)
817 }
818
819 fn append_stream(&mut self, stream: &'a Stream) -> Result<()> {
822 let events = stream.events();
823 let mut start = 0;
824 while start < events.len() {
825 let offset = events[start].offset();
826 let mut end = start + 1;
827 while end < events.len() && events[end].offset() == offset {
828 end += 1;
829 }
830 let group = &events[start..end];
831 if group.len() == 1 {
832 self.append_element(group[0].element())?;
833 } else {
834 let voices: Vec<&'a Stream> = group
835 .iter()
836 .filter_map(|event| match event.element() {
837 StreamElement::Stream(inner) if inner.kind() == StreamKind::Voice => {
838 Some(&**inner)
839 }
840 _ => None,
841 })
842 .collect();
843 if !voices.is_empty() {
844 let grouped = self.tree.add(Kind::Grouped {
845 simultaneous: None,
846 sequential: None,
847 });
848 let list = self.tree.add(Kind::MusicList {
849 contents: Vec::new(),
850 });
851 let simultaneous = self.tree.add(Kind::Simultaneous { list });
852 if let Kind::Grouped {
853 simultaneous: held, ..
854 } = &mut self.tree.nodes[grouped].kind
855 {
856 *held = Some(simultaneous);
857 }
858 self.tree.set_parent(simultaneous, grouped);
859 let mut composites = Vec::new();
860 for voice in voices {
861 composites.push(self.prefix_from_stream(voice, None, None, false)?);
862 }
863 for composite in &composites {
864 self.tree.adopt(list, *composite);
865 }
866 if let Kind::MusicList { contents } = &mut self.tree.nodes[list].kind {
867 *contents = composites.into_iter().map(Item::Node).collect();
868 }
869 self.append_to_context(grouped)?;
870 }
871 for event in group {
872 if !matches!(event.element(), StreamElement::Stream(inner) if inner.kind() == StreamKind::Voice)
873 {
874 self.append_element(event.element())?;
875 }
876 }
877 }
878 start = end;
879 }
880 Ok(())
881 }
882
883 fn append_element(&mut self, element: &'a StreamElement) -> Result<()> {
885 if !matches!(element, StreamElement::Stream(_))
886 && let Some(duration) = element.duration()
887 && duration.is_complex()
888 {
889 for (_, piece) in crate::makenotation::element_at_durations(element, duration)? {
890 self.append_owned(piece)?;
891 }
892 return Ok(());
893 }
894 match element {
895 StreamElement::Stream(measure) if measure.kind() == StreamKind::Measure => {
896 if let Some(bar) = self.close_measure() {
897 self.append_to_context(bar)?;
898 }
899 if let Some(pad) = self.padding(measure)? {
900 self.append_to_context(pad)?;
901 }
902 self.append_stream(measure)?;
903 self.current_measure = Some(measure);
904 }
905 StreamElement::Stream(inner) => {
906 let prefix = self.prefix_from_stream(inner, None, None, false)?;
907 self.append_to_context(prefix)?;
908 }
909 other => self.append_owned(other.clone())?,
910 }
911 Ok(())
912 }
913
914 fn append_owned(&mut self, element: StreamElement) -> Result<()> {
916 match &element {
917 StreamElement::Note(note) => self.append_note(note)?,
918 StreamElement::Rest(rest) => self.append_rest(rest)?,
919 StreamElement::Chord(chord) => self.append_chord(chord, chord.duration())?,
920 StreamElement::ChordSymbol(symbol) => {
922 let chord = Chord::new(symbol.pitches()?)?;
923 self.append_chord(&chord, Some(symbol.duration()))?;
924 }
925 StreamElement::Clef(clef) => {
926 let scheme = self.embedded(clef_scheme(clef));
927 self.append_to_context(scheme)?;
928 }
929 StreamElement::KeySignature(signature) => {
930 let scheme = self.embedded(key_scheme(&signature.as_key("major")));
931 self.append_to_context(scheme)?;
932 }
933 StreamElement::Key(key) => {
934 let scheme = self.embedded(key_scheme(key));
935 self.append_to_context(scheme)?;
936 }
937 StreamElement::TimeSignature(meter) => {
938 self.meter = Some(meter.clone());
939 let scheme = self.embedded(time_scheme(meter));
940 self.append_to_context(scheme)?;
941 }
942 StreamElement::MetronomeMark(mark) => {
943 if let Some(scheme) = tempo_scheme(mark)? {
944 let scheme = self.embedded(scheme);
945 self.append_to_context(scheme)?;
946 }
947 }
948 StreamElement::Layout(crate::layout::Layout::System(_)) => {
949 let scheme = self.embedded("\\break");
950 self.append_to_context(scheme)?;
951 }
952 StreamElement::Layout(crate::layout::Layout::Page(_)) => {
953 let scheme = self.embedded("\\pageBreak");
954 self.append_to_context(scheme)?;
955 }
956 _ => {}
957 }
958 Ok(())
959 }
960
961 fn tuplet_start(&mut self, duration: Option<&Duration>) -> Result<()> {
964 let Some(first) = duration.and_then(|duration| duration.tuplets().into_iter().next())
965 else {
966 return Ok(());
967 };
968 if first.tuplet_type() != Some(crate::duration::TupletType::Start) {
969 return Ok(());
970 }
971 let fraction = format!("{}/{}", first.tuplet_actual().0, first.tuplet_normal().0);
972 let list = self.tree.add(Kind::MusicList {
973 contents: Vec::new(),
974 });
975 let sequential = self.tree.add(Kind::Sequential {
976 list,
977 start_staff: false,
978 });
979 let prefix = self.tree.add(Kind::Prefix {
980 kind: "tuplet",
981 context: String::new(),
982 id: None,
983 modification: None,
984 music: sequential,
985 fraction,
986 });
987 self.append_to_context(prefix)?;
988 self.new_context(list);
989 Ok(())
990 }
991
992 fn tuplet_stop(&mut self, duration: Option<&Duration>) {
994 if duration
995 .and_then(|duration| duration.tuplets().into_iter().next())
996 .is_some_and(|first| first.tuplet_type() == Some(crate::duration::TupletType::Stop))
997 {
998 self.restore_context();
999 }
1000 }
1001
1002 fn beam_code(&mut self, beams: &Beams) -> Result<()> {
1004 let (mut left, mut right) = (0, 0);
1005 for beam in beams.beams() {
1006 match beam.beam_type().map_or("", |kind| kind.as_str()) {
1007 "start" => right += 1,
1008 "continue" => {
1009 right += 1;
1010 left += 1;
1011 }
1012 "stop" => left += 1,
1013 "partial" => {
1014 if beam
1015 .direction()
1016 .is_some_and(|direction| direction.as_str() == "left")
1017 {
1018 left += 1;
1019 } else {
1020 right += 1;
1021 }
1022 }
1023 _ => {}
1024 }
1025 }
1026 if left > 0 {
1027 let scheme = self.embedded(format!("\\set stemLeftBeamCount = #{left}"));
1028 self.append_to_context(scheme)?;
1029 }
1030 if right > 0 {
1031 let scheme = self.embedded(format!("\\set stemRightBeamCount = #{right}"));
1032 self.append_to_context(scheme)?;
1033 }
1034 Ok(())
1035 }
1036
1037 fn stem_code(&mut self, direction: StemDirection) -> Result<()> {
1039 let said = match direction {
1040 StemDirection::Up => "UP",
1041 StemDirection::Down => "DOWN",
1042 _ => return Ok(()),
1043 };
1044 let scheme = self.embedded(format!("\\once \\override Stem.direction = #{said} "));
1045 self.append_to_context(scheme)
1046 }
1047
1048 fn append_note(&mut self, note: &Note) -> Result<()> {
1050 let duration = note.duration();
1051 if duration.is_some_and(Duration::is_grace) {
1052 return Ok(());
1053 }
1054 self.tuplet_start(duration)?;
1055 self.beam_code(note.beams())?;
1056 self.stem_code(note.stem_direction())?;
1057 let mut parts = Vec::new();
1058 if let Some(color) = note.color().filter(|color| !color.is_empty())
1059 && !note.hidden()
1060 {
1061 parts.push(Item::Text(format!(
1062 "\\override NoteHead.color = \"{color}\"\n"
1063 )));
1064 parts.push(Item::Text(format!("\\override Stem.color = \"{color}\"\n")));
1065 }
1066 if note.hidden() {
1067 parts.push(Item::Text("s ".to_string()));
1068 } else {
1069 parts.push(Item::Node(self.pitch_node(note.pitch())?));
1070 parts.extend(accidental_marks(note.pitch()));
1071 }
1072 let length = self.duration_node(&element_duration(duration))?;
1073 parts.push(Item::Node(length));
1074 if let Some(first) = note.beams().beams().first() {
1075 match first.beam_type().map_or("", |kind| kind.as_str()) {
1076 "start" => parts.push(Item::Text("[ ".to_string())),
1077 "stop" => parts.push(Item::Text("] ".to_string())),
1078 _ => {}
1079 }
1080 }
1081 let post = post_events(note.tie().map(|tie| tie.tie_type()), note.expressions());
1082 self.push_simple(parts, post)?;
1083 self.tuplet_stop(duration);
1084 Ok(())
1085 }
1086
1087 fn append_rest(&mut self, rest: &Rest) -> Result<()> {
1089 let duration = Some(rest.duration());
1090 if rest.duration().is_grace() {
1091 return Ok(());
1092 }
1093 self.tuplet_start(duration)?;
1094 let mut parts = vec![Item::Text(
1095 if rest.hidden() { "s " } else { "r " }.to_string(),
1096 )];
1097 let length = self.duration_node(rest.duration())?;
1098 parts.push(Item::Node(length));
1099 let post = post_events(rest.tie().map(|tie| tie.tie_type()), rest.expressions());
1100 self.push_simple(parts, post)?;
1101 self.tuplet_stop(duration);
1102 Ok(())
1103 }
1104
1105 fn push_simple(&mut self, parts: Vec<Item>, post: Vec<String>) -> Result<()> {
1106 let simple = self.tree.add(Kind::SimpleElement { parts });
1107 let chord = self.tree.add(Kind::EventChord {
1108 simple: Some(simple),
1109 post,
1110 note_chord: None,
1111 });
1112 let music = self.tree.add(Kind::SimpleMusic { chord });
1113 self.append_to_context(music)
1114 }
1115
1116 fn append_chord(&mut self, chord: &Chord, duration: Option<&Duration>) -> Result<()> {
1119 self.tuplet_start(duration)?;
1120 self.beam_code(chord.beams())?;
1121 self.stem_code(chord.stem_direction())?;
1122 self.beam_code(chord.beams())?;
1124 self.stem_code(chord.stem_direction())?;
1125 let mut elements = Vec::new();
1126 for note in chord.notes() {
1127 let mut parts = vec![Item::Node(self.pitch_node(note.pitch())?)];
1128 parts.extend(accidental_marks(note.pitch()));
1129 elements.push(self.tree.add(Kind::ChordBodyElement { parts }));
1130 }
1131 let body = self.tree.add(Kind::ChordBody { elements });
1132 let length = self.duration_node(&element_duration(duration))?;
1133 let post = post_events(chord.tie().map(|tie| tie.tie_type()), chord.expressions());
1134 let note_chord = self.tree.add(Kind::NoteChordElement {
1135 body,
1136 duration: length,
1137 post,
1138 });
1139 let event = self.tree.add(Kind::EventChord {
1140 simple: None,
1141 post: Vec::new(),
1142 note_chord: Some(note_chord),
1143 });
1144 let music = self.tree.add(Kind::SimpleMusic { chord: event });
1145 self.append_to_context(music)?;
1146 self.tuplet_stop(duration);
1147 Ok(())
1148 }
1149
1150 fn pitch_node(&mut self, pitch: &Pitch) -> Result<usize> {
1152 let quotes = octave_marks(pitch)?;
1153 Ok(self.tree.add(Kind::Pitch {
1154 name: base_name(pitch),
1155 quotes,
1156 }))
1157 }
1158
1159 fn duration_node(&mut self, duration: &Duration) -> Result<usize> {
1161 let (number, dots) = duration_number(duration)?;
1162 Ok(self.tree.add(Kind::Duration { number, dots }))
1163 }
1164
1165 fn close_measure(&mut self) -> Option<usize> {
1167 let measure = self.current_measure.take()?;
1168 let mut bar = match measure.right_barline() {
1169 None => format!("\\bar {}", quote("|")),
1170 Some(barline) => format!("\\bar {}", quote(barline_sign(barline.bar_type()))),
1171 };
1172 bar += &format!(" %{{ end measure {} %}} ", measure.number());
1173 Some(self.embedded(bar))
1174 }
1175
1176 fn padding(&mut self, measure: &Stream) -> Result<Option<usize>> {
1178 let padding = measure.padding_left();
1179 if padding == 0.0 {
1180 return Ok(None);
1181 }
1182 let bar =
1183 meter_of(measure, self.meter.clone()).map_or(4.0, |meter| meter.bar_quarter_length());
1184 let remaining = bar - padding;
1185 if remaining <= 0.0 {
1186 return Err(lily_error("your first pickup measure is non-existent!"));
1187 }
1188 Ok(Some(self.embedded(format!(
1189 "\\partial 32*{} ",
1190 (remaining * 8.0) as i64
1191 ))))
1192 }
1193}
1194
1195fn meter_of(measure: &Stream, fallback: Option<TimeSignature>) -> Option<TimeSignature> {
1197 measure
1198 .events()
1199 .iter()
1200 .find_map(|event| match event.element() {
1201 StreamElement::TimeSignature(meter) => Some(meter.clone()),
1202 _ => None,
1203 })
1204 .or(fallback)
1205}
1206
1207fn element_duration(duration: Option<&Duration>) -> Duration {
1209 duration.cloned().unwrap_or_else(Duration::quarter)
1210}
1211
1212fn duration_number(duration: &Duration) -> Result<(String, u32)> {
1214 let name = crate::braille::basic::duration_type(duration);
1215 let number: FloatType = match name.as_str() {
1216 "duplex-maxima" => 0.0625,
1217 "maxima" => 0.125,
1218 "longa" => 0.25,
1219 "breve" => 0.5,
1220 "zero" => 0.0,
1221 "inexpressible" | "complex" => {
1222 return Err(lily_error(format!(
1223 "DurationException for durationObject {}: Could not determine durationNumber from {name}",
1224 duration_repr(duration)
1225 )));
1226 }
1227 "whole" => 1.0,
1228 "half" => 2.0,
1229 "quarter" => 4.0,
1230 "eighth" => 8.0,
1231 other => other
1232 .trim_end_matches(|c: char| c.is_ascii_alphabetic())
1233 .parse()
1234 .unwrap_or(0.0),
1235 };
1236 if number == 0.0 {
1237 return Err(lily_error(format!(
1238 "Cannot translate an object of zero duration {}",
1239 duration_repr(duration)
1240 )));
1241 }
1242 let written = if number < 1.0 {
1243 if number == 0.5 {
1244 "\\breve".to_string()
1245 } else if number == 0.25 {
1246 "\\longa".to_string()
1247 } else {
1248 return Err(lily_error("Cannot support durations longer than longa"));
1249 }
1250 } else {
1251 format!("{}", number as i64)
1252 };
1253 Ok((written, crate::braille::basic::duration_dots(duration)))
1254}
1255
1256fn duration_repr(duration: &Duration) -> String {
1257 format!(
1258 "<music21.duration.Duration {}>",
1259 crate::braille::basic::quarter_length_repr(duration.quarter_length())
1260 )
1261}
1262
1263fn base_name(pitch: &Pitch) -> String {
1265 let mut name = pitch.step().as_char().to_ascii_lowercase().to_string();
1266 if let Some(accidental) = pitch.written_accidental() {
1267 name += match accidental.name() {
1268 "double-sharp" => "isis",
1269 "double-flat" => "eses",
1270 "one-and-a-half-sharp" => "isih",
1271 "one-and-a-half-flat" => "eseh",
1272 "sharp" => "is",
1273 "flat" => "es",
1274 "half-sharp" => "ih",
1275 "half-flat" => "eh",
1276 _ => "",
1277 };
1278 }
1279 name
1280}
1281
1282fn octave_marks(pitch: &Pitch) -> Result<String> {
1284 let Some(octave) = pitch.octave() else {
1285 return Err(lily_error(
1286 "a pitch with no octave has no place on the staff",
1287 ));
1288 };
1289 Ok(if octave < 3 {
1290 ",".repeat((3 - octave) as usize)
1291 } else {
1292 "'".repeat((octave - 3) as usize)
1293 })
1294}
1295
1296fn accidental_marks(pitch: &Pitch) -> Vec<Item> {
1298 let mut marks = Vec::new();
1299 if let Some(accidental) = pitch.written_accidental() {
1300 if accidental.display_type() == "always" {
1301 marks.push(Item::Text("! ".to_string()));
1302 }
1303 if accidental.display_style() == "parentheses" {
1304 marks.push(Item::Text("? ".to_string()));
1305 }
1306 }
1307 marks
1308}
1309
1310fn post_events(tie: Option<TieType>, expressions: &[Expression]) -> Vec<String> {
1312 let mut post = Vec::new();
1313 if tie.is_some_and(|tie| tie != TieType::Stop) {
1314 post.push("~ ".to_string());
1315 }
1316 for expression in expressions {
1317 if matches!(expression, Expression::Fermata(_)) {
1318 post.push("\\fermata ".to_string());
1319 }
1320 }
1321 post
1322}
1323
1324fn clef_scheme(clef: &Clef) -> String {
1326 let kind = clef.kind();
1327 let classes: Vec<&str> = std::iter::once(kind.class_name())
1328 .chain(kind.parents().iter().copied())
1329 .collect();
1330 let name = [
1331 ("Treble8vbClef", "treble_8"),
1332 ("TrebleClef", "treble"),
1333 ("BassClef", "bass"),
1334 ("AltoClef", "alto"),
1335 ("TenorClef", "tenor"),
1336 ("SopranoClef", "soprano"),
1337 ("PercussionClef", "percussion"),
1338 ]
1339 .iter()
1340 .find(|(class, _)| classes.contains(class))
1341 .map_or("", |(_, name)| name);
1342 format!("\\clef {}\n", quote(name))
1343}
1344
1345fn key_scheme(key: &Key) -> String {
1347 format!("\\key {} \\{} \n", base_name(&key.tonic()), key.mode())
1348}
1349
1350fn time_scheme(meter: &TimeSignature) -> String {
1352 format!("\\time {}\n", meter.ratio_string())
1353}
1354
1355fn tempo_scheme(mark: &MetronomeMark) -> Result<Option<String>> {
1357 let Some(number) = mark.number() else {
1358 return Ok(None);
1359 };
1360 let (steno, dots) = duration_number(mark.referent())?;
1361 let steno = format!("{steno}{} ", ".".repeat(dots as usize));
1362 Ok(Some(format!(
1363 "\\tempo {steno} = {}\n",
1364 if number.fract() == 0.0 {
1365 format!("{}", number as i64)
1366 } else {
1367 crate::statistics::python_repr(number)
1368 }
1369 )))
1370}
1371
1372fn barline_sign(bar: BarlineType) -> &'static str {
1374 match bar {
1375 BarlineType::Regular => "|",
1376 BarlineType::Dotted => ";",
1377 BarlineType::Dashed => "!",
1378 BarlineType::Heavy => ".",
1379 BarlineType::Double => "||",
1380 BarlineType::Final => "|.",
1381 BarlineType::HeavyLight => ".|",
1382 BarlineType::HeavyHeavy => "..",
1383 BarlineType::Tick => "'",
1384 BarlineType::Short => ",",
1385 BarlineType::None => "",
1386 }
1387}
1388
1389fn staff_lines(stream: &Stream) -> i64 {
1391 for (offset, element) in stream.recurse() {
1392 if offset > 0.0 {
1393 break;
1394 }
1395 if let StreamElement::Layout(crate::layout::Layout::Staff(staff)) = element
1396 && let Some(lines) = staff.staff_lines
1397 {
1398 return lines;
1399 }
1400 }
1401 5
1402}
1403
1404fn stream_lyrics(stream: &Stream) -> Vec<(i64, Vec<Option<&crate::notation::Lyric>>)> {
1407 let mut verses: Vec<(i64, Vec<Option<&crate::notation::Lyric>>)> = Vec::new();
1408 let mut count = 0usize;
1409 let mut notes: Vec<&StreamElement> = Vec::new();
1410 for event in stream.events() {
1411 match event.element() {
1412 StreamElement::Stream(measure) if measure.kind() == StreamKind::Measure => {
1413 notes.extend(
1414 measure
1415 .events()
1416 .iter()
1417 .map(|event| event.element())
1418 .filter(|element| is_not_rest(element)),
1419 );
1420 }
1421 element if is_not_rest(element) => notes.push(element),
1422 _ => {}
1423 }
1424 }
1425 for element in notes {
1426 let tie = match element {
1427 StreamElement::Note(note) => note.tie().map(|tie| tie.tie_type()),
1428 StreamElement::Chord(chord) => chord.tie().map(|tie| tie.tie_type()),
1429 _ => None,
1430 };
1431 if tie.is_some_and(|tie| tie != TieType::Start) {
1432 continue;
1433 }
1434 let lyrics: &[crate::notation::Lyric] = match element {
1435 StreamElement::Note(note) => note.lyrics(),
1436 StreamElement::Chord(chord) => chord.lyrics(),
1437 _ => &[],
1438 };
1439 let mut said: Vec<i64> = Vec::new();
1440 for lyric in lyrics {
1441 let number = lyric.number() as i64;
1442 match verses.iter_mut().find(|(known, _)| *known == number) {
1443 Some((_, list)) => list.push(Some(lyric)),
1444 None => {
1445 let mut list = vec![None; count];
1446 list.push(Some(lyric));
1447 verses.push((number, list));
1448 }
1449 }
1450 said.push(number);
1451 }
1452 for (number, list) in &mut verses {
1453 if !said.contains(number) {
1454 list.push(None);
1455 }
1456 }
1457 count += 1;
1458 }
1459 verses.sort_by_key(|(number, _)| *number);
1460 verses
1461}
1462
1463fn is_not_rest(element: &StreamElement) -> bool {
1465 matches!(
1466 element,
1467 StreamElement::Note(_)
1468 | StreamElement::Chord(_)
1469 | StreamElement::Unpitched(_)
1470 | StreamElement::PercussionChord(_)
1471 | StreamElement::ChordSymbol(_)
1472 )
1473}
1474
1475pub fn to_lilypond(stream: &Stream, options: &LilyOptions) -> Result<String> {
1490 let wrapped;
1491 let score = if stream.kind() == StreamKind::Score {
1492 stream
1493 } else {
1494 let mut score = Stream::with_kind(StreamKind::Score);
1495 if matches!(stream.kind(), StreamKind::Measure | StreamKind::Voice) {
1496 let mut part = Stream::with_kind(StreamKind::Part);
1497 part.insert(0.0, StreamElement::Stream(Box::new(stream.clone())));
1498 score.insert(0.0, StreamElement::Stream(Box::new(part)));
1499 } else {
1500 score.insert(0.0, StreamElement::Stream(Box::new(stream.clone())));
1501 }
1502 wrapped = score;
1503 &wrapped
1504 };
1505 let mut converter = Converter::new();
1506 converter.load_score(score, options)?;
1507 let written = converter.tree.str_of(converter.top);
1508 Ok(collapse_blank_lines(&written).trim().to_string())
1509}
1510
1511fn collapse_blank_lines(text: &str) -> String {
1514 let chars: Vec<char> = text.chars().collect();
1515 let mut out = String::with_capacity(text.len());
1516 let mut index = 0;
1517 while index < chars.len() {
1518 if !chars[index].is_whitespace() {
1519 out.push(chars[index]);
1520 index += 1;
1521 continue;
1522 }
1523 let mut end = index;
1524 while end < chars.len() && chars[end].is_whitespace() {
1525 end += 1;
1526 }
1527 let newlines: Vec<usize> = (index..end).filter(|&at| chars[at] == '\n').collect();
1528 if let [_, .., last] = newlines[..] {
1529 out.push('\n');
1530 out.extend(&chars[last + 1..end]);
1531 } else {
1532 out.extend(&chars[index..end]);
1533 }
1534 index = end;
1535 }
1536 out
1537}
1538
1539#[cfg(test)]
1540mod tests {
1541 use super::*;
1542 use crate::tinynotation::from_tiny_notation;
1543
1544 #[test]
1545 fn blank_lines_are_closed_up_as_python_closes_them() {
1546 assert_eq!(collapse_blank_lines("a \n \n b"), "a\n b");
1547 assert_eq!(collapse_blank_lines("a\n\n\nb"), "a\nb");
1548 assert_eq!(collapse_blank_lines("a\nb"), "a\nb");
1549 }
1550
1551 #[test]
1552 fn ids_are_made_of_letters_as_music21_makes_them() {
1553 assert_eq!(letters_only("P1"), "Px");
1554 assert_eq!(letters_only("Soprano"), "Soprano");
1555 }
1556
1557 #[test]
1558 fn a_line_is_written_as_music21_writes_it() {
1559 let line = from_tiny_notation("4/4 c4 d8 e f#4. g8 a2 r2").unwrap();
1560 let text = to_lilypond(&line, &LilyOptions::default()).unwrap();
1561 assert!(text.contains("\\time 4/4"));
1562 assert!(text.contains("fis' 4."));
1563 assert!(text.contains("\\bar \"|.\" %{ end measure 2 %}"));
1564 }
1565}