1use std::collections::{BTreeMap, VecDeque};
12
13use super::basic::{
14 self, NoteContext, Transcription, barline_to_braille, chord_to_braille, clef_to_braille,
15 dynamic_to_braille, note_to_braille, number_to_braille, rest_to_braille,
16 text_expression_to_braille, transcribe_heading, transcribe_signatures, yield_dots,
17};
18use super::lookup::{self, symbol};
19use super::text::{BrailleKeyboard, BrailleText};
20use crate::{
21 defaults::{FloatType, IntegerType},
22 duration::Duration,
23 error::{Error, Result},
24 makenotation::op_frac,
25 meter::TimeSignature,
26 note::Note,
27 pitch::Pitch,
28 rest::Rest,
29 stream::{Stream, StreamElement, StreamKind},
30};
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub enum Affinity {
36 Signature = 3,
38 TempoText = 4,
40 MetronomeMark = 5,
42 LongTextExpression = 6,
44 Inaccord = 7,
46 SplitNoteGroupingA = 8,
48 NoteGrouping = 9,
50 SplitNoteGroupingB = 10,
52}
53
54impl Affinity {
55 pub fn name(self) -> &'static str {
57 match self {
58 Self::Signature => "Signature Grouping",
59 Self::TempoText => "Tempo Text Grouping",
60 Self::MetronomeMark => "Metronome Mark Grouping",
61 Self::LongTextExpression => "Long Text Expression Grouping",
62 Self::Inaccord => "Inaccord Grouping",
63 Self::NoteGrouping => "Note Grouping",
64 Self::SplitNoteGroupingA => "Split Note Grouping A",
65 Self::SplitNoteGroupingB => "Split Note Grouping B",
66 }
67 }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
72pub enum Hand {
73 Left,
75 Right,
77}
78
79#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
82pub struct SegmentKey {
83 pub measure: IntegerType,
85 pub ordinal: usize,
87 pub affinity: Affinity,
89 pub hand: Option<Hand>,
91}
92
93#[derive(Clone, Debug)]
97pub struct BrailleElement {
98 pub element: StreamElement,
100 pub offset: FloatType,
102 pub affinity: Affinity,
104 pub sort_order: i32,
107 pub english: Option<Vec<String>>,
109 pub context: NoteContext,
111 pub outgoing_key: Option<Option<IntegerType>>,
114 pub leaf: usize,
117 pub voice_english: Vec<Option<Vec<String>>>,
120}
121
122#[derive(Clone, Debug)]
125pub struct BrailleElementGrouping {
126 pub elements: Vec<BrailleElement>,
128 pub key_signature: Option<IntegerType>,
130 pub time_signature: TimeSignature,
132 pub descending_chords: Option<bool>,
135 pub show_clef_signs: bool,
137 pub upper_first_in_note_fingering: bool,
139 pub with_hyphen: bool,
141 pub num_repeats: usize,
143}
144
145impl Default for BrailleElementGrouping {
146 fn default() -> Self {
147 Self {
148 elements: Vec::new(),
149 key_signature: Some(0),
150 time_signature: TimeSignature::new(4, 4).expect("4/4 is a meter"),
151 descending_chords: Some(true),
152 show_clef_signs: false,
153 upper_first_in_note_fingering: true,
154 with_hyphen: false,
155 num_repeats: 0,
156 }
157 }
158}
159
160pub(crate) fn element_repr(element: &StreamElement) -> String {
163 match element {
164 StreamElement::Note(note) => format!("<music21.note.Note {}>", note.pitch().name()),
165 StreamElement::Rest(rest) => {
166 let duration = rest.duration();
167 let name = duration.full_name().to_lowercase();
168 let text = if name.chars().count() < 15 {
169 name.replace(' ', "-")
170 } else {
171 let length = duration.quarter_length();
172 if length.fract() == 0.0 {
173 format!("{}ql", length as i64)
174 } else {
175 format!("{}ql", crate::statistics::python_repr(length))
176 }
177 };
178 format!("<music21.note.Rest {text}>")
179 }
180 StreamElement::Chord(chord) => {
181 let names: Vec<String> = chord
182 .pitches()
183 .iter()
184 .map(Pitch::name_with_octave)
185 .collect();
186 format!("<music21.chord.Chord {}>", names.join(" "))
187 }
188 StreamElement::Clef(clef) => format!("<music21.clef.{}>", clef.kind().class_name()),
189 StreamElement::Barline(barline) => {
190 format!("<music21.bar.Barline type={}>", barline.bar_type().as_str())
191 }
192 StreamElement::Dynamic(dynamic) => {
193 format!("<music21.dynamics.Dynamic {}>", dynamic.value())
194 }
195 StreamElement::TextExpression(text) => {
196 let content = text.content();
197 let shown = if content.chars().count() >= 13 {
198 format!("{}...", content.chars().take(10).collect::<String>())
199 } else {
200 content.to_string()
201 };
202 format!(
203 "<music21.expressions.TextExpression {}>",
204 python_str_repr(&shown)
205 )
206 }
207 StreamElement::TempoText(tempo) => {
208 format!(
209 "<music21.tempo.TempoText {}>",
210 python_str_repr(tempo.text())
211 )
212 }
213 StreamElement::KeySignature(signature) => match signature.sharps() {
214 Some(sharps) => format!(
215 "<music21.key.KeySignature of {}>",
216 sharps_description(sharps)
217 ),
218 None => "<music21.key.KeySignature of pitches: []>".to_string(),
219 },
220 StreamElement::Key(key) => {
221 let tonic = music21_pitch_name(key.tonic_pitch());
223 let tonic = if key.mode() == "minor" {
224 tonic.to_lowercase()
225 } else {
226 tonic
227 };
228 format!("{tonic} {}", key.mode())
229 }
230 StreamElement::MetronomeMark(mark) => {
231 let mut sounding = "";
232 let mut number = mark.number();
233 if number.is_none() && mark.number_sounding().is_some() {
234 sounding = " (playback only)";
235 number = mark.number_sounding();
236 }
237 let number = number.map_or_else(|| "None".to_string(), python_number);
238 let referent = mark.referent().full_name();
239 match mark.text() {
240 Some(text) => {
241 format!("<music21.tempo.MetronomeMark {text} {referent}={number}{sounding}>")
242 }
243 None => format!("<music21.tempo.MetronomeMark {referent}={number}{sounding}>"),
244 }
245 }
246 StreamElement::Unpitched(unpitched) => match unpitched
247 .stored_instrument()
248 .and_then(|instrument| instrument.name())
249 {
250 Some(name) => format!("<music21.note.Unpitched {}>", python_str_repr(name)),
251 None => "<music21.note.Unpitched>".to_string(),
252 },
253 StreamElement::PercussionChord(chord) => {
254 let members: Vec<String> = chord
255 .members()
256 .iter()
257 .map(|member| match member {
258 crate::percussion::PercussionNote::Note(note) => {
259 note.pitch().name_with_octave()
260 }
261 crate::percussion::PercussionNote::Unpitched(unpitched) => match unpitched
262 .stored_instrument()
263 .and_then(|instrument| instrument.name())
264 {
265 Some(name) => name.to_string(),
266 None => format!("unpitched[{}]", unpitched.display_name()),
267 },
268 })
269 .collect();
270 format!(
271 "<music21.percussion.PercussionChord [{}]>",
272 members.join(" ")
273 )
274 }
275 StreamElement::TimeSignature(meter) => format!(
276 "<music21.meter.TimeSignature {}/{}>",
277 meter.numerator(),
278 meter.denominator()
279 ),
280 StreamElement::Stream(stream) => match stream.id() {
281 Some(id) => format!("<music21.stream.{:?} {id}>", stream.kind()),
282 None => format!("<music21.stream.{:?}>", stream.kind()),
283 },
284 other => format!("<music21.{other:?}>"),
285 }
286}
287
288fn music21_pitch_name(pitch: &Pitch) -> String {
290 let modifier = match pitch
291 .written_accidental()
292 .map(|accidental| accidental.name())
293 {
294 Some("flat") => "-",
295 Some("double-flat") => "--",
296 Some("sharp") => "#",
297 Some("double-sharp") => "##",
298 _ => "",
299 };
300 format!("{}{modifier}", pitch.step().as_char())
301}
302
303fn sharps_description(sharps: IntegerType) -> String {
305 match sharps {
306 0 => "no sharps or flats".to_string(),
307 1 => "1 sharp".to_string(),
308 -1 => "1 flat".to_string(),
309 sharps if sharps > 0 => format!("{sharps} sharps"),
310 sharps => format!("{} flats", -sharps),
311 }
312}
313
314fn python_number(number: FloatType) -> String {
316 if number.fract() == 0.0 {
317 format!("{}", number as i64)
318 } else {
319 crate::statistics::python_repr(number)
320 }
321}
322
323fn python_str_repr(text: &str) -> String {
326 let quote = if text.contains('\'') && !text.contains('"') {
327 '"'
328 } else {
329 '\''
330 };
331 let mut out = String::new();
332 out.push(quote);
333 for character in text.chars() {
334 match character {
335 '\\' => out.push_str("\\\\"),
336 '\n' => out.push_str("\\n"),
337 '\r' => out.push_str("\\r"),
338 '\t' => out.push_str("\\t"),
339 c if c == quote => {
340 out.push('\\');
341 out.push(c);
342 }
343 c if (c as u32) < 0x20 || c as u32 == 0x7f => {
344 out.push_str(&format!("\\x{:02x}", c as u32));
345 }
346 c => out.push(c),
347 }
348 }
349 out.push(quote);
350 out
351}
352
353fn affinity_of(element: &StreamElement) -> Option<(Affinity, i32)> {
356 Some(match element {
357 StreamElement::Note(_) | StreamElement::Rest(_) | StreamElement::Chord(_) => {
358 (Affinity::NoteGrouping, 10)
359 }
360 StreamElement::ChordSymbol(_) => (Affinity::NoteGrouping, 10),
361 StreamElement::Dynamic(_) => (Affinity::NoteGrouping, 9),
362 StreamElement::Clef(_) => (Affinity::NoteGrouping, 7),
363 StreamElement::Barline(_) => (Affinity::SplitNoteGroupingB, 0),
364 StreamElement::KeySignature(_) | StreamElement::Key(_) => (Affinity::Signature, 1),
365 StreamElement::TimeSignature(_) => (Affinity::Signature, 2),
366 StreamElement::TempoText(_) => (Affinity::TempoText, 3),
367 StreamElement::MetronomeMark(_) => (Affinity::MetronomeMark, 4),
368 StreamElement::Stream(inner) if inner.kind() == StreamKind::Voice => {
369 (Affinity::Inaccord, 10)
370 }
371 StreamElement::TextExpression(text) => {
372 if text.content().split_whitespace().count() > 1 {
373 (Affinity::LongTextExpression, 8)
374 } else {
375 (Affinity::NoteGrouping, 8)
376 }
377 }
378 _ => return None,
379 })
380}
381
382fn is_general_note(element: &StreamElement) -> bool {
384 matches!(
385 element,
386 StreamElement::Note(_)
387 | StreamElement::Rest(_)
388 | StreamElement::Chord(_)
389 | StreamElement::ChordSymbol(_)
390 | StreamElement::Unpitched(_)
391 | StreamElement::PercussionChord(_)
392 )
393}
394
395fn is_not_rest(element: &StreamElement) -> bool {
397 matches!(
398 element,
399 StreamElement::Note(_)
400 | StreamElement::Chord(_)
401 | StreamElement::ChordSymbol(_)
402 | StreamElement::Unpitched(_)
403 | StreamElement::PercussionChord(_)
404 )
405}
406
407fn walk_of(stream: &Stream, leaf_base: usize) -> Vec<(FloatType, StreamElement, usize)> {
411 let mut out = Vec::new();
412 if let Some(barline) = stream.left_barline() {
413 out.push((0.0, StreamElement::Barline(barline.clone()), leaf_base));
414 }
415 let mut leaf = leaf_base;
416 for event in stream.events() {
417 let element = event.element().clone();
418 let leaves = match &element {
419 StreamElement::Stream(inner) => inner.leaves().len(),
420 _ => 1,
421 };
422 out.push((event.offset(), element, leaf));
423 leaf += leaves;
424 }
425 if let Some(barline) = stream.right_barline() {
426 out.push((
427 stream.end_offset(),
428 StreamElement::Barline(barline.clone()),
429 leaf,
430 ));
431 }
432 out
433}
434
435pub fn extract_braille_elements(
439 stream: &Stream,
440 leaf_base: usize,
441 contexts: &[NoteContext],
442) -> BrailleElementGrouping {
443 let mut grouping = BrailleElementGrouping::default();
444 let mut last_clef: Option<(FloatType, StreamElement, usize)> = None;
445 for (offset, element, leaf) in walk_of(stream, leaf_base) {
446 if matches!(element, StreamElement::Clef(_)) {
447 last_clef = Some((offset, element, leaf));
448 continue;
449 }
450 if let StreamElement::Barline(barline) = &element
451 && barline.bar_type().as_str() == "regular"
452 {
453 continue;
454 }
455 let Some((affinity, sort_order)) = affinity_of(&element) else {
456 continue;
457 };
458 let attaches = is_general_note(&element)
459 || matches!(&element, StreamElement::Stream(inner) if inner.kind() == StreamKind::Voice);
460 if attaches && let Some((clef_offset, clef, clef_leaf)) = last_clef.take() {
461 grouping.elements.push(BrailleElement {
462 english: Some(vec![element_repr(&clef)]),
463 element: clef,
464 offset: clef_offset,
465 affinity,
466 sort_order: 7,
467 context: NoteContext::default(),
468 outgoing_key: None,
469 leaf: clef_leaf,
470 voice_english: Vec::new(),
471 });
472 }
473 grouping.elements.push(BrailleElement {
474 english: Some(vec![element_repr(&element)]),
475 context: contexts.get(leaf).copied().unwrap_or_default(),
476 element,
477 offset,
478 affinity,
479 sort_order,
480 outgoing_key: None,
481 leaf,
482 voice_english: Vec::new(),
483 });
484 }
485 let order = |left: &BrailleElement, right: &BrailleElement| {
486 left.offset
487 .total_cmp(&right.offset)
488 .then(left.sort_order.cmp(&right.sort_order))
489 };
490 grouping.elements.sort_by(order);
491 let count = grouping.elements.len();
492 if count >= 2
493 && matches!(
494 grouping.elements[count - 1].element,
495 StreamElement::Dynamic(_)
496 )
497 && matches!(
498 grouping.elements[count - 2].element,
499 StreamElement::Barline(_)
500 )
501 {
502 grouping.elements[count - 1].sort_order = -1;
503 grouping.elements.sort_by(order);
504 }
505 grouping
506}
507
508fn first_beam_type(element: &StreamElement) -> Option<String> {
511 let beams = match element {
512 StreamElement::Note(note) => note.beams(),
513 StreamElement::Chord(chord) => chord.beams(),
514 _ => return None,
515 };
516 beams
517 .beams()
518 .iter()
519 .find(|beam| beam.number() == Some(1))
520 .and_then(|beam| beam.beam_type())
521 .map(|kind| kind.as_str().to_string())
522}
523
524fn whole_beat(meter: Option<&TimeSignature>, offset: FloatType) -> Option<i64> {
526 meter
527 .and_then(|meter| meter.beat_at_offset(offset).ok())
528 .map(i64::from)
529}
530
531pub fn prepare_beamed_notes(
536 items: &[(FloatType, StreamElement)],
537 meter: Option<&TimeSignature>,
538) -> Vec<(bool, bool)> {
539 let mut flags = vec![(false, false); items.len()];
540 let notes: Vec<usize> = (0..items.len())
542 .filter(|&i| is_not_rest(&items[i].1))
543 .collect();
544 let general: Vec<usize> = (0..items.len())
545 .filter(|&i| is_general_note(&items[i].1))
546 .collect();
547 let starts: Vec<usize> = notes
548 .iter()
549 .copied()
550 .filter(|&i| first_beam_type(&items[i].1).as_deref() == Some("start"))
551 .collect();
552 let stops: Vec<usize> = notes
553 .iter()
554 .copied()
555 .filter(|&i| first_beam_type(&items[i].1).as_deref() == Some("stop"))
556 .collect();
557 if starts.len() != stops.len() {
558 return flags;
559 }
560 let length = |i: usize| items[i].1.quarter_length();
561 let same_length = |a: FloatType, b: FloatType| (a - b).abs() < 1e-9;
564 for (start, stop) in starts.into_iter().zip(stops) {
565 if length(start) == 0.5 {
566 continue;
567 }
568 let (Some(start_index), Some(stop_index)) = (
569 general.iter().position(|&i| i == start),
570 general.iter().position(|&i| i == stop),
571 ) else {
572 continue;
573 };
574 if (stop_index as i64) - (start_index as i64) + 1 < 3 {
575 continue;
576 }
577 let mut same = (start_index + 1..=stop_index).all(|index| {
578 let i = general[index];
579 same_length(length(i), length(start)) && !matches!(items[i].1, StreamElement::Rest(_))
580 });
581 if let Some(&after) = general.get(stop_index + 1)
582 && matches!(items[after].1, StreamElement::Rest(_))
583 && whole_beat(meter, items[after].0) == whole_beat(meter, items[stop].0)
584 {
585 same = false;
586 }
587 if !same {
588 continue;
589 }
590 if let Some(&after) = general.get(stop_index + 1)
591 && length(after) == 0.5
592 {
593 continue;
594 }
595 flags[start].0 = true;
596 if start_index >= 1 {
597 let before = general[start_index - 1];
598 if matches!(items[before].1, StreamElement::Rest(_))
599 && whole_beat(meter, items[before].0) == whole_beat(meter, items[start].0)
600 && same_length(length(before), length(start))
601 {
602 flags[start].1 = true;
603 }
604 } else {
605 let before = general[general.len() - 1];
608 if matches!(items[before].1, StreamElement::Rest(_))
609 && whole_beat(meter, items[before].0) == whole_beat(meter, items[start].0)
610 && same_length(length(before), length(start))
611 {
612 flags[start].1 = true;
613 }
614 }
615 for index in start_index + 1..=stop_index {
616 flags[general[index]].1 = true;
617 }
618 }
619 flags
620}
621
622#[derive(Clone, Copy, Debug, PartialEq, Eq)]
624pub struct SegmentOptions {
625 pub cancel_outgoing_key_sig: bool,
627 pub descending_chords: Option<bool>,
630 pub dummy_rest_length: Option<usize>,
632 pub max_line_length: usize,
634 pub show_clef_signs: bool,
636 pub show_first_measure_number: bool,
638 pub show_hand: Option<Hand>,
640 pub show_heading: bool,
642 pub show_long_slurs_and_ties_together: Option<bool>,
644 pub show_short_slurs_and_ties_together: bool,
646 pub slur_long_phrase_with_brackets: bool,
649 pub suppress_octave_marks: bool,
651 pub upper_first_in_note_fingering: bool,
653}
654
655impl Default for SegmentOptions {
656 fn default() -> Self {
657 Self {
658 cancel_outgoing_key_sig: true,
659 descending_chords: None,
660 dummy_rest_length: None,
661 max_line_length: 40,
662 show_clef_signs: false,
663 show_first_measure_number: true,
664 show_hand: None,
665 show_heading: true,
666 show_long_slurs_and_ties_together: None,
667 show_short_slurs_and_ties_together: false,
668 slur_long_phrase_with_brackets: true,
669 suppress_octave_marks: false,
670 upper_first_in_note_fingering: true,
671 }
672 }
673}
674
675const MAX_NOTES_FOR_SHORT_SLUR: usize = 4;
678
679const MAX_ELEMENTS_IN_SEGMENT: usize = 48;
682
683pub fn prepare_slurred_notes(part: &Stream, options: &SegmentOptions) -> Vec<NoteContext> {
687 let leaves = part.leaves();
688 let mut contexts = vec![NoteContext::default(); leaves.len()];
689 if part.spanners().is_empty() {
690 return contexts;
691 }
692 let show_long = options
693 .show_long_slurs_and_ties_together
694 .unwrap_or(options.slur_long_phrase_with_brackets);
695 let mut notes: Vec<(FloatType, i32, bool, usize)> = leaves
697 .iter()
698 .enumerate()
699 .filter(|(_, (_, element))| is_not_rest(element))
700 .map(|(leaf, (offset, element))| {
701 let grace = element.duration().is_some_and(Duration::is_grace);
702 (*offset, element.class_sort_order(), !grace, leaf)
703 })
704 .collect();
705 notes.sort_by(|left, right| {
706 left.0
707 .total_cmp(&right.0)
708 .then(left.1.cmp(&right.1))
709 .then(left.2.cmp(&right.2))
710 });
711 let notes: Vec<usize> = notes.into_iter().map(|(_, _, _, leaf)| leaf).collect();
712 let tie_of = |leaf: usize| -> Option<crate::notation::TieType> {
713 match leaves[leaf].1 {
714 StreamElement::Note(note) => note.tie().map(|tie| tie.tie_type()),
715 StreamElement::Chord(chord) => chord.tie().map(|tie| tie.tie_type()),
716 _ => None,
717 }
718 };
719 let at = |index: i64| -> usize {
720 let count = notes.len() as i64;
721 notes[(((index % count) + count) % count) as usize]
722 };
723 for spanner in part.spanners() {
724 if spanner.kind() != crate::spanner::SpannerKind::Slur {
725 continue;
726 }
727 let spanned = spanner.spanned();
728 let (Some(Some(first)), Some(Some(last))) = (spanned.first(), spanned.last()) else {
729 continue;
730 };
731 let (Some(begin), Some(end)) = (
732 notes.iter().position(|leaf| leaf == first),
733 notes.iter().position(|leaf| leaf == last),
734 ) else {
735 continue;
736 };
737 let (mut begin, mut end) = (begin as i64, end as i64);
738 let delta = (end - begin).unsigned_abs() as usize + 1;
739 let short = delta <= MAX_NOTES_FOR_SHORT_SLUR;
740 if (short && !options.show_short_slurs_and_ties_together) || (!short && !show_long) {
741 if tie_of(at(begin)) == Some(crate::notation::TieType::Start) {
742 begin += 1;
743 }
744 if tie_of(at(end)) == Some(crate::notation::TieType::Stop) {
745 end -= 1;
746 }
747 }
748 if short {
749 for index in begin..end {
750 contexts[at(index)].short_slur = true;
751 }
752 } else if options.slur_long_phrase_with_brackets {
753 contexts[at(begin)].begin_long_bracket_slur = true;
754 contexts[at(end)].end_long_bracket_slur = true;
755 } else {
756 contexts[at(begin + 1)].begin_long_double_slur = true;
757 contexts[at(end - 1)].end_long_double_slur = true;
758 }
759 }
760 contexts
761}
762
763#[derive(Clone, Debug)]
766pub struct BrailleSegment {
767 pub groupings: BTreeMap<SegmentKey, BrailleElementGrouping>,
769 pub options: SegmentOptions,
771 pub descending_chords: Option<bool>,
774 pub end_hyphen: bool,
776 pub begins_mid_measure: bool,
778 pub contexts: Vec<NoteContext>,
780}
781
782impl BrailleSegment {
783 fn new(options: SegmentOptions, contexts: &[NoteContext]) -> Self {
784 Self {
785 groupings: BTreeMap::new(),
786 options,
787 descending_chords: options.descending_chords,
788 end_hyphen: false,
789 begins_mid_measure: false,
790 contexts: contexts.to_vec(),
791 }
792 }
793}
794
795fn sharps_of(element: &StreamElement) -> Option<Option<IntegerType>> {
797 match element {
798 StreamElement::Key(key) => Some(Some(key.sharps())),
799 StreamElement::KeySignature(signature) => Some(signature.sharps()),
800 _ => None,
801 }
802}
803
804fn measures_of(part: &Stream) -> Vec<(&Stream, usize)> {
806 let mut out = Vec::new();
807 let mut leaf = 0;
808 for event in part.events() {
809 match event.element() {
810 StreamElement::Stream(inner) => {
811 if inner.kind() == StreamKind::Measure {
812 out.push((&**inner, leaf));
813 }
814 leaf += inner.leaves().len();
815 }
816 _ => leaf += 1,
817 }
818 }
819 out
820}
821
822fn meters_of(part: &Stream) -> Vec<Option<TimeSignature>> {
826 let loose: Vec<(FloatType, TimeSignature)> = part
827 .events()
828 .iter()
829 .filter_map(|event| match event.element() {
830 StreamElement::TimeSignature(meter) => Some((event.offset(), meter.clone())),
831 _ => None,
832 })
833 .collect();
834 let mut stated: Option<TimeSignature> = None;
835 let mut out = Vec::new();
836 for event in part.events() {
837 let StreamElement::Stream(measure) = event.element() else {
838 continue;
839 };
840 if measure.kind() != StreamKind::Measure {
841 continue;
842 }
843 for inner in measure.events() {
844 if let StreamElement::TimeSignature(meter) = inner.element()
845 && inner.offset() <= 0.0
846 {
847 stated = Some(meter.clone());
848 }
849 }
850 let meter = stated.clone().or_else(|| {
851 loose
852 .iter()
853 .rev()
854 .find(|(at, _)| *at <= event.offset())
855 .map(|(_, meter)| meter.clone())
856 });
857 out.push(meter);
858 }
859 out
860}
861
862pub fn get_raw_segments(
866 part: &Stream,
867 hand: Option<Hand>,
868 contexts: &[NoteContext],
869 options: SegmentOptions,
870) -> Vec<BrailleSegment> {
871 let mut segments = Vec::new();
872 let mut contexts = contexts.to_vec();
873 let mut current = BrailleSegment::new(options, &contexts);
874 let mut in_current = 0;
875 let mut start_new = false;
876 let meters = meters_of(part);
877 for (index, (measure, leaf_base)) in measures_of(part).into_iter().enumerate() {
878 let walk = walk_of(measure, leaf_base);
879 let items: Vec<(FloatType, StreamElement)> = walk
880 .iter()
881 .filter(|(_, element, _)| !matches!(element, StreamElement::Stream(_)))
882 .map(|(offset, element, _)| (*offset, element.clone()))
883 .collect();
884 let leaves: Vec<usize> = walk
885 .iter()
886 .filter(|(_, element, _)| !matches!(element, StreamElement::Stream(_)))
887 .map(|(_, _, leaf)| *leaf)
888 .collect();
889 let flags = prepare_beamed_notes(&items, meters.get(index).and_then(Option::as_ref));
890 for ((item, leaf), (start, continued)) in items.iter().zip(&leaves).zip(flags) {
891 if is_not_rest(&item.1) && *leaf < contexts.len() {
892 contexts[*leaf].beam_start = start;
893 contexts[*leaf].beam_continue = continued;
894 }
895 }
896 let elements = extract_braille_elements(measure, leaf_base, &contexts);
897 let number = IntegerType::from(measure.number());
898 let mut ordinal = 0;
899 let mut previous: Option<Affinity> = None;
900 for element in elements.elements {
901 if start_new {
902 if element.offset != 0.0 {
903 current.end_hyphen = true;
904 }
905 segments.push(std::mem::replace(
906 &mut current,
907 BrailleSegment::new(options, &contexts),
908 ));
909 if element.offset != 0.0 {
910 current.begins_mid_measure = true;
911 }
912 in_current = 0;
913 if previous.is_some() {
914 ordinal += 1;
915 }
916 start_new = false;
917 }
918 if let StreamElement::Barline(barline) = &element.element
919 && in_current > MAX_ELEMENTS_IN_SEGMENT
920 && matches!(barline.bar_type().as_str(), "double" | "final")
921 {
922 start_new = true;
923 }
924 if previous.is_some_and(|previous| element.affinity < previous) {
925 ordinal += 1;
926 }
927 let affinity = match element.affinity {
928 Affinity::SplitNoteGroupingA => Affinity::Inaccord,
929 Affinity::SplitNoteGroupingB => Affinity::NoteGrouping,
930 other => other,
931 };
932 let key = SegmentKey {
933 measure: number,
934 ordinal,
935 affinity,
936 hand,
937 };
938 previous = Some(element.affinity);
939 current
940 .groupings
941 .entry(key)
942 .or_default()
943 .elements
944 .push(element);
945 in_current += 1;
946 }
947 }
948 current.contexts = contexts;
949 segments.push(current);
950 segments
951}
952
953fn elements_equal(left: &BrailleElement, right: &BrailleElement) -> bool {
955 super::equality::equal(&left.element, &right.element)
956}
957
958fn groupings_identical(left: &[BrailleElement], right: &[BrailleElement]) -> bool {
961 left.len() == right.len()
962 && left
963 .iter()
964 .zip(right)
965 .all(|(left, right)| elements_equal(left, right))
966}
967
968impl BrailleSegment {
969 pub fn add_grouping_attributes(&mut self) {
973 let mut current_key: Option<IntegerType> = Some(0);
974 let mut current_meter = TimeSignature::new(4, 4).expect("4/4 is a meter");
975 let keys: Vec<SegmentKey> = self.groupings.keys().copied().collect();
976 let mut previous: Option<SegmentKey> = None;
977 for key in keys {
978 if let Some(previous_key) = previous {
979 if key.ordinal >= 1 {
980 self.groupings
981 .get_mut(&previous_key)
982 .expect("a grouping")
983 .with_hyphen = true;
984 }
985 if previous_key.ordinal == 0
986 && previous_key.affinity == Affinity::NoteGrouping
987 && key.ordinal == 0
988 && key.affinity == Affinity::NoteGrouping
989 {
990 let previous_elements = &self.groupings[&previous_key].elements;
991 let these = &self.groupings[&key].elements;
992 let repeated = if previous_elements
993 .first()
994 .is_some_and(|first| matches!(first.element, StreamElement::Clef(_)))
995 {
996 groupings_identical(&previous_elements[1..], these)
997 } else {
998 groupings_identical(previous_elements, these)
999 };
1000 if repeated {
1001 self.groupings
1002 .get_mut(&previous_key)
1003 .expect("a grouping")
1004 .num_repeats += 1;
1005 self.groupings.remove(&key);
1006 continue;
1007 }
1008 }
1009 }
1010 let descending = &mut self.descending_chords;
1011 let grouping = self.groupings.get_mut(&key).expect("a grouping");
1012 if key.affinity == Affinity::Signature {
1013 for element in &mut grouping.elements {
1014 if let StreamElement::TimeSignature(meter) = &element.element {
1015 current_meter = meter.clone();
1016 } else if let Some(sharps) = sharps_of(&element.element) {
1017 element.outgoing_key = Some(current_key);
1018 current_key = sharps;
1019 }
1020 }
1021 } else if matches!(key.affinity, Affinity::Inaccord | Affinity::NoteGrouping) {
1022 if let Some(StreamElement::Clef(clef)) =
1023 grouping.elements.first().map(|e| &e.element)
1024 {
1025 match clef.kind().class_name() {
1026 "TrebleClef" | "Treble8vbClef" | "Treble8vaClef" | "AltoClef" => {
1027 *descending = Some(true);
1028 }
1029 "BassClef" | "Bass8vbClef" | "Bass8vaClef" | "TenorClef" => {
1030 *descending = Some(false);
1031 }
1032 _ => {}
1033 }
1034 }
1035 let general: Vec<usize> = (0..grouping.elements.len())
1036 .filter(|&i| is_general_note(&grouping.elements[i].element))
1037 .collect();
1038 if let [only] = general.as_slice()
1039 && let StreamElement::Rest(rest) = &mut grouping.elements[*only].element
1040 {
1041 rest.set_full_measure(Some(true));
1042 }
1043 }
1044 grouping.key_signature = current_key;
1045 grouping.time_signature = current_meter.clone();
1046 grouping.descending_chords = *descending;
1047 grouping.show_clef_signs = self.options.show_clef_signs;
1048 grouping.upper_first_in_note_fingering = self.options.upper_first_in_note_fingering;
1049 previous = Some(key);
1050 }
1051 if self.end_hyphen
1052 && let Some(last) = previous
1053 {
1054 self.groupings
1055 .get_mut(&last)
1056 .expect("a grouping")
1057 .with_hyphen = true;
1058 }
1059 }
1060
1061 pub fn fix_articulations(&mut self) {
1065 let mut runs: Vec<Vec<(SegmentKey, usize)>> = Vec::new();
1068 let mut in_run = false;
1069 for (key, grouping) in &self.groupings {
1070 if key.affinity != Affinity::NoteGrouping {
1071 in_run = false;
1072 continue;
1073 }
1074 if !in_run {
1075 runs.push(Vec::new());
1076 in_run = true;
1077 }
1078 let run = runs.last_mut().expect("a run");
1079 for (index, element) in grouping.elements.iter().enumerate() {
1080 if matches!(element.element, StreamElement::Note(_)) {
1081 run.push((*key, index));
1082 }
1083 }
1084 }
1085 for notes in runs {
1086 for start in 0..notes.len() {
1087 let mut article = 0;
1088 while let Some(articulation) = self
1089 .note(notes[start])
1090 .articulations()
1091 .get(article)
1092 .cloned()
1093 {
1094 article += 1;
1095 if articulation.is_a("TechnicalIndication") && !articulation.is_a("Bowing") {
1096 continue;
1097 }
1098 self.fix_one_articulation(&articulation, ¬es, start);
1099 }
1100 }
1101 }
1102 }
1103
1104 fn note(&self, (key, index): (SegmentKey, usize)) -> &Note {
1105 match &self.groupings[&key].elements[index].element {
1106 StreamElement::Note(note) => note,
1107 _ => unreachable!("only notes are gathered"),
1108 }
1109 }
1110
1111 fn element_mut(&mut self, (key, index): (SegmentKey, usize)) -> &mut BrailleElement {
1112 &mut self.groupings.get_mut(&key).expect("a grouping").elements[index]
1113 }
1114
1115 fn note_mut(&mut self, place: (SegmentKey, usize)) -> &mut Note {
1116 match &mut self.element_mut(place).element {
1117 StreamElement::Note(note) => note,
1118 _ => unreachable!("only notes are gathered"),
1119 }
1120 }
1121
1122 fn fix_one_articulation(
1123 &mut self,
1124 articulation: &crate::articulations::Articulation,
1125 notes: &[(SegmentKey, usize)],
1126 start: usize,
1127 ) {
1128 let name = articulation.name();
1129 let count = notes.len() as i64;
1130 let wrap = |index: i64| notes[(((index % count) + count) % count) as usize];
1131 if (articulation.is_a("Staccato") || articulation.is_a("Tenuto"))
1132 && let Some(tie) = self.note(notes[start]).tie().map(|tie| tie.tie_type())
1133 {
1134 if tie == crate::notation::TieType::Stop {
1135 let before = wrap(start as i64 - 1);
1136 self.note_mut(before).set_tie(None);
1137 self.element_mut(before).context.short_slur = true;
1138 } else {
1139 let after = wrap(start as i64 + 1);
1140 self.note_mut(after).set_tie(None);
1141 self.element_mut(notes[start]).context.short_slur = true;
1142 }
1143 self.note_mut(notes[start]).set_tie(None);
1144 }
1145 let mut sequential = 0;
1146 for &place in ¬es[start + 1..] {
1147 if self
1148 .note(place)
1149 .articulations()
1150 .iter()
1151 .any(|other| other.name() == name)
1152 {
1153 sequential += 1;
1154 continue;
1155 }
1156 break;
1157 }
1158 if sequential < 3 {
1159 return;
1160 }
1161 self.note_mut(notes[start])
1162 .articulations_mut()
1163 .push(articulation.clone());
1164 for &place in ¬es[start + 1..start + sequential] {
1165 let articulations = self.note_mut(place).articulations_mut();
1166 let mut index = 0;
1169 while index < articulations.len() {
1170 if articulations[index].name() == name {
1171 drop(articulations.remove(index));
1172 }
1173 index += 1;
1174 }
1175 }
1176 }
1177}
1178
1179pub fn find_segments(
1182 part: &Stream,
1183 hand: Option<Hand>,
1184 options: SegmentOptions,
1185) -> Vec<BrailleSegment> {
1186 let contexts = prepare_slurred_notes(part, &options);
1187 let mut segments = get_raw_segments(part, hand, &contexts, options);
1188 for segment in &mut segments {
1189 segment.add_grouping_attributes();
1190 segment.fix_articulations();
1191 }
1192 segments
1193}
1194
1195#[derive(Clone, Debug)]
1198pub struct NoteGroupingTranscriber {
1199 pub show_leading_octave: bool,
1201 previous_note: Option<Pitch>,
1202 previous_element: Option<usize>,
1203 trans: Vec<String>,
1204}
1205
1206impl Default for NoteGroupingTranscriber {
1207 fn default() -> Self {
1208 Self {
1209 show_leading_octave: true,
1210 previous_note: None,
1211 previous_element: None,
1212 trans: Vec::new(),
1213 }
1214 }
1215}
1216
1217impl NoteGroupingTranscriber {
1218 pub fn transcribe_group(&mut self, grouping: &mut BrailleElementGrouping) -> Result<String> {
1226 self.previous_note = None;
1227 self.previous_element = None;
1228 self.trans = Vec::new();
1229 for index in 0..grouping.elements.len() {
1230 self.transcribe_one_element(grouping, index)?;
1231 }
1232 if grouping.with_hyphen {
1233 self.trans.push(symbol("music_hyphen"));
1234 }
1235 Ok(self.trans.concat())
1236 }
1237
1238 fn octave_shown(&self, pitch: &Pitch) -> bool {
1239 match &self.previous_note {
1240 None => self.show_leading_octave,
1241 Some(previous) => basic::show_octave_with_note(Some(previous), pitch),
1242 }
1243 }
1244
1245 fn transcribe_one_element(
1246 &mut self,
1247 grouping: &mut BrailleElementGrouping,
1248 index: usize,
1249 ) -> Result<()> {
1250 let descending = grouping.descending_chords.unwrap_or(false);
1251 let show_clef_signs = grouping.show_clef_signs;
1252 let upper_first = grouping.upper_first_in_note_fingering;
1253 let item = &mut grouping.elements[index];
1254 let written: Option<Transcription> = match &item.element {
1255 StreamElement::Note(note) => {
1256 let shown = self.octave_shown(note.pitch());
1257 let written = note_to_braille(note, shown, upper_first, item.context);
1258 self.previous_note = Some(note.pitch().clone());
1259 Some(written)
1260 }
1261 StreamElement::Rest(rest) => Some(rest_to_braille(rest)),
1262 StreamElement::Chord(chord) => {
1263 let mut pitches = chord.pitches();
1264 pitches.sort_by(|left, right| left.ps().total_cmp(&right.ps()));
1265 let chosen = if descending {
1266 pitches.last().cloned()
1267 } else {
1268 pitches.first().cloned()
1269 };
1270 chosen.map(|pitch| {
1271 let shown = self.octave_shown(&pitch);
1272 let written = chord_to_braille(chord, descending, shown);
1273 self.previous_note = Some(pitch);
1274 written
1275 })
1276 }
1277 StreamElement::ChordSymbol(symbol) => symbol.pitches().ok().and_then(|pitches| {
1278 let mut chord = crate::chord::Chord::new(pitches.as_slice()).ok()?;
1279 chord.set_duration(symbol.duration().clone());
1280 let mut sorted = pitches.clone();
1281 sorted.sort_by(|left, right| left.ps().total_cmp(&right.ps()));
1282 let chosen = if descending {
1283 sorted.last()
1284 } else {
1285 sorted.first()
1286 }
1287 .cloned()?;
1288 let shown = self.octave_shown(&chosen);
1289 let written = chord_to_braille(&chord, descending, shown);
1290 self.previous_note = Some(chosen);
1291 Some(written)
1292 }),
1293 StreamElement::Dynamic(dynamic) => {
1294 self.previous_note = None;
1295 self.show_leading_octave = true;
1296 Some(dynamic_to_braille(dynamic, true))
1297 }
1298 StreamElement::TextExpression(text) => {
1299 self.previous_note = None;
1300 self.show_leading_octave = true;
1301 Some(text_expression_to_braille(text, true))
1302 }
1303 StreamElement::Barline(barline) => Some(barline_to_braille(barline)),
1304 StreamElement::Clef(clef) => {
1305 if show_clef_signs {
1306 self.previous_note = None;
1307 self.show_leading_octave = true;
1308 Some(clef_to_braille(clef, false))
1309 } else {
1310 None
1311 }
1312 }
1313 _ => None,
1314 };
1315 if let Some(written) = written {
1316 item.english = Some(written.english);
1317 self.trans.push(written.braille);
1318 }
1319 self.optionally_add_dot_to_previous(grouping, index)?;
1320 self.previous_element = Some(index);
1321 Ok(())
1322 }
1323
1324 fn optionally_add_dot_to_previous(
1328 &mut self,
1329 grouping: &mut BrailleElementGrouping,
1330 index: usize,
1331 ) -> Result<()> {
1332 let Some(previous) = self.previous_element else {
1333 return Ok(());
1334 };
1335 let Some(last) = self.trans.last() else {
1336 return Ok(());
1337 };
1338 if matches!(
1339 grouping.elements[index].element,
1340 StreamElement::Dynamic(_) | StreamElement::TextExpression(_)
1341 ) {
1342 return Ok(());
1343 }
1344 let dotted = match &grouping.elements[previous].element {
1345 StreamElement::Dynamic(_) => true,
1346 StreamElement::Clef(_) => grouping.show_clef_signs,
1347 StreamElement::TextExpression(text) => match text.content().chars().last() {
1348 Some(character) => character != '.',
1349 None => return Err(Error::Notation("string index out of range".to_string())),
1350 },
1351 _ => false,
1352 };
1353 if !dotted {
1354 return Ok(());
1355 }
1356 let Some(first) = last.chars().next() else {
1357 return Ok(());
1358 };
1359 if let Some(dot) = yield_dots(first).into_iter().next() {
1360 let at = self.trans.len() - 1;
1361 self.trans.insert(at, dot.clone());
1362 grouping.elements[previous]
1363 .english
1364 .get_or_insert_with(Vec::new)
1365 .push(format!("Dot 3 {dot}"));
1366 }
1367 Ok(())
1368 }
1369}
1370
1371pub fn transcribe_note_grouping(
1378 grouping: &mut BrailleElementGrouping,
1379 show_leading_octave: bool,
1380) -> Result<String> {
1381 let mut transcriber = NoteGroupingTranscriber {
1382 show_leading_octave,
1383 ..NoteGroupingTranscriber::default()
1384 };
1385 transcriber.transcribe_group(grouping)
1386}
1387
1388pub fn split_note_grouping(
1397 grouping: &mut BrailleElementGrouping,
1398 beat_division_offset: usize,
1399) -> Result<Option<(BrailleElementGrouping, BrailleElementGrouping)>> {
1400 Ok(split_with_sources(grouping, beat_division_offset)?
1401 .map(|((first, _), (second, _))| (first, second)))
1402}
1403
1404type Half = (BrailleElementGrouping, Vec<usize>);
1406
1407type Rebeamed = (usize, StreamElement, (bool, bool));
1410
1411fn split_with_sources(
1413 grouping: &mut BrailleElementGrouping,
1414 beat_division_offset: usize,
1415) -> Result<Option<(Half, Half)>> {
1416 let meter = grouping.time_signature.clone();
1417 let mut offset = 0.0;
1418 if beat_division_offset != 0 {
1419 let divisions = meter.beat_division_durations()?;
1420 if beat_division_offset > divisions.len() {
1421 return Ok(None);
1424 }
1425 if let Some(duration) = divisions.get(divisions.len() - beat_division_offset) {
1426 offset = op_frac(offset + op_frac(duration.quarter_length()));
1427 }
1428 }
1429 let mut beats = meter.beat_sequence().clone();
1430 let (start_zero, mut end_zero) = match beats.partition_by_count(2, false) {
1431 Ok(()) => beats.level_span(0)[0],
1432 Err(_) => {
1433 beats.partition_by_count(3, false)?;
1434 let spans = beats.level_span(0);
1435 (spans[0].0, spans[spans.len() - 2].1)
1436 }
1437 };
1438 end_zero -= offset;
1439 let elements = &grouping.elements;
1440 let mut order: Vec<usize> = (0..elements.len()).collect();
1443 order.sort_by(|&a, &b| {
1444 elements[a]
1445 .offset
1446 .total_cmp(&elements[b].offset)
1447 .then(elements[a].sort_order.cmp(&elements[b].sort_order))
1448 });
1449 let mut left: Vec<usize> = Vec::new();
1450 let mut right: Vec<usize> = Vec::new();
1451 for &index in &order {
1452 let at = elements[index].offset;
1453 let barline = matches!(elements[index].element, StreamElement::Barline(_));
1454 if at >= start_zero && (at < end_zero || (at == end_zero && barline)) {
1455 left.push(index);
1456 } else {
1457 right.push(index);
1458 }
1459 }
1460 let highest = elements
1461 .iter()
1462 .map(|element| op_frac(element.offset + element.element.quarter_length()))
1463 .fold(0.0, FloatType::max);
1464 let mut offsets: Vec<FloatType> = elements.iter().map(|element| element.offset).collect();
1465 while let Some(position) = right
1468 .iter()
1469 .position(|&i| is_not_rest(&elements[i].element))
1470 {
1471 let index = right[position];
1472 let tied = match &elements[index].element {
1473 StreamElement::Note(note) => note.tie().is_some(),
1474 StreamElement::Chord(chord) => chord.tie().is_some(),
1475 _ => false,
1476 };
1477 if !tied {
1478 break;
1479 }
1480 offsets[index] = left
1481 .iter()
1482 .map(|&i| op_frac(offsets[i] + elements[i].element.quarter_length()))
1483 .fold(0.0, FloatType::max);
1484 left.push(index);
1485 right.remove(position);
1486 end_zero += elements[index].element.quarter_length();
1487 }
1488 let rebeam =
1491 |indices: &[usize], rest_at: FloatType, rest_length: FloatType| -> Result<Vec<Rebeamed>> {
1492 let rest_length = if rest_length == 0.0 { 1.0 } else { rest_length };
1496 let rest = Rest::new(Duration::new(rest_length.max(0.0))?);
1497 let mut placed: Vec<(FloatType, Option<usize>, StreamElement)> = indices
1500 .iter()
1501 .map(|&i| (offsets[i], Some(i), elements[i].element.clone()))
1502 .collect();
1503 placed.push((rest_at, None, StreamElement::Rest(rest)));
1504 placed.sort_by(|a, b| a.0.total_cmp(&b.0));
1505 let mut measure = Stream::with_kind(StreamKind::Measure);
1506 measure.insert(0.0, meter.clone());
1507 for (at, _, element) in &placed {
1508 measure.insert(*at, element.clone());
1509 }
1510 crate::makenotation::make_beams(&mut measure)?;
1511 let beamed: Vec<(FloatType, StreamElement)> = measure
1512 .events()
1513 .iter()
1514 .filter(|event| !matches!(event.element(), StreamElement::TimeSignature(_)))
1515 .map(|event| (event.offset(), event.element().clone()))
1516 .collect();
1517 let flags = prepare_beamed_notes(&beamed, Some(&meter));
1518 Ok(placed
1519 .iter()
1520 .zip(beamed)
1521 .zip(flags)
1522 .filter_map(|(((_, source, _), (_, element)), flags)| {
1523 source.map(|source| (source, element, flags))
1524 })
1525 .collect())
1526 };
1527 let left_beamed = rebeam(&left, end_zero, highest - end_zero)?;
1528 let right_beamed = rebeam(&right, 0.0, end_zero)?;
1529 for (index, element, (start, continued)) in left_beamed.iter().chain(&right_beamed) {
1532 let item = &mut grouping.elements[*index];
1533 item.offset = offsets[*index];
1534 item.element = element.clone();
1535 if is_not_rest(&item.element) {
1536 item.context.beam_start = *start;
1537 item.context.beam_continue = *continued;
1538 }
1539 }
1540 let half = |beamed: &[Rebeamed]| -> Half {
1541 let mut half = grouping.clone();
1542 half.elements = beamed
1543 .iter()
1544 .map(|(index, _, _)| grouping.elements[*index].clone())
1545 .collect();
1546 (half, beamed.iter().map(|(index, _, _)| *index).collect())
1547 };
1548 Ok(Some((half(&left_beamed), half(&right_beamed))))
1549}
1550
1551impl BrailleSegment {
1552 pub fn transcribe(&mut self) -> Result<String> {
1559 let hand = match self.options.show_hand {
1560 Some(Hand::Right) => Some("right"),
1561 Some(Hand::Left) => Some("left"),
1562 None => None,
1563 };
1564 let mut writer = SegmentWriter {
1565 text: BrailleText::new(self.options.max_line_length, hand)?,
1566 keys: self.groupings.keys().copied().collect(),
1567 current: None,
1568 previous: None,
1569 last_note: None,
1570 };
1571 if self.options.show_heading {
1572 writer.extract_heading(self)?;
1573 }
1574 if self.options.show_first_measure_number {
1575 writer.extract_measure_number(self)?;
1576 }
1577 if let Some(length) = self.options.dummy_rest_length {
1578 let dummy = lookup::rest("dummy").unwrap_or_default().repeat(length);
1579 writer.text.add_signatures(&dummy)?;
1580 }
1581 writer.previous = None;
1582 while let Some(key) = writer.keys.pop_front() {
1583 writer.current = Some(key);
1584 match key.affinity {
1585 Affinity::NoteGrouping => writer.extract_note_grouping(self)?,
1586 Affinity::Signature => writer.extract_signature_grouping(self)?,
1587 Affinity::LongTextExpression => writer.extract_long_expression_grouping(self)?,
1588 Affinity::Inaccord => writer.extract_inaccord_grouping(self)?,
1589 Affinity::TempoText => writer.extract_tempo_text_grouping(self)?,
1590 _ => {}
1591 }
1592 writer.previous = writer.current;
1593 }
1594 Ok(writer.text.render())
1595 }
1596
1597 pub fn english(&self) -> String {
1600 let mut entries = Vec::new();
1601 let mut previous: Option<SegmentKey> = None;
1602 for (key, grouping) in &self.groupings {
1603 if previous.is_some_and(|previous| previous.affinity == Affinity::SplitNoteGroupingA) {
1604 previous = Some(*key);
1605 continue;
1606 }
1607 entries.push(format!(
1608 "Measure {}, {} {}:\n{}\n===",
1609 key.measure,
1610 key.affinity.name(),
1611 key.ordinal + 1,
1612 grouping_english(grouping)
1613 ));
1614 previous = Some(*key);
1615 }
1616 [
1617 "---begin segment---".to_string(),
1618 "<music21.braille.segment BrailleSegment>".to_string(),
1619 entries.join("\n"),
1620 "---end segment---".to_string(),
1621 ]
1622 .join("\n")
1623 }
1624}
1625
1626fn grouping_english(grouping: &BrailleElementGrouping) -> String {
1629 let mut lines = Vec::new();
1630 let mut previous_was_voice = false;
1631 for element in &grouping.elements {
1632 if let StreamElement::Stream(voice) = &element.element
1633 && voice.kind() == StreamKind::Voice
1634 {
1635 if previous_was_voice {
1636 lines.push(format!("full inaccord {}", symbol("full_inaccord")));
1637 }
1638 for (index, inner) in voice.events().iter().enumerate() {
1639 lines.push(match element.voice_english.get(index) {
1640 Some(Some(english)) => english.join("\n"),
1641 _ => element_repr(inner.element()),
1642 });
1643 }
1644 previous_was_voice = true;
1645 } else {
1646 lines.push(match &element.english {
1647 Some(english) => english.join("\n"),
1648 None => element_repr(&element.element),
1649 });
1650 previous_was_voice = false;
1651 }
1652 }
1653 if grouping.num_repeats > 0 {
1654 lines.push(format!("** Grouping x {} **", grouping.num_repeats + 1));
1655 }
1656 if grouping.with_hyphen {
1657 lines.push(format!("music hyphen {}", symbol("music_hyphen")));
1658 }
1659 lines.join("\n")
1660}
1661
1662fn voice_english_of(
1665 voice: &Stream,
1666 leaf_base: usize,
1667 written: &BrailleElementGrouping,
1668) -> Vec<Option<Vec<String>>> {
1669 let mut leaf = leaf_base;
1670 voice
1671 .events()
1672 .iter()
1673 .map(|event| {
1674 let english = written
1675 .elements
1676 .iter()
1677 .find(|element| {
1678 element.leaf == leaf && !matches!(element.element, StreamElement::Stream(_))
1679 })
1680 .and_then(|element| element.english.clone());
1681 leaf += match event.element() {
1682 StreamElement::Stream(inner) => inner.leaves().len(),
1683 _ => 1,
1684 };
1685 english
1686 })
1687 .collect()
1688}
1689
1690fn heading_english(
1695 sharps: Option<Option<IntegerType>>,
1696 meter: Option<&TimeSignature>,
1697 tempo: Option<&crate::tempo::TempoText>,
1698 mark: Option<&crate::tempo::MetronomeMark>,
1699 line_length: usize,
1700) -> [Option<Vec<String>>; 4] {
1701 let mut out: [Option<Vec<String>>; 4] = [None, None, None, None];
1702 out[2] = tempo.map(|text| basic::tempo_to_braille(text, line_length).english);
1703 if sharps.is_some() || meter.is_some() || mark.is_some() {
1704 out[3] = mark.and_then(|found| {
1705 basic::metronome_mark_to_braille(found).map(|written| written.english)
1706 });
1707 let empty = meter.is_none() && sharps.is_none_or(|sharps| sharps == Some(0));
1708 if !empty {
1709 out[0] = sharps.map(|sharps| basic::key_sig_to_braille(sharps, None).english);
1710 out[1] = meter.map(|time| basic::time_sig_to_braille(time).english);
1711 }
1712 }
1713 out
1714}
1715
1716struct SegmentWriter {
1718 text: BrailleText,
1719 keys: VecDeque<SegmentKey>,
1720 current: Option<SegmentKey>,
1721 previous: Option<SegmentKey>,
1722 last_note: Option<Pitch>,
1723}
1724
1725impl SegmentWriter {
1726 fn extract_measure_number(&mut self, segment: &BrailleSegment) -> Result<()> {
1727 let first = self
1728 .keys
1729 .front()
1730 .copied()
1731 .or_else(|| segment.groupings.keys().next().copied());
1732 let Some(first) = first else {
1733 return Ok(());
1734 };
1735 let mut number = number_to_braille(&first.measure.to_string(), true, false)?;
1736 if segment.begins_mid_measure {
1737 number.push_str(&symbol("dot"));
1738 }
1739 self.text.add_measure_number(&number)
1740 }
1741
1742 fn extract_heading(&mut self, segment: &mut BrailleSegment) -> Result<()> {
1743 let mut sharps: Option<Option<IntegerType>> = None;
1744 let mut meter: Option<TimeSignature> = None;
1745 let mut tempo: Option<crate::tempo::TempoText> = None;
1746 let mut mark: Option<crate::tempo::MetronomeMark> = None;
1747 let from_writer = !self.keys.is_empty();
1750 let mut keys: VecDeque<SegmentKey> = if from_writer {
1751 std::mem::take(&mut self.keys)
1752 } else {
1753 segment.groupings.keys().copied().collect()
1754 };
1755 let mut key_place: Option<(SegmentKey, usize)> = None;
1756 let mut meter_place: Option<(SegmentKey, usize)> = None;
1757 let mut tempo_place: Option<(SegmentKey, usize)> = None;
1758 let mut mark_place: Option<(SegmentKey, usize)> = None;
1759 while let Some(&key) = keys.front() {
1760 if key.affinity > Affinity::MetronomeMark {
1761 break;
1762 }
1763 keys.pop_front();
1764 let grouping = &segment.groupings[&key];
1765 match key.affinity {
1766 Affinity::Signature => {
1767 if grouping.elements.len() >= 2 {
1768 sharps = sharps_of(&grouping.elements[0].element);
1769 key_place = sharps.map(|_| (key, 0));
1770 if let StreamElement::TimeSignature(time) = &grouping.elements[1].element {
1771 meter = Some(time.clone());
1772 meter_place = Some((key, 1));
1773 }
1774 } else if let Some(only) = grouping.elements.first() {
1775 if let Some(found) = sharps_of(&only.element) {
1776 sharps = Some(found);
1777 key_place = Some((key, 0));
1778 } else if let StreamElement::TimeSignature(time) = &only.element {
1779 meter = Some(time.clone());
1780 meter_place = Some((key, 0));
1781 }
1782 }
1783 }
1784 Affinity::TempoText => {
1785 if let Some(StreamElement::TempoText(text)) =
1786 grouping.elements.first().map(|e| &e.element)
1787 {
1788 tempo = Some(text.clone());
1789 tempo_place = Some((key, 0));
1790 }
1791 }
1792 Affinity::MetronomeMark => {
1793 if let Some(StreamElement::MetronomeMark(found)) =
1794 grouping.elements.first().map(|e| &e.element)
1795 {
1796 mark = Some(found.clone());
1797 mark_place = Some((key, 0));
1798 }
1799 }
1800 _ => {}
1801 }
1802 }
1803 if from_writer {
1804 self.keys = keys;
1805 }
1806 if sharps.is_none() && meter.is_none() && tempo.is_none() && mark.is_none() {
1807 return Ok(());
1808 }
1809 let line_length = self.text.line_length;
1810 let heading = transcribe_heading(
1811 sharps,
1812 meter.as_ref(),
1813 tempo.as_ref(),
1814 mark.as_ref(),
1815 line_length,
1816 )?;
1817 let mut set = |place: Option<(SegmentKey, usize)>, english: Option<Vec<String>>| {
1821 if let (Some((key, index)), Some(english)) = (place, english) {
1822 segment
1823 .groupings
1824 .get_mut(&key)
1825 .expect("a grouping")
1826 .elements[index]
1827 .english = Some(english);
1828 }
1829 };
1830 if let Some(text) = &tempo {
1831 set(
1832 tempo_place,
1833 Some(basic::tempo_to_braille(text, line_length).english),
1834 );
1835 }
1836 if sharps.is_some() || meter.is_some() || mark.is_some() {
1837 if let Some(found) = &mark {
1838 set(
1839 mark_place,
1840 basic::metronome_mark_to_braille(found).map(|written| written.english),
1841 );
1842 }
1843 let empty = meter.is_none() && sharps.is_none_or(|sharps| sharps == Some(0));
1844 if !empty {
1845 if let Some(sharps) = sharps {
1846 set(
1847 key_place,
1848 Some(basic::key_sig_to_braille(sharps, None).english),
1849 );
1850 }
1851 if let Some(time) = &meter {
1852 set(meter_place, Some(basic::time_sig_to_braille(time).english));
1853 }
1854 }
1855 }
1856 self.text.add_heading(&heading)
1857 }
1858
1859 fn extract_inaccord_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
1860 let key = self.current.expect("a grouping being written");
1861 let grouping = segment.groupings[&key].clone();
1862 let mut pending_clef: Option<BrailleElement> = None;
1863 let mut seen_voice = false;
1864 let mut written: Vec<(usize, Vec<Option<Vec<String>>>)> = Vec::new();
1865 for element in &grouping.elements {
1866 if matches!(element.element, StreamElement::Clef(_)) {
1867 pending_clef = Some(element.clone());
1868 continue;
1869 }
1870 let StreamElement::Stream(voice) = &element.element else {
1871 continue;
1872 };
1873 let mut inner = extract_braille_elements(voice, element.leaf, &segment.contexts);
1874 if let Some(clef) = pending_clef.take() {
1875 inner.elements.insert(0, clef);
1876 }
1877 inner.descending_chords = grouping.descending_chords;
1878 inner.show_clef_signs = grouping.show_clef_signs;
1879 inner.upper_first_in_note_fingering = grouping.upper_first_in_note_fingering;
1880 let mut braille = if seen_voice {
1881 symbol("full_inaccord")
1882 } else {
1883 String::new()
1884 };
1885 braille.push_str(&transcribe_note_grouping(&mut inner, true)?);
1886 written.push((element.leaf, voice_english_of(voice, element.leaf, &inner)));
1887 self.text.add_inaccord(&braille)?;
1888 seen_voice = true;
1889 }
1890 let stored = segment.groupings.get_mut(&key).expect("a grouping");
1891 for (leaf, english) in written {
1892 if let Some(element) = stored.elements.iter_mut().find(|element| {
1893 element.leaf == leaf && matches!(element.element, StreamElement::Stream(_))
1894 }) {
1895 element.voice_english = english;
1896 }
1897 }
1898 Ok(())
1899 }
1900
1901 fn extract_long_expression_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
1902 let key = self.current.expect("a grouping being written");
1903 let grouping = segment.groupings.get_mut(&key).expect("a grouping");
1904 if let Some(first) = grouping.elements.first_mut()
1905 && let StreamElement::TextExpression(text) = &first.element
1906 {
1907 let written = text_expression_to_braille(text, true);
1908 first.english = Some(written.english);
1909 self.text.add_long_expression(&written.braille)?;
1910 }
1911 Ok(())
1912 }
1913
1914 fn show_leading_octave_from(
1915 &mut self,
1916 grouping: &BrailleElementGrouping,
1917 suppress: bool,
1918 ) -> bool {
1919 if let (Some(previous), Some(current)) = (self.previous, self.current)
1920 && (previous.affinity != Affinity::NoteGrouping
1921 || current.affinity != Affinity::NoteGrouping
1922 || (current.measure == previous.measure
1923 && current.ordinal == previous.ordinal + 1
1924 && current.hand == previous.hand))
1925 {
1926 self.last_note = None;
1927 }
1928 if suppress {
1929 return false;
1930 }
1931 let notes: Vec<&Pitch> = grouping
1932 .elements
1933 .iter()
1934 .filter_map(|element| match &element.element {
1935 StreamElement::Note(note) => Some(note.pitch()),
1936 _ => None,
1937 })
1938 .collect();
1939 let mut show = true;
1940 if let (Some(first), Some(last)) = (notes.first(), notes.last()) {
1941 if let Some(previous) = &self.last_note {
1942 show = basic::show_octave_with_note(Some(previous), first);
1943 }
1944 self.last_note = Some((*last).clone());
1945 }
1946 show
1947 }
1948
1949 fn needs_split_to_fit(&mut self, braille: &str) -> bool {
1950 let quarter = self.text.line_length / 4;
1951 let left = self.text.line_length - self.text.current_line().text_location;
1952 left > quarter && braille.chars().count() > quarter
1953 }
1954
1955 fn extract_note_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
1956 let key = self.current.expect("a grouping being written");
1957 let suppress = segment.options.suppress_octave_marks;
1958 let mut grouping = segment.groupings[&key].clone();
1959 let show_leading = self.show_leading_octave_from(&grouping, suppress);
1960 let mut transcriber = NoteGroupingTranscriber {
1961 show_leading_octave: show_leading,
1962 ..NoteGroupingTranscriber::default()
1963 };
1964 let mut braille = transcriber.transcribe_group(&mut grouping)?;
1965 let add_space = self
1966 .text
1967 .optional_add_keyboard_symbols_and_dots(Some(&braille))?;
1968 if self.text.current_line().can_append(&braille, add_space) {
1969 self.text.current_line().append(&braille, add_space)?;
1970 } else {
1971 let mut split = false;
1972 if self.needs_split_to_fit(&braille)
1973 && let Some((first, second)) =
1974 self.split_and_transcribe(segment, &mut grouping, show_leading, add_space)?
1975 {
1976 split = true;
1977 self.text.current_line().append(&first, add_space)?;
1978 self.text.add_to_new_line(&second)?;
1979 }
1980 if !split {
1981 if !show_leading && !suppress {
1982 transcriber.show_leading_octave = true;
1983 braille = transcriber.transcribe_group(&mut grouping)?;
1984 }
1985 self.text.current_line().last_hyphen_to_space();
1986 self.text.add_to_new_line(&braille)?;
1987 }
1988 }
1989 segment.groupings.insert(key, grouping.clone());
1990 self.add_repeat_symbols(grouping.num_repeats)?;
1991 Ok(())
1992 }
1993
1994 fn split_and_transcribe(
1995 &mut self,
1996 segment: &mut BrailleSegment,
1997 grouping: &mut BrailleElementGrouping,
1998 show_leading_octave_on_first: bool,
1999 add_space_to_first: bool,
2000 ) -> Result<Option<(String, String)>> {
2001 let mut transcriber = NoteGroupingTranscriber::default();
2002 let mut offset = 0;
2003 let mut halves = None;
2004 let mut first_braille = String::new();
2005 while offset < 10 {
2006 let Some(((mut first, sources), second)) = split_with_sources(grouping, offset)? else {
2007 return Ok(None);
2008 };
2009 transcriber.show_leading_octave = show_leading_octave_on_first;
2010 first.with_hyphen = true;
2011 first_braille = transcriber.transcribe_group(&mut first)?;
2012 for (element, source) in first.elements.iter().zip(sources) {
2015 grouping.elements[source].english = element.english.clone();
2016 }
2017 let fits = self
2018 .text
2019 .current_line()
2020 .can_append(&first_braille, add_space_to_first);
2021 halves = Some((first, second));
2022 if fits {
2023 break;
2024 }
2025 offset += 1;
2026 }
2027 let (first, (mut second, second_sources)) = halves.expect("at least one split is tried");
2028 transcriber.show_leading_octave = !segment.options.suppress_octave_marks;
2029 let second_braille = transcriber.transcribe_group(&mut second)?;
2030 for (element, source) in second.elements.iter().zip(second_sources) {
2031 grouping.elements[source].english = element.english.clone();
2032 }
2033 let key = self.current.expect("a grouping being written");
2034 segment.groupings.insert(
2035 SegmentKey {
2036 affinity: Affinity::SplitNoteGroupingA,
2037 ..key
2038 },
2039 first,
2040 );
2041 segment.groupings.insert(
2042 SegmentKey {
2043 affinity: Affinity::SplitNoteGroupingB,
2044 ..key
2045 },
2046 second,
2047 );
2048 Ok(Some((first_braille, second_braille)))
2049 }
2050
2051 fn add_repeat_symbols(&mut self, times: usize) -> Result<()> {
2052 if (1..3).contains(×) {
2053 for _ in 0..times {
2054 self.text.add_signatures(&symbol("repeat"))?;
2055 }
2056 } else if times >= 3 {
2057 let number = number_to_braille(×.to_string(), true, false)?;
2058 self.text.add_signatures(&(symbol("repeat") + &number))?;
2059 self.last_note = None;
2060 }
2061 Ok(())
2062 }
2063
2064 fn extract_signature_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
2065 let key = self.current.expect("a grouping being written");
2066 let grouping = segment.groupings.get_mut(&key).expect("a grouping");
2067 let (key_index, meter_index) = match grouping.elements.len() {
2068 0 => (None, None),
2069 1 => {
2070 if sharps_of(&grouping.elements[0].element).is_some() {
2071 (Some(0), None)
2072 } else {
2073 (None, Some(0))
2074 }
2075 }
2076 _ => (Some(0), Some(1)),
2077 };
2078 let sharps = key_index.and_then(|index| sharps_of(&grouping.elements[index].element));
2079 let outgoing = if segment.options.cancel_outgoing_key_sig {
2080 key_index.and_then(|index| grouping.elements[index].outgoing_key)
2081 } else {
2082 None
2083 };
2084 let meter = meter_index.and_then(|index| match &grouping.elements[index].element {
2085 StreamElement::TimeSignature(meter) => Some(meter.clone()),
2086 _ => None,
2087 });
2088 let written = transcribe_signatures(sharps, meter.as_ref(), outgoing);
2089 if let Some(index) = key_index
2090 && let Some(sharps) = sharps
2091 {
2092 grouping.elements[index].english =
2093 Some(basic::key_sig_to_braille(sharps, outgoing).english);
2094 }
2095 if let (Some(index), Some(meter)) = (meter_index, &meter) {
2096 grouping.elements[index].english = Some(basic::time_sig_to_braille(meter).english);
2097 }
2098 if !written.braille.is_empty() {
2099 self.text.add_signatures(&written.braille)?;
2100 }
2101 Ok(())
2102 }
2103
2104 fn extract_tempo_text_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
2105 let current = self.current.expect("a grouping being written");
2106 self.keys.push_front(current);
2107 let previous = self.previous.ok_or_else(|| {
2108 Error::Notation("'NoneType' object has no attribute 'affinity'".to_string())
2109 })?;
2110 if previous.affinity == Affinity::Signature {
2111 self.keys.push_front(previous);
2112 }
2113 self.extract_heading(segment)?;
2114 self.extract_measure_number(segment)
2115 }
2116}
2117
2118#[derive(Clone, Debug)]
2121pub struct BrailleGrandSegment {
2122 pub groupings: BTreeMap<SegmentKey, BrailleElementGrouping>,
2124 pub line_length: usize,
2126 pub right_contexts: Vec<NoteContext>,
2128 pub left_contexts: Vec<NoteContext>,
2130}
2131
2132impl BrailleGrandSegment {
2133 pub fn combined_keys(&self) -> Vec<(Option<SegmentKey>, Option<SegmentKey>)> {
2136 let mut keys: Vec<SegmentKey> = self.groupings.keys().copied().collect();
2137 keys.sort_by_key(|key| {
2138 (
2139 key.measure,
2140 key.ordinal,
2141 key.affinity,
2142 if key.hand == Some(Hand::Right) { -1 } else { 1 },
2143 )
2144 });
2145 let matches = |one: &SegmentKey, other: &SegmentKey| {
2146 one.measure == other.measure
2147 && one.ordinal == other.ordinal
2148 && one.affinity == other.affinity
2149 };
2150 let mut out = Vec::new();
2151 let mut stored_right: Option<SegmentKey> = None;
2152 let mut stored_left: Option<SegmentKey> = None;
2153 for key in keys {
2154 match key.hand {
2155 Some(Hand::Right) => {
2156 if let Some(left) = stored_left.take() {
2157 if matches(&key, &left)
2158 || (key.affinity == Affinity::NoteGrouping
2159 && matches(
2160 &SegmentKey {
2161 affinity: Affinity::Inaccord,
2162 ..key
2163 },
2164 &left,
2165 ))
2166 {
2167 out.push((Some(key), Some(left)));
2168 } else {
2169 out.push((None, Some(left)));
2170 stored_right = Some(key);
2171 }
2172 } else {
2173 stored_right = Some(key);
2174 }
2175 }
2176 Some(Hand::Left) => {
2177 if let Some(right) = stored_right.take() {
2178 if matches(&key, &right) {
2179 out.push((Some(right), Some(key)));
2180 } else if right.affinity < Affinity::Inaccord {
2181 out.push((Some(right), None));
2182 out.push((None, Some(key)));
2183 } else {
2184 out.push((Some(right), None));
2185 stored_left = Some(key);
2186 }
2187 } else {
2188 stored_left = Some(key);
2189 }
2190 }
2191 None => {}
2192 }
2193 }
2194 if let Some(right) = stored_right {
2195 out.push((Some(right), None));
2196 }
2197 if let Some(left) = stored_left {
2198 out.push((None, Some(left)));
2199 }
2200 out
2201 }
2202
2203 pub fn transcribe(&mut self) -> Result<String> {
2210 let mut pairs: VecDeque<(Option<SegmentKey>, Option<SegmentKey>)> =
2211 self.combined_keys().into();
2212 let mut keyboard = BrailleKeyboard::new(self.line_length);
2213 let Some(last) = pairs.back().copied() else {
2214 return Ok(keyboard.text.render());
2215 };
2216 let highest = last.0.or(last.1).map_or(0, |key| key.measure);
2217 keyboard.highest_measure_number_length = highest.to_string().chars().count();
2218 let mut sharps: Option<Option<IntegerType>> = None;
2220 let mut meter: Option<TimeSignature> = None;
2221 let mut tempo: Option<crate::tempo::TempoText> = None;
2222 let mut mark: Option<crate::tempo::MetronomeMark> = None;
2223 let mut places: [Option<(SegmentKey, usize)>; 4] = [None; 4];
2224 while let Some(&(right, _)) = pairs.front() {
2225 let use_key = right.ok_or_else(|| {
2229 Error::Notation("'NoneType' object has no attribute 'affinity'".to_string())
2230 })?;
2231 if use_key.affinity > Affinity::MetronomeMark {
2232 break;
2233 }
2234 pairs.pop_front();
2235 let grouping = &self.groupings[&use_key];
2236 match use_key.affinity {
2237 Affinity::Signature => {
2238 if grouping.elements.len() >= 2 {
2239 sharps = sharps_of(&grouping.elements[0].element);
2240 places[0] = sharps.map(|_| (use_key, 0));
2241 if let StreamElement::TimeSignature(time) = &grouping.elements[1].element {
2242 meter = Some(time.clone());
2243 places[1] = Some((use_key, 1));
2244 }
2245 } else if let Some(only) = grouping.elements.first() {
2246 match &only.element {
2251 StreamElement::TimeSignature(time) => {
2252 meter = Some(time.clone());
2253 places[1] = Some((use_key, 0));
2254 }
2255 _ => {
2256 return Err(Error::Notation(
2257 "'KeySignature' object has no attribute 'symbol'".to_string(),
2258 ));
2259 }
2260 }
2261 }
2262 }
2263 Affinity::TempoText => {
2264 if let Some(StreamElement::TempoText(text)) =
2265 grouping.elements.first().map(|e| &e.element)
2266 {
2267 tempo = Some(text.clone());
2268 places[2] = Some((use_key, 0));
2269 }
2270 }
2271 Affinity::MetronomeMark => {
2272 if let Some(StreamElement::MetronomeMark(found)) =
2273 grouping.elements.first().map(|e| &e.element)
2274 {
2275 mark = Some(found.clone());
2276 places[3] = Some((use_key, 0));
2277 }
2278 }
2279 _ => {}
2280 }
2281 }
2282 if let Ok(heading) = transcribe_heading(
2283 sharps,
2284 meter.as_ref(),
2285 tempo.as_ref(),
2286 mark.as_ref(),
2287 self.line_length,
2288 ) {
2289 let english = heading_english(
2290 sharps,
2291 meter.as_ref(),
2292 tempo.as_ref(),
2293 mark.as_ref(),
2294 self.line_length,
2295 );
2296 for (place, english) in places.into_iter().zip(english) {
2297 if let (Some((key, index)), Some(english)) = (place, english) {
2298 self.groupings.get_mut(&key).expect("a grouping").elements[index].english =
2299 Some(english);
2300 }
2301 }
2302 keyboard.text.add_heading(&heading)?;
2303 }
2304 while let Some((right, left)) = pairs.pop_front() {
2305 let writes = right.is_some_and(|key| key.affinity >= Affinity::Inaccord)
2306 || left.is_some_and(|key| key.affinity >= Affinity::Inaccord);
2307 if !writes {
2308 continue;
2309 }
2310 let measure = right.or(left).map_or(0, |key| key.measure);
2311 let number = number_to_braille(&measure.to_string(), false, false)?;
2312 let right_braille = self.braille_from_key(right)?;
2313 let left_braille = self.braille_from_key(left)?;
2314 keyboard.add_note_groupings(&number, &right_braille, &left_braille)?;
2315 }
2316 Ok(keyboard.text.render())
2317 }
2318
2319 fn braille_from_key(&mut self, key: Option<SegmentKey>) -> Result<String> {
2320 let Some(key) = key else {
2321 return Ok(String::new());
2322 };
2323 let contexts = if key.hand == Some(Hand::Left) {
2324 &self.left_contexts
2325 } else {
2326 &self.right_contexts
2327 };
2328 let grouping = self.groupings.get_mut(&key).expect("a grouping");
2329 if key.affinity == Affinity::Inaccord {
2330 let mut voices = Vec::new();
2331 let (descending, clefs, upper) = (
2332 grouping.descending_chords,
2333 grouping.show_clef_signs,
2334 grouping.upper_first_in_note_fingering,
2335 );
2336 for element in &mut grouping.elements {
2337 let StreamElement::Stream(voice) = &element.element else {
2338 continue;
2339 };
2340 let mut inner = extract_braille_elements(voice, element.leaf, contexts);
2341 inner.descending_chords = descending;
2342 inner.show_clef_signs = clefs;
2343 inner.upper_first_in_note_fingering = upper;
2344 voices.push(transcribe_note_grouping(&mut inner, true)?);
2345 element.voice_english = voice_english_of(voice, element.leaf, &inner);
2346 }
2347 Ok(voices.join(&symbol("full_inaccord")))
2348 } else {
2349 transcribe_note_grouping(grouping, true)
2350 }
2351 }
2352
2353 pub fn english(&self) -> String {
2356 let mut pairs = String::new();
2357 for (right, left) in self.combined_keys() {
2358 let right_full = right.map_or_else(String::new, |key| {
2359 format!(
2360 "Measure {} Right, {} {}:\n{}",
2361 key.measure,
2362 key.affinity.name(),
2363 key.ordinal + 1,
2364 grouping_english(&self.groupings[&key])
2365 )
2366 });
2367 let left_full = left.map_or_else(String::new, |key| {
2368 format!(
2369 "\nMeasure {} Left, {} {}:\n{}",
2370 key.measure,
2371 key.affinity.name(),
2372 key.ordinal + 1,
2373 grouping_english(&self.groupings[&key])
2374 )
2375 });
2376 pairs.push_str(&[right_full, left_full, "====\n".to_string()].join("\n"));
2377 }
2378 [
2379 "---begin grand segment---".to_string(),
2380 "<music21.braille.segment BrailleGrandSegment>\n===".to_string(),
2381 pairs,
2382 "---end grand segment---".to_string(),
2383 ]
2384 .join("\n")
2385 }
2386}