1use std::collections::{BTreeMap, VecDeque};
12
13use super::SegmentDivision;
14use super::basic::{
15 self, NoteContext, Transcription, barline_to_braille, chord_symbol_to_braille,
16 chord_to_braille, clef_to_braille, dynamic_to_braille, note_to_braille, number_to_braille,
17 rest_to_braille, text_expression_to_braille, transcribe_heading, transcribe_signatures,
18 yield_dots,
19};
20use super::lookup::{self, symbol};
21use super::text::{BrailleKeyboard, BrailleText};
22use crate::{
23 defaults::{FloatType, IntegerType},
24 duration::Duration,
25 error::{Error, Result},
26 makenotation::op_frac,
27 meter::TimeSignature,
28 note::Note,
29 pitch::Pitch,
30 rest::Rest,
31 stream::{Stream, StreamElement, StreamKind},
32};
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub enum Affinity {
38 Signature = 3,
40 TempoText = 4,
42 MetronomeMark = 5,
44 LongTextExpression = 6,
46 Inaccord = 7,
48 SplitNoteGroupingA = 8,
50 NoteGrouping = 9,
52 SplitNoteGroupingB = 10,
54}
55
56impl Affinity {
57 pub fn name(self) -> &'static str {
59 match self {
60 Self::Signature => "Signature Grouping",
61 Self::TempoText => "Tempo Text Grouping",
62 Self::MetronomeMark => "Metronome Mark Grouping",
63 Self::LongTextExpression => "Long Text Expression Grouping",
64 Self::Inaccord => "Inaccord Grouping",
65 Self::NoteGrouping => "Note Grouping",
66 Self::SplitNoteGroupingA => "Split Note Grouping A",
67 Self::SplitNoteGroupingB => "Split Note Grouping B",
68 }
69 }
70}
71
72#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
74pub enum Hand {
75 Left,
77 Right,
79}
80
81#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
84pub struct SegmentKey {
85 pub measure: IntegerType,
87 pub ordinal: usize,
89 pub affinity: Affinity,
91 pub hand: Option<Hand>,
93}
94
95#[derive(Clone, Debug)]
99pub struct BrailleElement {
100 pub element: StreamElement,
102 pub offset: FloatType,
104 pub affinity: Affinity,
106 pub sort_order: i32,
109 pub english: Option<Vec<String>>,
111 pub context: NoteContext,
113 pub outgoing_key: Option<Option<IntegerType>>,
116 pub leaf: usize,
119 pub voice_english: Vec<Option<Vec<String>>>,
122}
123
124#[derive(Clone, Debug)]
127pub struct BrailleElementGrouping {
128 pub elements: Vec<BrailleElement>,
130 pub key_signature: Option<IntegerType>,
132 pub time_signature: TimeSignature,
134 pub descending_chords: Option<bool>,
137 pub show_clef_signs: bool,
139 pub upper_first_in_note_fingering: bool,
141 pub with_hyphen: bool,
143 pub num_repeats: usize,
145}
146
147impl Default for BrailleElementGrouping {
148 fn default() -> Self {
149 Self {
150 elements: Vec::new(),
151 key_signature: Some(0),
152 time_signature: TimeSignature::new(4, 4).expect("4/4 is a meter"),
153 descending_chords: Some(true),
154 show_clef_signs: false,
155 upper_first_in_note_fingering: true,
156 with_hyphen: false,
157 num_repeats: 0,
158 }
159 }
160}
161
162pub(crate) fn element_repr(element: &StreamElement) -> String {
165 match element {
166 StreamElement::Note(note) => format!("<music21.note.Note {}>", note.pitch().name()),
167 StreamElement::Rest(rest) => {
168 let duration = rest.duration();
169 let name = duration.full_name().to_lowercase();
170 let text = if name.chars().count() < 15 {
171 name.replace(' ', "-")
172 } else {
173 let length = duration.quarter_length();
174 if length.fract() == 0.0 {
175 format!("{}ql", length as i64)
176 } else {
177 format!("{}ql", crate::statistics::python_repr(length))
178 }
179 };
180 format!("<music21.note.Rest {text}>")
181 }
182 StreamElement::Chord(chord) => {
183 let names: Vec<String> = chord
184 .pitches()
185 .iter()
186 .map(Pitch::name_with_octave)
187 .collect();
188 format!("<music21.chord.Chord {}>", names.join(" "))
189 }
190 StreamElement::Clef(clef) => format!("<music21.clef.{}>", clef.kind().class_name()),
191 StreamElement::Barline(barline) => {
192 format!("<music21.bar.Barline type={}>", barline.bar_type().as_str())
193 }
194 StreamElement::Dynamic(dynamic) => {
195 format!("<music21.dynamics.Dynamic {}>", dynamic.value())
196 }
197 StreamElement::TextExpression(text) => {
198 let content = text.content();
199 let shown = if content.chars().count() >= 13 {
200 format!("{}...", content.chars().take(10).collect::<String>())
201 } else {
202 content.to_string()
203 };
204 format!(
205 "<music21.expressions.TextExpression {}>",
206 python_str_repr(&shown)
207 )
208 }
209 StreamElement::TempoText(tempo) => {
210 format!(
211 "<music21.tempo.TempoText {}>",
212 python_str_repr(tempo.text())
213 )
214 }
215 StreamElement::KeySignature(signature) if signature.is_non_traditional() => {
216 "<music21.key.KeySignature of pitches: []>".to_string()
217 }
218 StreamElement::KeySignature(signature) => format!(
219 "<music21.key.KeySignature of {}>",
220 sharps_description(signature.sharps())
221 ),
222 StreamElement::Key(key) => {
223 let tonic = music21_pitch_name(key.tonic_pitch());
225 let tonic = if key.mode() == "minor" {
226 tonic.to_lowercase()
227 } else {
228 tonic
229 };
230 format!("{tonic} {}", key.mode())
231 }
232 StreamElement::MetronomeMark(mark) => {
233 let mut sounding = "";
234 let mut number = mark.number();
235 if number.is_none() && mark.number_sounding().is_some() {
236 sounding = " (playback only)";
237 number = mark.number_sounding();
238 }
239 let number = number.map_or_else(|| "None".to_string(), python_number);
240 let referent = mark.referent().full_name();
241 match mark.text() {
242 Some(text) => {
243 format!("<music21.tempo.MetronomeMark {text} {referent}={number}{sounding}>")
244 }
245 None => format!("<music21.tempo.MetronomeMark {referent}={number}{sounding}>"),
246 }
247 }
248 StreamElement::Unpitched(unpitched) => match unpitched
249 .stored_instrument()
250 .and_then(|instrument| instrument.name())
251 {
252 Some(name) => format!("<music21.note.Unpitched {}>", python_str_repr(name)),
253 None => "<music21.note.Unpitched>".to_string(),
254 },
255 StreamElement::PercussionChord(chord) => {
256 let members: Vec<String> = chord
257 .members()
258 .iter()
259 .map(|member| match member {
260 crate::percussion::PercussionNote::Note(note) => {
261 note.pitch().name_with_octave()
262 }
263 crate::percussion::PercussionNote::Unpitched(unpitched) => match unpitched
264 .stored_instrument()
265 .and_then(|instrument| instrument.name())
266 {
267 Some(name) => name.to_string(),
268 None => format!("unpitched[{}]", unpitched.display_name()),
269 },
270 })
271 .collect();
272 format!(
273 "<music21.percussion.PercussionChord [{}]>",
274 members.join(" ")
275 )
276 }
277 StreamElement::TimeSignature(meter) => format!(
278 "<music21.meter.TimeSignature {}/{}>",
279 meter.numerator(),
280 meter.denominator()
281 ),
282 StreamElement::Stream(stream) => match stream.id() {
283 Some(id) => format!("<music21.stream.{:?} {id}>", stream.kind()),
284 None => format!("<music21.stream.{:?}>", stream.kind()),
285 },
286 other => format!("<music21.{other:?}>"),
287 }
288}
289
290pub(crate) fn music21_pitch_name(pitch: &Pitch) -> String {
292 let modifier = match pitch
293 .written_accidental()
294 .map(|accidental| accidental.name())
295 {
296 Some("flat") => "-",
297 Some("double-flat") => "--",
298 Some("sharp") => "#",
299 Some("double-sharp") => "##",
300 _ => "",
301 };
302 format!("{}{modifier}", pitch.step().as_char())
303}
304
305fn sharps_description(sharps: IntegerType) -> String {
307 match sharps {
308 0 => "no sharps or flats".to_string(),
309 1 => "1 sharp".to_string(),
310 -1 => "1 flat".to_string(),
311 sharps if sharps > 0 => format!("{sharps} sharps"),
312 sharps => format!("{} flats", -sharps),
313 }
314}
315
316fn python_number(number: FloatType) -> String {
318 if number.fract() == 0.0 {
319 format!("{}", number as i64)
320 } else {
321 crate::statistics::python_repr(number)
322 }
323}
324
325fn python_str_repr(text: &str) -> String {
328 let quote = if text.contains('\'') && !text.contains('"') {
329 '"'
330 } else {
331 '\''
332 };
333 let mut out = String::new();
334 out.push(quote);
335 for character in text.chars() {
336 match character {
337 '\\' => out.push_str("\\\\"),
338 '\n' => out.push_str("\\n"),
339 '\r' => out.push_str("\\r"),
340 '\t' => out.push_str("\\t"),
341 c if c == quote => {
342 out.push('\\');
343 out.push(c);
344 }
345 c if (c as u32) < 0x20 || c as u32 == 0x7f => {
346 out.push_str(&format!("\\x{:02x}", c as u32));
347 }
348 c => out.push(c),
349 }
350 }
351 out.push(quote);
352 out
353}
354
355fn affinity_of(element: &StreamElement) -> Option<(Affinity, i32)> {
358 Some(match element {
359 StreamElement::Note(_) | StreamElement::Rest(_) | StreamElement::Chord(_) => {
360 (Affinity::NoteGrouping, 10)
361 }
362 StreamElement::ChordSymbol(_) => (Affinity::NoteGrouping, 10),
363 StreamElement::Dynamic(_) => (Affinity::NoteGrouping, 9),
364 StreamElement::Clef(_) => (Affinity::NoteGrouping, 7),
365 StreamElement::Barline(_) => (Affinity::SplitNoteGroupingB, 0),
366 StreamElement::KeySignature(_) | StreamElement::Key(_) => (Affinity::Signature, 1),
367 StreamElement::TimeSignature(_) => (Affinity::Signature, 2),
368 StreamElement::TempoText(_) => (Affinity::TempoText, 3),
369 StreamElement::MetronomeMark(_) => (Affinity::MetronomeMark, 4),
370 StreamElement::Stream(inner) if inner.kind() == StreamKind::Voice => {
371 (Affinity::Inaccord, 10)
372 }
373 StreamElement::BrailleSegmentDivision(_) => (Affinity::NoteGrouping, -100),
376 StreamElement::TextExpression(text) => {
377 if text.content().split_whitespace().count() > 1 {
378 (Affinity::LongTextExpression, 8)
379 } else {
380 (Affinity::NoteGrouping, 8)
381 }
382 }
383 _ => return None,
384 })
385}
386
387pub fn affinity_code(element: &StreamElement) -> Option<(Affinity, i32)> {
391 affinity_of(element)
392}
393
394fn is_general_note(element: &StreamElement) -> bool {
396 matches!(
397 element,
398 StreamElement::Note(_)
399 | StreamElement::Rest(_)
400 | StreamElement::Chord(_)
401 | StreamElement::ChordSymbol(_)
402 | StreamElement::Unpitched(_)
403 | StreamElement::PercussionChord(_)
404 )
405}
406
407fn is_not_rest(element: &StreamElement) -> bool {
409 matches!(
410 element,
411 StreamElement::Note(_)
412 | StreamElement::Chord(_)
413 | StreamElement::ChordSymbol(_)
414 | StreamElement::Unpitched(_)
415 | StreamElement::PercussionChord(_)
416 )
417}
418
419fn walk_of(stream: &Stream, leaf_base: usize) -> Vec<(FloatType, StreamElement, usize)> {
423 let mut out = Vec::new();
424 if let Some(barline) = stream.left_barline() {
425 out.push((0.0, StreamElement::Barline(barline.clone()), leaf_base));
426 }
427 let mut leaf = leaf_base;
428 for event in stream.events() {
429 let element = event.element().clone();
430 let leaves = match &element {
431 StreamElement::Stream(inner) => inner.leaves().len(),
432 _ => 1,
433 };
434 out.push((event.offset(), element, leaf));
435 leaf += leaves;
436 }
437 if let Some(barline) = stream.right_barline() {
438 out.push((
439 stream.end_offset(),
440 StreamElement::Barline(barline.clone()),
441 leaf,
442 ));
443 }
444 out
445}
446
447pub fn extract_braille_elements(
451 stream: &Stream,
452 leaf_base: usize,
453 contexts: &[NoteContext],
454) -> BrailleElementGrouping {
455 let mut grouping = BrailleElementGrouping::default();
456 let mut last_clef: Option<(FloatType, StreamElement, usize)> = None;
457 for (offset, element, leaf) in walk_of(stream, leaf_base) {
458 if matches!(element, StreamElement::Clef(_)) {
459 last_clef = Some((offset, element, leaf));
460 continue;
461 }
462 if let StreamElement::Barline(barline) = &element
463 && barline.bar_type().as_str() == "regular"
464 {
465 continue;
466 }
467 let Some((affinity, sort_order)) = affinity_of(&element) else {
468 continue;
469 };
470 let attaches = is_general_note(&element)
471 || matches!(&element, StreamElement::Stream(inner) if inner.kind() == StreamKind::Voice);
472 if attaches && let Some((clef_offset, clef, clef_leaf)) = last_clef.take() {
473 grouping.elements.push(BrailleElement {
474 english: Some(vec![element_repr(&clef)]),
475 element: clef,
476 offset: clef_offset,
477 affinity,
478 sort_order: 7,
479 context: NoteContext::default(),
480 outgoing_key: None,
481 leaf: clef_leaf,
482 voice_english: Vec::new(),
483 });
484 }
485 grouping.elements.push(BrailleElement {
486 english: Some(vec![element_repr(&element)]),
487 context: contexts.get(leaf).copied().unwrap_or_default(),
488 element,
489 offset,
490 affinity,
491 sort_order,
492 outgoing_key: None,
493 leaf,
494 voice_english: Vec::new(),
495 });
496 }
497 let order = |left: &BrailleElement, right: &BrailleElement| {
498 left.offset
499 .total_cmp(&right.offset)
500 .then(left.sort_order.cmp(&right.sort_order))
501 };
502 grouping.elements.sort_by(order);
503 let count = grouping.elements.len();
504 if count >= 2
505 && matches!(
506 grouping.elements[count - 1].element,
507 StreamElement::Dynamic(_)
508 )
509 && matches!(
510 grouping.elements[count - 2].element,
511 StreamElement::Barline(_)
512 )
513 {
514 grouping.elements[count - 1].sort_order = -1;
515 grouping.elements.sort_by(order);
516 }
517 grouping
518}
519
520fn first_beam_type(element: &StreamElement) -> Option<String> {
523 let beams = match element {
524 StreamElement::Note(note) => note.beams(),
525 StreamElement::Chord(chord) => chord.beams(),
526 _ => return None,
527 };
528 beams
529 .beams()
530 .iter()
531 .find(|beam| beam.number() == Some(1))
532 .and_then(|beam| beam.beam_type())
533 .map(|kind| kind.as_str().to_string())
534}
535
536fn whole_beat(meter: Option<&TimeSignature>, offset: FloatType) -> Option<i64> {
538 meter
539 .and_then(|meter| meter.beat_at_offset(offset).ok())
540 .map(i64::from)
541}
542
543pub fn prepare_beamed_notes(
548 items: &[(FloatType, StreamElement)],
549 meter: Option<&TimeSignature>,
550) -> Vec<(bool, bool)> {
551 let mut flags = vec![(false, false); items.len()];
552 let notes: Vec<usize> = (0..items.len())
554 .filter(|&i| is_not_rest(&items[i].1))
555 .collect();
556 let general: Vec<usize> = (0..items.len())
557 .filter(|&i| is_general_note(&items[i].1))
558 .collect();
559 let starts: Vec<usize> = notes
560 .iter()
561 .copied()
562 .filter(|&i| first_beam_type(&items[i].1).as_deref() == Some("start"))
563 .collect();
564 let stops: Vec<usize> = notes
565 .iter()
566 .copied()
567 .filter(|&i| first_beam_type(&items[i].1).as_deref() == Some("stop"))
568 .collect();
569 if starts.len() != stops.len() {
570 return flags;
571 }
572 let length = |i: usize| items[i].1.quarter_length();
573 let same_length = |a: FloatType, b: FloatType| (a - b).abs() < 1e-9;
576 for (start, stop) in starts.into_iter().zip(stops) {
577 if length(start) == 0.5 {
578 continue;
579 }
580 let (Some(start_index), Some(stop_index)) = (
581 general.iter().position(|&i| i == start),
582 general.iter().position(|&i| i == stop),
583 ) else {
584 continue;
585 };
586 if (stop_index as i64) - (start_index as i64) + 1 < 3 {
587 continue;
588 }
589 let mut same = (start_index + 1..=stop_index).all(|index| {
590 let i = general[index];
591 same_length(length(i), length(start)) && !matches!(items[i].1, StreamElement::Rest(_))
592 });
593 if let Some(&after) = general.get(stop_index + 1)
594 && matches!(items[after].1, StreamElement::Rest(_))
595 && whole_beat(meter, items[after].0) == whole_beat(meter, items[stop].0)
596 {
597 same = false;
598 }
599 if !same {
600 continue;
601 }
602 if let Some(&after) = general.get(stop_index + 1)
603 && length(after) == 0.5
604 {
605 continue;
606 }
607 flags[start].0 = true;
608 if start_index >= 1 {
609 let before = general[start_index - 1];
610 if matches!(items[before].1, StreamElement::Rest(_))
611 && whole_beat(meter, items[before].0) == whole_beat(meter, items[start].0)
612 && same_length(length(before), length(start))
613 {
614 flags[start].1 = true;
615 }
616 } else {
617 let before = general[general.len() - 1];
620 if matches!(items[before].1, StreamElement::Rest(_))
621 && whole_beat(meter, items[before].0) == whole_beat(meter, items[start].0)
622 && same_length(length(before), length(start))
623 {
624 flags[start].1 = true;
625 }
626 }
627 for index in start_index + 1..=stop_index {
628 flags[general[index]].1 = true;
629 }
630 }
631 flags
632}
633
634#[derive(Clone, Copy, Debug, PartialEq, Eq)]
636#[non_exhaustive]
637pub struct SegmentOptions {
638 pub cancel_outgoing_key_sig: bool,
640 pub descending_chords: Option<bool>,
645 pub dummy_rest_length: Option<usize>,
647 pub max_line_length: usize,
649 pub show_clef_signs: bool,
651 pub show_first_measure_number: bool,
653 pub show_hand: Option<Hand>,
655 pub show_heading: bool,
657 pub show_long_slurs_and_ties_together: Option<bool>,
659 pub show_short_slurs_and_ties_together: bool,
661 pub slur_long_phrase_with_brackets: bool,
664 pub suppress_octave_marks: bool,
666 pub upper_first_in_note_fingering: bool,
668}
669
670impl Default for SegmentOptions {
671 fn default() -> Self {
672 Self {
673 cancel_outgoing_key_sig: true,
674 descending_chords: None,
675 dummy_rest_length: None,
676 max_line_length: 40,
677 show_clef_signs: false,
678 show_first_measure_number: true,
679 show_hand: None,
680 show_heading: true,
681 show_long_slurs_and_ties_together: None,
682 show_short_slurs_and_ties_together: false,
683 slur_long_phrase_with_brackets: true,
684 suppress_octave_marks: false,
685 upper_first_in_note_fingering: true,
686 }
687 }
688}
689
690const MAX_NOTES_FOR_SHORT_SLUR: usize = 4;
693
694const MAX_ELEMENTS_IN_SEGMENT: usize = 48;
697
698pub fn prepare_slurred_notes(part: &Stream, options: &SegmentOptions) -> Vec<NoteContext> {
702 let leaves = part.leaves();
703 let mut contexts = vec![NoteContext::default(); leaves.len()];
704 if part.spanners().is_empty() {
705 return contexts;
706 }
707 let show_long = options
708 .show_long_slurs_and_ties_together
709 .unwrap_or(options.slur_long_phrase_with_brackets);
710 let mut notes: Vec<(FloatType, i32, bool, usize)> = leaves
712 .iter()
713 .enumerate()
714 .filter(|(_, (_, element))| is_not_rest(element))
715 .map(|(leaf, (offset, element))| {
716 let grace = element.duration().is_some_and(Duration::is_grace);
717 (*offset, element.class_sort_order(), !grace, leaf)
718 })
719 .collect();
720 notes.sort_by(|left, right| {
721 left.0
722 .total_cmp(&right.0)
723 .then(left.1.cmp(&right.1))
724 .then(left.2.cmp(&right.2))
725 });
726 let notes: Vec<usize> = notes.into_iter().map(|(_, _, _, leaf)| leaf).collect();
727 let tie_of = |leaf: usize| -> Option<crate::notation::TieType> {
728 match leaves[leaf].1 {
729 StreamElement::Note(note) => note.tie().map(|tie| tie.tie_type()),
730 StreamElement::Chord(chord) => chord.tie().map(|tie| tie.tie_type()),
731 _ => None,
732 }
733 };
734 let at = |index: i64| -> usize {
735 let count = notes.len() as i64;
736 notes[(((index % count) + count) % count) as usize]
737 };
738 for spanner in part.spanners() {
739 if spanner.kind() != crate::spanner::SpannerKind::Slur {
740 continue;
741 }
742 let spanned = spanner.spanned();
743 let (Some(Some(first)), Some(Some(last))) = (spanned.first(), spanned.last()) else {
744 continue;
745 };
746 let (Some(begin), Some(end)) = (
747 notes.iter().position(|leaf| leaf == first),
748 notes.iter().position(|leaf| leaf == last),
749 ) else {
750 continue;
751 };
752 let (mut begin, mut end) = (begin.min(end) as i64, begin.max(end) as i64);
753 let delta = (end - begin) as usize + 1;
754 let short = delta <= MAX_NOTES_FOR_SHORT_SLUR;
755 if (short && !options.show_short_slurs_and_ties_together) || (!short && !show_long) {
756 if tie_of(at(begin)) == Some(crate::notation::TieType::Start) {
757 begin += 1;
758 }
759 if tie_of(at(end)) == Some(crate::notation::TieType::Stop) {
760 end -= 1;
761 }
762 }
763 if short {
764 for index in begin..end {
765 contexts[at(index)].short_slur = true;
766 }
767 } else if options.slur_long_phrase_with_brackets {
768 contexts[at(begin)].begin_long_bracket_slur = true;
769 contexts[at(end)].end_long_bracket_slur = true;
770 } else {
771 contexts[at(begin + 1)].begin_long_double_slur = true;
772 contexts[at(end - 1)].end_long_double_slur = true;
773 }
774 }
775 contexts
776}
777
778#[derive(Clone, Debug)]
781pub struct BrailleSegment {
782 pub groupings: BTreeMap<SegmentKey, BrailleElementGrouping>,
784 pub options: SegmentOptions,
786 pub descending_chords: bool,
789 pub end_hyphen: bool,
791 pub begins_mid_measure: bool,
793 pub contexts: Vec<NoteContext>,
795}
796
797impl BrailleSegment {
798 fn new(options: SegmentOptions, contexts: &[NoteContext]) -> Self {
799 Self {
800 groupings: BTreeMap::new(),
801 options,
802 descending_chords: options.descending_chords.unwrap_or(true),
803 end_hyphen: false,
804 begins_mid_measure: false,
805 contexts: contexts.to_vec(),
806 }
807 }
808}
809
810fn sharps_of(element: &StreamElement) -> Option<Option<IntegerType>> {
812 match element {
813 StreamElement::Key(key) => Some(Some(key.sharps())),
814 StreamElement::KeySignature(signature) => Some(Some(signature.sharps())),
815 _ => None,
816 }
817}
818
819fn measures_of(part: &Stream) -> Vec<(&Stream, usize)> {
821 let mut out = Vec::new();
822 let mut leaf = 0;
823 for event in part.events() {
824 match event.element() {
825 StreamElement::Stream(inner) => {
826 if inner.kind() == StreamKind::Measure {
827 out.push((&**inner, leaf));
828 }
829 leaf += inner.leaves().len();
830 }
831 _ => leaf += 1,
832 }
833 }
834 out
835}
836
837fn meters_of(part: &Stream) -> Vec<Option<TimeSignature>> {
841 let loose: Vec<(FloatType, TimeSignature)> = part
842 .events()
843 .iter()
844 .filter_map(|event| match event.element() {
845 StreamElement::TimeSignature(meter) => Some((event.offset(), meter.clone())),
846 _ => None,
847 })
848 .collect();
849 let mut stated: Option<TimeSignature> = None;
850 let mut out = Vec::new();
851 for event in part.events() {
852 let StreamElement::Stream(measure) = event.element() else {
853 continue;
854 };
855 if measure.kind() != StreamKind::Measure {
856 continue;
857 }
858 for inner in measure.events() {
859 if let StreamElement::TimeSignature(meter) = inner.element()
860 && inner.offset() <= 0.0
861 {
862 stated = Some(meter.clone());
863 }
864 }
865 let meter = stated.clone().or_else(|| {
866 loose
867 .iter()
868 .rev()
869 .find(|(at, _)| *at <= event.offset())
870 .map(|(_, meter)| meter.clone())
871 });
872 out.push(meter);
873 }
874 out
875}
876
877pub fn get_raw_segments(
881 part: &Stream,
882 hand: Option<Hand>,
883 contexts: &[NoteContext],
884 options: SegmentOptions,
885) -> Vec<BrailleSegment> {
886 let mut segments = Vec::new();
887 let mut contexts = contexts.to_vec();
888 let mut current = BrailleSegment::new(options, &contexts);
889 let mut in_current = 0;
890 let mut start_new = false;
891 let meters = meters_of(part);
892 for (index, (measure, leaf_base)) in measures_of(part).into_iter().enumerate() {
893 let walk = walk_of(measure, leaf_base);
894 let items: Vec<(FloatType, StreamElement)> = walk
895 .iter()
896 .filter(|(_, element, _)| !matches!(element, StreamElement::Stream(_)))
897 .map(|(offset, element, _)| (*offset, element.clone()))
898 .collect();
899 let leaves: Vec<usize> = walk
900 .iter()
901 .filter(|(_, element, _)| !matches!(element, StreamElement::Stream(_)))
902 .map(|(_, _, leaf)| *leaf)
903 .collect();
904 let flags = prepare_beamed_notes(&items, meters.get(index).and_then(Option::as_ref));
905 for ((item, leaf), (start, continued)) in items.iter().zip(&leaves).zip(flags) {
906 if is_not_rest(&item.1) && *leaf < contexts.len() {
907 contexts[*leaf].beam_start = start;
908 contexts[*leaf].beam_continue = continued;
909 }
910 }
911 let elements = extract_braille_elements(measure, leaf_base, &contexts);
912 let number = IntegerType::from(measure.number());
913 let mut ordinal = 0;
914 let mut previous: Option<Affinity> = None;
915 for element in elements.elements {
916 if start_new {
917 if element.offset != 0.0 {
918 current.end_hyphen = true;
919 }
920 segments.push(std::mem::replace(
921 &mut current,
922 BrailleSegment::new(options, &contexts),
923 ));
924 if element.offset != 0.0 {
925 current.begins_mid_measure = true;
926 }
927 in_current = 0;
928 if previous.is_some() {
929 ordinal += 1;
930 }
931 start_new = false;
932 }
933 if let StreamElement::BrailleSegmentDivision(division) = &element.element {
936 let waits =
937 *division == SegmentDivision::Optional && in_current <= MAX_ELEMENTS_IN_SEGMENT;
938 if !waits && in_current > 0 {
939 start_new = true;
940 }
941 continue;
942 }
943 if let StreamElement::Barline(barline) = &element.element
944 && in_current > MAX_ELEMENTS_IN_SEGMENT
945 && matches!(barline.bar_type().as_str(), "double" | "final")
946 {
947 start_new = true;
948 }
949 if previous.is_some_and(|previous| element.affinity < previous) {
950 ordinal += 1;
951 }
952 let affinity = match element.affinity {
953 Affinity::SplitNoteGroupingA => Affinity::Inaccord,
954 Affinity::SplitNoteGroupingB => Affinity::NoteGrouping,
955 other => other,
956 };
957 let key = SegmentKey {
958 measure: number,
959 ordinal,
960 affinity,
961 hand,
962 };
963 previous = Some(element.affinity);
964 current
965 .groupings
966 .entry(key)
967 .or_default()
968 .elements
969 .push(element);
970 in_current += 1;
971 }
972 }
973 current.contexts = contexts;
974 segments.push(current);
975 segments
976}
977
978fn elements_equal(left: &BrailleElement, right: &BrailleElement) -> bool {
980 super::equality::equal(&left.element, &right.element)
981}
982
983fn groupings_identical(left: &[BrailleElement], right: &[BrailleElement]) -> bool {
986 left.len() == right.len()
987 && left
988 .iter()
989 .zip(right)
990 .all(|(left, right)| elements_equal(left, right))
991}
992
993impl BrailleSegment {
994 pub fn add_grouping_attributes(&mut self) {
998 let mut current_key: Option<IntegerType> = Some(0);
999 let mut current_meter = TimeSignature::new(4, 4).expect("4/4 is a meter");
1000 let keys: Vec<SegmentKey> = self.groupings.keys().copied().collect();
1001 let mut previous: Option<SegmentKey> = None;
1002 for key in keys {
1003 if let Some(previous_key) = previous {
1004 if key.ordinal >= 1 {
1005 self.groupings
1006 .get_mut(&previous_key)
1007 .expect("a grouping")
1008 .with_hyphen = true;
1009 }
1010 if previous_key.ordinal == 0
1011 && previous_key.affinity == Affinity::NoteGrouping
1012 && key.ordinal == 0
1013 && key.affinity == Affinity::NoteGrouping
1014 {
1015 let previous_elements = &self.groupings[&previous_key].elements;
1016 let these = &self.groupings[&key].elements;
1017 let repeated = if previous_elements
1018 .first()
1019 .is_some_and(|first| matches!(first.element, StreamElement::Clef(_)))
1020 {
1021 groupings_identical(&previous_elements[1..], these)
1022 } else {
1023 groupings_identical(previous_elements, these)
1024 };
1025 if repeated {
1026 self.groupings
1027 .get_mut(&previous_key)
1028 .expect("a grouping")
1029 .num_repeats += 1;
1030 self.groupings.remove(&key);
1031 continue;
1032 }
1033 }
1034 }
1035 let descending = &mut self.descending_chords;
1036 let grouping = self.groupings.get_mut(&key).expect("a grouping");
1037 if key.affinity == Affinity::Signature {
1038 for element in &mut grouping.elements {
1039 if let StreamElement::TimeSignature(meter) = &element.element {
1040 current_meter = meter.clone();
1041 } else if let Some(sharps) = sharps_of(&element.element) {
1042 element.outgoing_key = Some(current_key);
1043 current_key = sharps;
1044 }
1045 }
1046 } else if matches!(key.affinity, Affinity::Inaccord | Affinity::NoteGrouping) {
1047 if let Some(StreamElement::Clef(clef)) =
1048 grouping.elements.first().map(|e| &e.element)
1049 {
1050 match clef.kind().class_name() {
1051 "TrebleClef" | "Treble8vbClef" | "Treble8vaClef" | "AltoClef" => {
1052 *descending = true;
1053 }
1054 "BassClef" | "Bass8vbClef" | "Bass8vaClef" | "TenorClef" => {
1055 *descending = false;
1056 }
1057 _ => {}
1058 }
1059 }
1060 let general: Vec<usize> = (0..grouping.elements.len())
1061 .filter(|&i| is_general_note(&grouping.elements[i].element))
1062 .collect();
1063 if let [only] = general.as_slice()
1064 && let StreamElement::Rest(rest) = &mut grouping.elements[*only].element
1065 {
1066 rest.set_full_measure(crate::rest::FullMeasure::Yes);
1067 }
1068 }
1069 grouping.key_signature = current_key;
1070 grouping.time_signature = current_meter.clone();
1071 grouping.descending_chords = Some(*descending);
1072 grouping.show_clef_signs = self.options.show_clef_signs;
1073 grouping.upper_first_in_note_fingering = self.options.upper_first_in_note_fingering;
1074 previous = Some(key);
1075 }
1076 if self.end_hyphen
1077 && let Some(last) = previous
1078 {
1079 self.groupings
1080 .get_mut(&last)
1081 .expect("a grouping")
1082 .with_hyphen = true;
1083 }
1084 }
1085
1086 pub fn fix_articulations(&mut self) {
1092 let mut runs: Vec<Vec<(SegmentKey, usize)>> = Vec::new();
1095 let mut in_run = false;
1096 for (key, grouping) in &self.groupings {
1097 if key.affinity != Affinity::NoteGrouping {
1098 in_run = false;
1099 continue;
1100 }
1101 if !in_run {
1102 runs.push(Vec::new());
1103 in_run = true;
1104 }
1105 let run = runs.last_mut().expect("a run");
1106 for (index, element) in grouping.elements.iter().enumerate() {
1107 if matches!(element.element, StreamElement::Note(_)) {
1108 run.push((*key, index));
1109 }
1110 }
1111 }
1112 for notes in runs {
1113 for start in 0..notes.len() {
1114 let articulations = self.note(notes[start]).articulations().to_vec();
1115 for articulation in &articulations {
1116 if articulation.is_a("TechnicalIndication") && !articulation.is_a("Bowing") {
1117 continue;
1118 }
1119 self.fix_one_articulation(articulation, ¬es, start);
1120 }
1121 }
1122 }
1123 }
1124
1125 fn note(&self, (key, index): (SegmentKey, usize)) -> &Note {
1126 match &self.groupings[&key].elements[index].element {
1127 StreamElement::Note(note) => note,
1128 _ => unreachable!("only notes are gathered"),
1129 }
1130 }
1131
1132 fn element_mut(&mut self, (key, index): (SegmentKey, usize)) -> &mut BrailleElement {
1133 &mut self.groupings.get_mut(&key).expect("a grouping").elements[index]
1134 }
1135
1136 fn note_mut(&mut self, place: (SegmentKey, usize)) -> &mut Note {
1137 match &mut self.element_mut(place).element {
1138 StreamElement::Note(note) => note,
1139 _ => unreachable!("only notes are gathered"),
1140 }
1141 }
1142
1143 fn fix_one_articulation(
1144 &mut self,
1145 articulation: &crate::articulations::Articulation,
1146 notes: &[(SegmentKey, usize)],
1147 start: usize,
1148 ) {
1149 let name = articulation.name();
1150 if (articulation.is_a("Staccato") || articulation.is_a("Tenuto"))
1151 && let Some(tie) = self.note(notes[start]).tie().map(|tie| tie.tie_type())
1152 {
1153 if tie == crate::notation::TieType::Stop {
1156 if start > 0 {
1157 let before = notes[start - 1];
1158 self.note_mut(before).set_tie(None);
1159 self.element_mut(before).context.short_slur = true;
1160 self.note_mut(notes[start]).set_tie(None);
1161 }
1162 } else if start + 1 < notes.len() {
1163 self.note_mut(notes[start + 1]).set_tie(None);
1164 self.element_mut(notes[start]).context.short_slur = true;
1165 self.note_mut(notes[start]).set_tie(None);
1166 }
1167 }
1168 let mut sequential = 0;
1169 for &place in ¬es[start + 1..] {
1170 if self
1171 .note(place)
1172 .articulations()
1173 .iter()
1174 .any(|other| other.name() == name)
1175 {
1176 sequential += 1;
1177 continue;
1178 }
1179 break;
1180 }
1181 if sequential < 3 {
1182 return;
1183 }
1184 self.note_mut(notes[start])
1185 .articulations_mut()
1186 .push(articulation.clone());
1187 for &place in ¬es[start + 1..start + sequential] {
1188 self.note_mut(place)
1189 .articulations_mut()
1190 .retain(|other| other.name() != name);
1191 }
1192 }
1193}
1194
1195pub fn find_segments(
1198 part: &Stream,
1199 hand: Option<Hand>,
1200 options: SegmentOptions,
1201) -> Vec<BrailleSegment> {
1202 let contexts = prepare_slurred_notes(part, &options);
1203 let mut segments = get_raw_segments(part, hand, &contexts, options);
1204 let mut descending = options.descending_chords.unwrap_or(true);
1206 for segment in &mut segments {
1207 segment.descending_chords = descending;
1208 segment.add_grouping_attributes();
1209 descending = segment.descending_chords;
1210 segment.fix_articulations();
1211 }
1212 segments
1213}
1214
1215#[derive(Clone, Debug)]
1218pub struct NoteGroupingTranscriber {
1219 pub show_leading_octave: bool,
1221 previous_note: Option<Pitch>,
1222 previous_element: Option<usize>,
1223 trans: Vec<String>,
1224}
1225
1226impl Default for NoteGroupingTranscriber {
1227 fn default() -> Self {
1228 Self {
1229 show_leading_octave: true,
1230 previous_note: None,
1231 previous_element: None,
1232 trans: Vec::new(),
1233 }
1234 }
1235}
1236
1237impl NoteGroupingTranscriber {
1238 pub fn transcribe_group(&mut self, grouping: &mut BrailleElementGrouping) -> String {
1241 self.previous_note = None;
1242 self.previous_element = None;
1243 self.trans = Vec::new();
1244 for index in 0..grouping.elements.len() {
1245 self.transcribe_one_element(grouping, index);
1246 }
1247 if grouping.with_hyphen {
1248 self.trans.push(symbol("music_hyphen"));
1249 }
1250 self.trans.concat()
1251 }
1252
1253 fn octave_shown(&self, pitch: &Pitch) -> bool {
1254 match &self.previous_note {
1255 None => self.show_leading_octave,
1256 Some(previous) => basic::show_octave_with_note(Some(previous), pitch),
1257 }
1258 }
1259
1260 pub fn transcribed(&self) -> &[String] {
1263 &self.trans
1264 }
1265
1266 pub fn transcribe_element(&mut self, grouping: &mut BrailleElementGrouping, index: usize) {
1276 self.transcribe_one_element(grouping, index);
1277 }
1278
1279 fn follow_written_note(&mut self, written: &Transcription, pitch: Pitch) {
1283 if written.braille == symbol("basic_exception") {
1284 self.previous_note = None;
1285 self.show_leading_octave = true;
1286 } else {
1287 self.previous_note = Some(pitch);
1288 }
1289 }
1290
1291 fn transcribe_one_element(&mut self, grouping: &mut BrailleElementGrouping, index: usize) {
1292 let descending = grouping.descending_chords.unwrap_or(false);
1293 let show_clef_signs = grouping.show_clef_signs;
1294 let upper_first = grouping.upper_first_in_note_fingering;
1295 let item = &mut grouping.elements[index];
1296 let written: Option<Transcription> = match &item.element {
1297 StreamElement::Note(note) => {
1298 let shown = self.octave_shown(note.pitch());
1299 let written = note_to_braille(note, shown, upper_first, item.context);
1300 self.follow_written_note(&written, note.pitch().clone());
1301 Some(written)
1302 }
1303 StreamElement::Rest(rest) => Some(rest_to_braille(rest)),
1304 StreamElement::Chord(chord) => {
1305 let mut pitches = chord.pitches();
1306 pitches.sort_by(|left, right| left.ps().total_cmp(&right.ps()));
1307 let chosen = if descending {
1308 pitches.last().cloned()
1309 } else {
1310 pitches.first().cloned()
1311 };
1312 chosen.map(|pitch| {
1313 let shown = self.octave_shown(&pitch);
1314 let written = chord_to_braille(chord, descending, shown);
1315 self.follow_written_note(&written, pitch);
1316 written
1317 })
1318 }
1319 StreamElement::ChordSymbol(chord_symbol) => {
1320 let mut sorted = chord_symbol.pitches().unwrap_or_default();
1321 sorted.sort_by(|left, right| left.ps().total_cmp(&right.ps()));
1322 let chosen = if descending {
1323 sorted.last()
1324 } else {
1325 sorted.first()
1326 }
1327 .cloned();
1328 chosen.and_then(|pitch| {
1329 let shown = self.octave_shown(&pitch);
1330 let written = chord_symbol_to_braille(chord_symbol, descending, shown)?;
1331 self.follow_written_note(&written, pitch);
1332 Some(written)
1333 })
1334 }
1335 StreamElement::Dynamic(dynamic) => {
1336 self.previous_note = None;
1337 self.show_leading_octave = true;
1338 Some(dynamic_to_braille(dynamic, true))
1339 }
1340 StreamElement::TextExpression(text) => {
1341 self.previous_note = None;
1342 self.show_leading_octave = true;
1343 Some(text_expression_to_braille(text, true))
1344 }
1345 StreamElement::Barline(barline) => Some(barline_to_braille(barline)),
1346 StreamElement::Clef(clef) => {
1347 if show_clef_signs {
1348 self.previous_note = None;
1349 self.show_leading_octave = true;
1350 Some(clef_to_braille(clef, false))
1351 } else {
1352 None
1353 }
1354 }
1355 _ => None,
1356 };
1357 if let Some(written) = written {
1358 item.english = Some(written.english);
1359 self.trans.push(written.braille);
1360 }
1361 self.optionally_add_dot_to_previous(grouping, index);
1362 self.previous_element = Some(index);
1363 }
1364
1365 fn optionally_add_dot_to_previous(
1369 &mut self,
1370 grouping: &mut BrailleElementGrouping,
1371 index: usize,
1372 ) {
1373 let Some(previous) = self.previous_element else {
1374 return;
1375 };
1376 let Some(last) = self.trans.last() else {
1377 return;
1378 };
1379 if matches!(
1380 grouping.elements[index].element,
1381 StreamElement::Dynamic(_) | StreamElement::TextExpression(_)
1382 ) {
1383 return;
1384 }
1385 let dotted = match &grouping.elements[previous].element {
1386 StreamElement::Dynamic(_) => true,
1387 StreamElement::Clef(_) => grouping.show_clef_signs,
1388 StreamElement::TextExpression(text) => !text.content().ends_with('.'),
1389 _ => false,
1390 };
1391 if !dotted {
1392 return;
1393 }
1394 let Some(first) = last.chars().next() else {
1395 return;
1396 };
1397 if let Some(dot) = yield_dots(first).into_iter().next() {
1398 let at = self.trans.len() - 1;
1399 self.trans.insert(at, dot.clone());
1400 grouping.elements[previous]
1401 .english
1402 .get_or_insert_with(Vec::new)
1403 .push(format!("Dot 3 {dot}"));
1404 }
1405 }
1406}
1407
1408pub fn transcribe_note_grouping(
1411 grouping: &mut BrailleElementGrouping,
1412 show_leading_octave: bool,
1413) -> String {
1414 let mut transcriber = NoteGroupingTranscriber {
1415 show_leading_octave,
1416 ..NoteGroupingTranscriber::default()
1417 };
1418 transcriber.transcribe_group(grouping)
1419}
1420
1421pub fn split_note_grouping(
1430 grouping: &mut BrailleElementGrouping,
1431 beat_division_offset: usize,
1432) -> Result<Option<(BrailleElementGrouping, BrailleElementGrouping)>> {
1433 Ok(split_with_sources(grouping, beat_division_offset)?
1434 .map(|((first, _), (second, _))| (first, second)))
1435}
1436
1437type Half = (BrailleElementGrouping, Vec<usize>);
1439
1440type Rebeamed = (usize, StreamElement, (bool, bool));
1443
1444fn split_with_sources(
1446 grouping: &mut BrailleElementGrouping,
1447 beat_division_offset: usize,
1448) -> Result<Option<(Half, Half)>> {
1449 let meter = grouping.time_signature.clone();
1450 let mut offset = 0.0;
1451 if beat_division_offset != 0 {
1452 let divisions = meter.beat_division_durations()?;
1453 if beat_division_offset > divisions.len() {
1454 return Ok(None);
1457 }
1458 if let Some(duration) = divisions.get(divisions.len() - beat_division_offset) {
1459 offset = op_frac(offset + op_frac(duration.quarter_length()));
1460 }
1461 }
1462 let beats = meter.beat_sequence();
1463 let (start_zero, mut end_zero) = match beats.partitioned_by_count(2, false) {
1464 Ok(halves) => halves.level_span(0)[0],
1465 Err(_) => {
1466 let spans = beats.partitioned_by_count(3, false)?.level_span(0);
1467 (spans[0].0, spans[spans.len() - 2].1)
1468 }
1469 };
1470 end_zero -= offset;
1471 let elements = &grouping.elements;
1472 let mut order: Vec<usize> = (0..elements.len()).collect();
1475 order.sort_by(|&a, &b| {
1476 elements[a]
1477 .offset
1478 .total_cmp(&elements[b].offset)
1479 .then(elements[a].sort_order.cmp(&elements[b].sort_order))
1480 });
1481 let mut left: Vec<usize> = Vec::new();
1482 let mut right: Vec<usize> = Vec::new();
1483 for &index in &order {
1484 let at = elements[index].offset;
1485 let barline = matches!(elements[index].element, StreamElement::Barline(_));
1486 if at >= start_zero && (at < end_zero || (at == end_zero && barline)) {
1487 left.push(index);
1488 } else {
1489 right.push(index);
1490 }
1491 }
1492 let highest = elements
1493 .iter()
1494 .map(|element| op_frac(element.offset + element.element.quarter_length()))
1495 .fold(0.0, FloatType::max);
1496 let mut offsets: Vec<FloatType> = elements.iter().map(|element| element.offset).collect();
1497 while let Some(position) = right
1500 .iter()
1501 .position(|&i| is_not_rest(&elements[i].element))
1502 {
1503 let index = right[position];
1504 let tied = match &elements[index].element {
1505 StreamElement::Note(note) => note.tie().is_some(),
1506 StreamElement::Chord(chord) => chord.tie().is_some(),
1507 _ => false,
1508 };
1509 if !tied {
1510 break;
1511 }
1512 offsets[index] = left
1513 .iter()
1514 .map(|&i| op_frac(offsets[i] + elements[i].element.quarter_length()))
1515 .fold(0.0, FloatType::max);
1516 left.push(index);
1517 right.remove(position);
1518 end_zero += elements[index].element.quarter_length();
1519 }
1520 let rebeam =
1523 |indices: &[usize], rest_at: FloatType, rest_length: FloatType| -> Result<Vec<Rebeamed>> {
1524 let rest = Rest::new(Duration::new(rest_length.max(0.0))?);
1525 let mut placed: Vec<(FloatType, Option<usize>, StreamElement)> = indices
1528 .iter()
1529 .map(|&i| (offsets[i], Some(i), elements[i].element.clone()))
1530 .collect();
1531 placed.push((rest_at, None, StreamElement::Rest(rest)));
1532 placed.sort_by(|a, b| a.0.total_cmp(&b.0));
1533 let mut measure = Stream::with_kind(StreamKind::Measure);
1534 measure.insert(0.0, meter.clone());
1535 for (at, _, element) in &placed {
1536 measure.insert(*at, element.clone());
1537 }
1538 crate::makenotation::make_beams(&mut measure)?;
1539 let beamed: Vec<(FloatType, StreamElement)> = measure
1540 .events()
1541 .iter()
1542 .filter(|event| !matches!(event.element(), StreamElement::TimeSignature(_)))
1543 .map(|event| (event.offset(), event.element().clone()))
1544 .collect();
1545 let flags = prepare_beamed_notes(&beamed, Some(&meter));
1546 Ok(placed
1547 .iter()
1548 .zip(beamed)
1549 .zip(flags)
1550 .filter_map(|(((_, source, _), (_, element)), flags)| {
1551 source.map(|source| (source, element, flags))
1552 })
1553 .collect())
1554 };
1555 let left_beamed = rebeam(&left, end_zero, highest - end_zero)?;
1556 let right_beamed = rebeam(&right, 0.0, end_zero)?;
1557 for (index, element, (start, continued)) in left_beamed.iter().chain(&right_beamed) {
1560 let item = &mut grouping.elements[*index];
1561 item.offset = offsets[*index];
1562 item.element = element.clone();
1563 if is_not_rest(&item.element) {
1564 item.context.beam_start = *start;
1565 item.context.beam_continue = *continued;
1566 }
1567 }
1568 let half = |beamed: &[Rebeamed]| -> Half {
1569 let mut half = grouping.clone();
1570 half.elements = beamed
1571 .iter()
1572 .map(|(index, _, _)| grouping.elements[*index].clone())
1573 .collect();
1574 (half, beamed.iter().map(|(index, _, _)| *index).collect())
1575 };
1576 Ok(Some((half(&left_beamed), half(&right_beamed))))
1577}
1578
1579impl BrailleSegment {
1580 pub fn transcribe(&mut self) -> Result<String> {
1587 let hand = match self.options.show_hand {
1588 Some(Hand::Right) => Some("right"),
1589 Some(Hand::Left) => Some("left"),
1590 None => None,
1591 };
1592 let text = BrailleText::new(self.options.max_line_length, hand)?;
1593 let mut transcriber = SegmentTranscriber::new(text);
1594 transcriber.transcribe(self)?;
1595 Ok(transcriber.into_text().render())
1596 }
1597
1598 pub fn english(&self) -> String {
1601 let mut entries = Vec::new();
1602 let mut previous: Option<SegmentKey> = None;
1603 for (key, grouping) in &self.groupings {
1604 if previous.is_some_and(|previous| previous.affinity == Affinity::SplitNoteGroupingA) {
1605 previous = Some(*key);
1606 continue;
1607 }
1608 entries.push(format!(
1609 "Measure {}, {} {}:\n{}\n===",
1610 key.measure,
1611 key.affinity.name(),
1612 key.ordinal + 1,
1613 grouping_english(grouping)
1614 ));
1615 previous = Some(*key);
1616 }
1617 [
1618 "---begin segment---".to_string(),
1619 "<music21.braille.segment BrailleSegment>".to_string(),
1620 entries.join("\n"),
1621 "---end segment---".to_string(),
1622 ]
1623 .join("\n")
1624 }
1625}
1626
1627fn grouping_english(grouping: &BrailleElementGrouping) -> String {
1630 let mut lines = Vec::new();
1631 let mut previous_was_voice = false;
1632 for element in &grouping.elements {
1633 if let StreamElement::Stream(voice) = &element.element
1634 && voice.kind() == StreamKind::Voice
1635 {
1636 if previous_was_voice {
1637 lines.push(format!("full inaccord {}", symbol("full_inaccord")));
1638 }
1639 for (index, inner) in voice.events().iter().enumerate() {
1640 lines.push(match element.voice_english.get(index) {
1641 Some(Some(english)) => english.join("\n"),
1642 _ => element_repr(inner.element()),
1643 });
1644 }
1645 previous_was_voice = true;
1646 } else {
1647 lines.push(match &element.english {
1648 Some(english) => english.join("\n"),
1649 None => element_repr(&element.element),
1650 });
1651 previous_was_voice = false;
1652 }
1653 }
1654 if grouping.num_repeats > 0 {
1655 lines.push(format!("** Grouping x {} **", grouping.num_repeats + 1));
1656 }
1657 if grouping.with_hyphen {
1658 lines.push(format!("music hyphen {}", symbol("music_hyphen")));
1659 }
1660 lines.join("\n")
1661}
1662
1663fn voice_english_of(
1666 voice: &Stream,
1667 leaf_base: usize,
1668 written: &BrailleElementGrouping,
1669) -> Vec<Option<Vec<String>>> {
1670 let mut leaf = leaf_base;
1671 voice
1672 .events()
1673 .iter()
1674 .map(|event| {
1675 let english = written
1676 .elements
1677 .iter()
1678 .find(|element| {
1679 element.leaf == leaf && !matches!(element.element, StreamElement::Stream(_))
1680 })
1681 .and_then(|element| element.english.clone());
1682 leaf += match event.element() {
1683 StreamElement::Stream(inner) => inner.leaves().len(),
1684 _ => 1,
1685 };
1686 english
1687 })
1688 .collect()
1689}
1690
1691fn heading_english(
1696 sharps: Option<Option<IntegerType>>,
1697 meter: Option<&TimeSignature>,
1698 tempo: Option<&crate::tempo::TempoText>,
1699 mark: Option<&crate::tempo::MetronomeMark>,
1700 line_length: usize,
1701) -> [Option<Vec<String>>; 4] {
1702 let mut out: [Option<Vec<String>>; 4] = [None, None, None, None];
1703 out[2] = tempo.map(|text| basic::tempo_to_braille(text, line_length).english);
1704 if sharps.is_some() || meter.is_some() || mark.is_some() {
1705 out[3] = mark.and_then(|found| {
1706 basic::metronome_mark_to_braille(found).map(|written| written.english)
1707 });
1708 let empty = meter.is_none() && sharps.is_none_or(|sharps| sharps == Some(0));
1709 if !empty {
1710 out[0] = sharps.map(|sharps| basic::key_sig_to_braille(sharps, None).english);
1711 out[1] = meter.map(|time| basic::time_sig_to_braille(time).english);
1712 }
1713 }
1714 out
1715}
1716
1717#[derive(Clone, Debug)]
1719struct SegmentWriter {
1720 text: BrailleText,
1721 keys: VecDeque<SegmentKey>,
1722 current: Option<SegmentKey>,
1723 previous: Option<SegmentKey>,
1724 last_note: Option<Pitch>,
1725}
1726
1727impl SegmentWriter {
1728 fn extract_measure_number(&mut self, segment: &BrailleSegment) -> Result<()> {
1729 let first = self
1730 .keys
1731 .front()
1732 .copied()
1733 .or_else(|| segment.groupings.keys().next().copied());
1734 let Some(first) = first else {
1735 return Ok(());
1736 };
1737 let mut number = number_to_braille(&first.measure.to_string(), true, false)?;
1738 if segment.begins_mid_measure {
1739 number.push_str(&symbol("dot"));
1740 }
1741 self.text.add_measure_number(&number)
1742 }
1743
1744 fn extract_heading(&mut self, segment: &mut BrailleSegment) -> Result<()> {
1745 let mut sharps: Option<Option<IntegerType>> = None;
1746 let mut meter: Option<TimeSignature> = None;
1747 let mut tempo: Option<crate::tempo::TempoText> = None;
1748 let mut mark: Option<crate::tempo::MetronomeMark> = None;
1749 let from_writer = !self.keys.is_empty();
1752 let mut keys: VecDeque<SegmentKey> = if from_writer {
1753 std::mem::take(&mut self.keys)
1754 } else {
1755 segment.groupings.keys().copied().collect()
1756 };
1757 let mut key_place: Option<(SegmentKey, usize)> = None;
1758 let mut meter_place: Option<(SegmentKey, usize)> = None;
1759 let mut tempo_place: Option<(SegmentKey, usize)> = None;
1760 let mut mark_place: Option<(SegmentKey, usize)> = None;
1761 while let Some(&key) = keys.front() {
1762 if key.affinity > Affinity::MetronomeMark {
1763 break;
1764 }
1765 keys.pop_front();
1766 let grouping = &segment.groupings[&key];
1767 match key.affinity {
1768 Affinity::Signature => {
1769 if grouping.elements.len() >= 2 {
1770 sharps = sharps_of(&grouping.elements[0].element);
1771 key_place = sharps.map(|_| (key, 0));
1772 if let StreamElement::TimeSignature(time) = &grouping.elements[1].element {
1773 meter = Some(time.clone());
1774 meter_place = Some((key, 1));
1775 }
1776 } else if let Some(only) = grouping.elements.first() {
1777 if let Some(found) = sharps_of(&only.element) {
1778 sharps = Some(found);
1779 key_place = Some((key, 0));
1780 } else if let StreamElement::TimeSignature(time) = &only.element {
1781 meter = Some(time.clone());
1782 meter_place = Some((key, 0));
1783 }
1784 }
1785 }
1786 Affinity::TempoText => {
1787 if let Some(StreamElement::TempoText(text)) =
1788 grouping.elements.first().map(|e| &e.element)
1789 {
1790 tempo = Some(text.clone());
1791 tempo_place = Some((key, 0));
1792 }
1793 }
1794 Affinity::MetronomeMark => {
1795 if let Some(StreamElement::MetronomeMark(found)) =
1796 grouping.elements.first().map(|e| &e.element)
1797 {
1798 mark = Some(found.clone());
1799 mark_place = Some((key, 0));
1800 }
1801 }
1802 _ => {}
1803 }
1804 }
1805 if from_writer {
1806 self.keys = keys;
1807 }
1808 if sharps.is_none() && meter.is_none() && tempo.is_none() && mark.is_none() {
1809 return Ok(());
1810 }
1811 let line_length = self.text.line_length;
1812 let heading = transcribe_heading(
1813 sharps,
1814 meter.as_ref(),
1815 tempo.as_ref(),
1816 mark.as_ref(),
1817 line_length,
1818 )?;
1819 let mut set = |place: Option<(SegmentKey, usize)>, english: Option<Vec<String>>| {
1823 if let (Some((key, index)), Some(english)) = (place, english) {
1824 segment
1825 .groupings
1826 .get_mut(&key)
1827 .expect("a grouping")
1828 .elements[index]
1829 .english = Some(english);
1830 }
1831 };
1832 if let Some(text) = &tempo {
1833 set(
1834 tempo_place,
1835 Some(basic::tempo_to_braille(text, line_length).english),
1836 );
1837 }
1838 if sharps.is_some() || meter.is_some() || mark.is_some() {
1839 if let Some(found) = &mark {
1840 set(
1841 mark_place,
1842 basic::metronome_mark_to_braille(found).map(|written| written.english),
1843 );
1844 }
1845 let empty = meter.is_none() && sharps.is_none_or(|sharps| sharps == Some(0));
1846 if !empty {
1847 if let Some(sharps) = sharps {
1848 set(
1849 key_place,
1850 Some(basic::key_sig_to_braille(sharps, None).english),
1851 );
1852 }
1853 if let Some(time) = &meter {
1854 set(meter_place, Some(basic::time_sig_to_braille(time).english));
1855 }
1856 }
1857 }
1858 self.text.add_heading(&heading)
1859 }
1860
1861 fn extract_inaccord_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
1862 let key = self.current.expect("a grouping being written");
1863 let grouping = segment.groupings[&key].clone();
1864 let mut pending_clef: Option<BrailleElement> = None;
1865 let mut seen_voice = false;
1866 let mut written: Vec<(usize, Vec<Option<Vec<String>>>)> = Vec::new();
1867 for element in &grouping.elements {
1868 if matches!(element.element, StreamElement::Clef(_)) {
1869 pending_clef = Some(element.clone());
1870 continue;
1871 }
1872 let StreamElement::Stream(voice) = &element.element else {
1873 continue;
1874 };
1875 let mut inner = extract_braille_elements(voice, element.leaf, &segment.contexts);
1876 if let Some(clef) = pending_clef.take() {
1877 inner.elements.insert(0, clef);
1878 }
1879 inner.descending_chords = grouping.descending_chords;
1880 inner.show_clef_signs = grouping.show_clef_signs;
1881 inner.upper_first_in_note_fingering = grouping.upper_first_in_note_fingering;
1882 let mut braille = if seen_voice {
1883 symbol("full_inaccord")
1884 } else {
1885 String::new()
1886 };
1887 braille.push_str(&transcribe_note_grouping(&mut inner, true));
1888 written.push((element.leaf, voice_english_of(voice, element.leaf, &inner)));
1889 self.text.add_inaccord(&braille)?;
1890 seen_voice = true;
1891 }
1892 let stored = segment.groupings.get_mut(&key).expect("a grouping");
1893 for (leaf, english) in written {
1894 if let Some(element) = stored.elements.iter_mut().find(|element| {
1895 element.leaf == leaf && matches!(element.element, StreamElement::Stream(_))
1896 }) {
1897 element.voice_english = english;
1898 }
1899 }
1900 Ok(())
1901 }
1902
1903 fn extract_long_expression_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
1904 let key = self.current.expect("a grouping being written");
1905 let grouping = segment.groupings.get_mut(&key).expect("a grouping");
1906 if let Some(first) = grouping.elements.first_mut()
1907 && let StreamElement::TextExpression(text) = &first.element
1908 {
1909 let written = text_expression_to_braille(text, true);
1910 first.english = Some(written.english);
1911 self.text.add_long_expression(&written.braille)?;
1912 }
1913 Ok(())
1914 }
1915
1916 fn show_leading_octave_from(
1917 &mut self,
1918 grouping: &BrailleElementGrouping,
1919 suppress: bool,
1920 ) -> bool {
1921 if let (Some(previous), Some(current)) = (self.previous, self.current)
1922 && (previous.affinity != Affinity::NoteGrouping
1923 || current.affinity != Affinity::NoteGrouping
1924 || (current.measure == previous.measure
1925 && current.ordinal == previous.ordinal + 1
1926 && current.hand == previous.hand))
1927 {
1928 self.last_note = None;
1929 }
1930 if suppress {
1931 return false;
1932 }
1933 let notes: Vec<&Pitch> = grouping
1934 .elements
1935 .iter()
1936 .filter_map(|element| match &element.element {
1937 StreamElement::Note(note) => Some(note.pitch()),
1938 _ => None,
1939 })
1940 .collect();
1941 let mut show = true;
1942 if let (Some(first), Some(last)) = (notes.first(), notes.last()) {
1943 if let Some(previous) = &self.last_note {
1944 show = basic::show_octave_with_note(Some(previous), first);
1945 }
1946 self.last_note = Some((*last).clone());
1947 }
1948 show
1949 }
1950
1951 fn needs_split_to_fit(&mut self, braille: &str) -> bool {
1952 self.needs_split_for(braille.chars().count())
1953 }
1954
1955 fn needs_split_for(&mut self, cells: usize) -> bool {
1956 let quarter = self.text.line_length / 4;
1957 let left = self.text.line_length - self.text.current_line().text_location;
1958 left > quarter && cells > quarter
1959 }
1960
1961 fn extract_note_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
1962 let key = self.current.expect("a grouping being written");
1963 let suppress = segment.options.suppress_octave_marks;
1964 let mut grouping = segment.groupings[&key].clone();
1965 let show_leading = self.show_leading_octave_from(&grouping, suppress);
1966 let mut transcriber = NoteGroupingTranscriber {
1967 show_leading_octave: show_leading,
1968 ..NoteGroupingTranscriber::default()
1969 };
1970 let mut braille = transcriber.transcribe_group(&mut grouping);
1971 let add_space = self
1972 .text
1973 .optional_add_keyboard_symbols_and_dots(Some(&braille))?;
1974 if self.text.current_line().can_append(&braille, add_space) {
1975 self.text.current_line().append(&braille, add_space)?;
1976 } else {
1977 let mut split = false;
1978 if self.needs_split_to_fit(&braille)
1979 && let Some((first, second)) =
1980 self.split_and_transcribe(segment, &mut grouping, show_leading, add_space)?
1981 {
1982 split = true;
1983 self.text.current_line().append(&first, add_space)?;
1984 self.text.add_to_new_line(&second)?;
1985 }
1986 if !split {
1987 if !show_leading && !suppress {
1988 transcriber.show_leading_octave = true;
1989 braille = transcriber.transcribe_group(&mut grouping);
1990 }
1991 self.text.current_line().last_hyphen_to_space();
1992 self.text.add_to_new_line(&braille)?;
1993 }
1994 }
1995 segment.groupings.insert(key, grouping.clone());
1996 self.add_repeat_symbols(grouping.num_repeats)?;
1997 Ok(())
1998 }
1999
2000 fn split_and_transcribe(
2001 &mut self,
2002 segment: &mut BrailleSegment,
2003 grouping: &mut BrailleElementGrouping,
2004 show_leading_octave_on_first: bool,
2005 add_space_to_first: bool,
2006 ) -> Result<Option<(String, String)>> {
2007 let mut transcriber = NoteGroupingTranscriber::default();
2008 let mut offset = 0;
2009 let mut halves = None;
2010 let mut first_braille = String::new();
2011 while offset < 10 {
2012 let Some(((mut first, sources), second)) = split_with_sources(grouping, offset)? else {
2013 return Ok(None);
2014 };
2015 transcriber.show_leading_octave = show_leading_octave_on_first;
2016 first.with_hyphen = true;
2017 first_braille = transcriber.transcribe_group(&mut first);
2018 for (element, source) in first.elements.iter().zip(sources) {
2021 grouping.elements[source].english = element.english.clone();
2022 }
2023 let fits = self
2024 .text
2025 .current_line()
2026 .can_append(&first_braille, add_space_to_first);
2027 halves = Some((first, second));
2028 if fits {
2029 break;
2030 }
2031 offset += 1;
2032 }
2033 let (first, (mut second, second_sources)) = halves.expect("at least one split is tried");
2034 transcriber.show_leading_octave = !segment.options.suppress_octave_marks;
2035 let second_braille = transcriber.transcribe_group(&mut second);
2036 for (element, source) in second.elements.iter().zip(second_sources) {
2037 grouping.elements[source].english = element.english.clone();
2038 }
2039 let key = self.current.expect("a grouping being written");
2040 segment.groupings.insert(
2041 SegmentKey {
2042 affinity: Affinity::SplitNoteGroupingA,
2043 ..key
2044 },
2045 first,
2046 );
2047 segment.groupings.insert(
2048 SegmentKey {
2049 affinity: Affinity::SplitNoteGroupingB,
2050 ..key
2051 },
2052 second,
2053 );
2054 Ok(Some((first_braille, second_braille)))
2055 }
2056
2057 fn add_repeat_symbols(&mut self, times: usize) -> Result<()> {
2058 if (1..3).contains(×) {
2059 for _ in 0..times {
2060 self.text.add_signatures(&symbol("repeat"))?;
2061 }
2062 } else if times >= 3 {
2063 let number = number_to_braille(×.to_string(), true, false)?;
2064 self.text.add_signatures(&(symbol("repeat") + &number))?;
2065 self.last_note = None;
2066 }
2067 Ok(())
2068 }
2069
2070 fn extract_signature_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
2071 let key = self.current.expect("a grouping being written");
2072 let grouping = segment.groupings.get_mut(&key).expect("a grouping");
2073 let (key_index, meter_index) = match grouping.elements.len() {
2074 0 => (None, None),
2075 1 => {
2076 if sharps_of(&grouping.elements[0].element).is_some() {
2077 (Some(0), None)
2078 } else {
2079 (None, Some(0))
2080 }
2081 }
2082 _ => (Some(0), Some(1)),
2083 };
2084 let sharps = key_index.and_then(|index| sharps_of(&grouping.elements[index].element));
2085 let outgoing = if segment.options.cancel_outgoing_key_sig {
2086 key_index.and_then(|index| grouping.elements[index].outgoing_key)
2087 } else {
2088 None
2089 };
2090 let meter = meter_index.and_then(|index| match &grouping.elements[index].element {
2091 StreamElement::TimeSignature(meter) => Some(meter.clone()),
2092 _ => None,
2093 });
2094 let written = transcribe_signatures(sharps, meter.as_ref(), outgoing);
2095 if let Some(index) = key_index
2096 && let Some(sharps) = sharps
2097 {
2098 grouping.elements[index].english =
2099 Some(basic::key_sig_to_braille(sharps, outgoing).english);
2100 }
2101 if let (Some(index), Some(meter)) = (meter_index, &meter) {
2102 grouping.elements[index].english = Some(basic::time_sig_to_braille(meter).english);
2103 }
2104 if !written.braille.is_empty() {
2105 self.text.add_signatures(&written.braille)?;
2106 }
2107 Ok(())
2108 }
2109
2110 fn extract_tempo_text_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
2111 let current = self.current.expect("a grouping being written");
2112 self.keys.push_front(current);
2113 let previous = self
2114 .previous
2115 .ok_or_else(|| Error::Notation("tempo text with no grouping before it".to_string()))?;
2116 if previous.affinity == Affinity::Signature {
2117 self.keys.push_front(previous);
2118 }
2119 self.extract_heading(segment)?;
2120 self.extract_measure_number(segment)
2121 }
2122}
2123
2124#[derive(Clone, Debug)]
2132pub struct SegmentTranscriber {
2133 writer: SegmentWriter,
2134}
2135
2136impl SegmentTranscriber {
2137 pub fn new(text: BrailleText) -> Self {
2139 Self {
2140 writer: SegmentWriter {
2141 text,
2142 keys: VecDeque::new(),
2143 current: None,
2144 previous: None,
2145 last_note: None,
2146 },
2147 }
2148 }
2149
2150 pub fn text(&self) -> &BrailleText {
2152 &self.writer.text
2153 }
2154
2155 pub fn text_mut(&mut self) -> &mut BrailleText {
2157 &mut self.writer.text
2158 }
2159
2160 pub fn into_text(self) -> BrailleText {
2162 self.writer.text
2163 }
2164
2165 pub fn keys_mut(&mut self) -> &mut VecDeque<SegmentKey> {
2167 &mut self.writer.keys
2168 }
2169
2170 pub fn current(&self) -> Option<SegmentKey> {
2172 self.writer.current
2173 }
2174
2175 pub fn set_current(&mut self, key: Option<SegmentKey>) {
2177 self.writer.current = key;
2178 }
2179
2180 pub fn previous(&self) -> Option<SegmentKey> {
2182 self.writer.previous
2183 }
2184
2185 pub fn set_previous(&mut self, key: Option<SegmentKey>) {
2187 self.writer.previous = key;
2188 }
2189
2190 pub fn last_note(&self) -> Option<&Pitch> {
2192 self.writer.last_note.as_ref()
2193 }
2194
2195 pub fn set_last_note(&mut self, pitch: Option<Pitch>) {
2197 self.writer.last_note = pitch;
2198 }
2199
2200 pub fn transcribe(&mut self, segment: &mut BrailleSegment) -> Result<()> {
2207 let writer = &mut self.writer;
2208 writer.keys = segment.groupings.keys().copied().collect();
2209 if segment.options.show_heading {
2210 writer.extract_heading(segment)?;
2211 }
2212 if segment.options.show_first_measure_number {
2213 writer.extract_measure_number(segment)?;
2214 }
2215 if segment.options.dummy_rest_length.is_some() {
2216 self.add_dummy_rests(segment)?;
2217 }
2218 let writer = &mut self.writer;
2219 writer.previous = None;
2220 while let Some(key) = writer.keys.pop_front() {
2221 writer.current = Some(key);
2222 match key.affinity {
2223 Affinity::NoteGrouping => writer.extract_note_grouping(segment)?,
2224 Affinity::Signature => writer.extract_signature_grouping(segment)?,
2225 Affinity::LongTextExpression => writer.extract_long_expression_grouping(segment)?,
2226 Affinity::Inaccord => writer.extract_inaccord_grouping(segment)?,
2227 Affinity::TempoText => writer.extract_tempo_text_grouping(segment)?,
2228 _ => {}
2229 }
2230 writer.previous = writer.current;
2231 }
2232 Ok(())
2233 }
2234
2235 pub fn extract_heading(&mut self, segment: &mut BrailleSegment) -> Result<()> {
2242 self.writer.extract_heading(segment)
2243 }
2244
2245 pub fn extract_measure_number(&mut self, segment: &BrailleSegment) -> Result<()> {
2252 self.writer.extract_measure_number(segment)
2253 }
2254
2255 pub fn add_dummy_rests(&mut self, segment: &BrailleSegment) -> Result<()> {
2262 let length = segment.options.dummy_rest_length.unwrap_or(0);
2263 let dummy = lookup::rest("dummy").unwrap_or_default().repeat(length);
2264 self.writer.text.add_signatures(&dummy)
2265 }
2266
2267 pub fn extract_note_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
2274 self.require_current(segment)?;
2275 self.writer.extract_note_grouping(segment)
2276 }
2277
2278 pub fn extract_signature_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
2285 self.require_current(segment)?;
2286 self.writer.extract_signature_grouping(segment)
2287 }
2288
2289 pub fn extract_long_expression_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
2296 self.require_current(segment)?;
2297 self.writer.extract_long_expression_grouping(segment)
2298 }
2299
2300 pub fn extract_inaccord_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
2307 self.require_current(segment)?;
2308 self.writer.extract_inaccord_grouping(segment)
2309 }
2310
2311 pub fn extract_tempo_text_grouping(&mut self, segment: &mut BrailleSegment) -> Result<()> {
2318 self.require_current(segment)?;
2319 self.writer.extract_tempo_text_grouping(segment)
2320 }
2321
2322 pub fn show_leading_octave_from(
2325 &mut self,
2326 grouping: &BrailleElementGrouping,
2327 suppress_octave_marks: bool,
2328 ) -> bool {
2329 self.writer
2330 .show_leading_octave_from(grouping, suppress_octave_marks)
2331 }
2332
2333 pub fn needs_split_to_fit(&mut self, braille: &str) -> bool {
2336 self.writer.needs_split_to_fit(braille)
2337 }
2338
2339 pub fn needs_split_for(&mut self, cells: usize) -> bool {
2341 self.writer.needs_split_for(cells)
2342 }
2343
2344 pub fn split_and_transcribe(
2352 &mut self,
2353 segment: &mut BrailleSegment,
2354 show_leading_octave_on_first: bool,
2355 add_space_to_first: bool,
2356 ) -> Result<Option<(String, String)>> {
2357 let key = self.require_current(segment)?;
2358 let mut grouping = segment.groupings[&key].clone();
2359 let split = self.writer.split_and_transcribe(
2360 segment,
2361 &mut grouping,
2362 show_leading_octave_on_first,
2363 add_space_to_first,
2364 )?;
2365 segment.groupings.insert(key, grouping);
2366 Ok(split)
2367 }
2368
2369 pub fn add_repeat_symbols(&mut self, times: usize) -> Result<()> {
2376 self.writer.add_repeat_symbols(times)
2377 }
2378
2379 fn require_current(&self, segment: &BrailleSegment) -> Result<SegmentKey> {
2381 self.writer
2382 .current
2383 .filter(|key| segment.groupings.contains_key(key))
2384 .ok_or_else(|| {
2385 Error::Notation("no grouping of the segment is being written".to_string())
2386 })
2387 }
2388}
2389
2390pub type KeyPair = (Option<SegmentKey>, Option<SegmentKey>);
2393
2394#[derive(Clone, Debug)]
2397pub struct BrailleGrandSegment {
2398 pub groupings: BTreeMap<SegmentKey, BrailleElementGrouping>,
2400 pub line_length: usize,
2402 pub right_contexts: Vec<NoteContext>,
2404 pub left_contexts: Vec<NoteContext>,
2406}
2407
2408impl BrailleGrandSegment {
2409 pub fn combined_keys(&self) -> Vec<KeyPair> {
2412 let mut keys: Vec<SegmentKey> = self.groupings.keys().copied().collect();
2413 keys.sort_by_key(|key| {
2414 (
2415 key.measure,
2416 key.ordinal,
2417 key.affinity,
2418 if key.hand == Some(Hand::Right) { -1 } else { 1 },
2419 )
2420 });
2421 let matches = |one: &SegmentKey, other: &SegmentKey| {
2422 one.measure == other.measure
2423 && one.ordinal == other.ordinal
2424 && one.affinity == other.affinity
2425 };
2426 let mut out = Vec::new();
2427 let mut stored_right: Option<SegmentKey> = None;
2428 let mut stored_left: Option<SegmentKey> = None;
2429 for key in keys {
2430 match key.hand {
2431 Some(Hand::Right) => {
2432 if let Some(left) = stored_left.take() {
2433 if matches(&key, &left)
2434 || (key.affinity == Affinity::NoteGrouping
2435 && matches(
2436 &SegmentKey {
2437 affinity: Affinity::Inaccord,
2438 ..key
2439 },
2440 &left,
2441 ))
2442 {
2443 out.push((Some(key), Some(left)));
2444 } else {
2445 out.push((None, Some(left)));
2446 stored_right = Some(key);
2447 }
2448 } else {
2449 if let Some(right) = stored_right {
2450 out.push((Some(right), None));
2451 }
2452 stored_right = Some(key);
2453 }
2454 }
2455 Some(Hand::Left) => {
2456 if let Some(right) = stored_right.take() {
2457 if matches(&key, &right) {
2458 out.push((Some(right), Some(key)));
2459 } else if right.affinity < Affinity::Inaccord {
2460 out.push((Some(right), None));
2461 out.push((None, Some(key)));
2462 } else {
2463 out.push((Some(right), None));
2464 stored_left = Some(key);
2465 }
2466 } else {
2467 if let Some(left) = stored_left {
2468 out.push((None, Some(left)));
2469 }
2470 stored_left = Some(key);
2471 }
2472 }
2473 None => {}
2474 }
2475 }
2476 if let Some(right) = stored_right {
2477 out.push((Some(right), None));
2478 }
2479 if let Some(left) = stored_left {
2480 out.push((None, Some(left)));
2481 }
2482 out
2483 }
2484
2485 pub fn transcribe(&mut self) -> Result<String> {
2492 let mut keyboard = BrailleKeyboard::new(self.line_length);
2493 self.transcribe_into(&mut keyboard)?;
2494 Ok(keyboard.text.render())
2495 }
2496
2497 pub fn transcribe_into(&mut self, keyboard: &mut BrailleKeyboard) -> Result<()> {
2504 let mut pairs: VecDeque<KeyPair> = self.combined_keys().into();
2505 let Some(last) = pairs.back().copied() else {
2506 return Ok(());
2507 };
2508 let highest = last.0.or(last.1).map_or(0, |key| key.measure);
2509 keyboard.highest_measure_number_length = highest.to_string().chars().count();
2510 self.extract_heading(&mut pairs, keyboard)?;
2511 while let Some(pair) = pairs.pop_front() {
2512 let writes = pair.0.is_some_and(|key| key.affinity >= Affinity::Inaccord)
2513 || pair.1.is_some_and(|key| key.affinity >= Affinity::Inaccord);
2514 if writes {
2515 self.extract_note_grouping(pair, keyboard)?;
2516 }
2517 }
2518 Ok(())
2519 }
2520
2521 pub fn extract_heading(
2529 &mut self,
2530 pairs: &mut VecDeque<KeyPair>,
2531 keyboard: &mut BrailleKeyboard,
2532 ) -> Result<()> {
2533 let mut sharps: Option<Option<IntegerType>> = None;
2534 let mut meter: Option<TimeSignature> = None;
2535 let mut tempo: Option<crate::tempo::TempoText> = None;
2536 let mut mark: Option<crate::tempo::MetronomeMark> = None;
2537 let mut places: [Option<(SegmentKey, usize)>; 4] = [None; 4];
2538 while let Some(&(right, left)) = pairs.front() {
2539 let Some(use_key) = right.or(left) else {
2540 break;
2541 };
2542 if use_key.affinity > Affinity::MetronomeMark {
2543 break;
2544 }
2545 pairs.pop_front();
2546 let grouping = &self.groupings[&use_key];
2547 match use_key.affinity {
2548 Affinity::Signature => {
2549 for (index, held) in grouping.elements.iter().enumerate() {
2552 if sharps.is_none()
2553 && let Some(found) = sharps_of(&held.element)
2554 {
2555 sharps = Some(found);
2556 places[0] = found.map(|_| (use_key, index));
2557 } else if meter.is_none()
2558 && let StreamElement::TimeSignature(time) = &held.element
2559 {
2560 meter = Some(time.clone());
2561 places[1] = Some((use_key, index));
2562 }
2563 }
2564 }
2565 Affinity::TempoText => {
2566 if let Some(StreamElement::TempoText(text)) =
2567 grouping.elements.first().map(|e| &e.element)
2568 {
2569 tempo = Some(text.clone());
2570 places[2] = Some((use_key, 0));
2571 }
2572 }
2573 Affinity::MetronomeMark => {
2574 if let Some(StreamElement::MetronomeMark(found)) =
2575 grouping.elements.first().map(|e| &e.element)
2576 {
2577 mark = Some(found.clone());
2578 places[3] = Some((use_key, 0));
2579 }
2580 }
2581 _ => {}
2582 }
2583 }
2584 if let Ok(heading) = transcribe_heading(
2585 sharps,
2586 meter.as_ref(),
2587 tempo.as_ref(),
2588 mark.as_ref(),
2589 self.line_length,
2590 ) {
2591 let english = heading_english(
2592 sharps,
2593 meter.as_ref(),
2594 tempo.as_ref(),
2595 mark.as_ref(),
2596 self.line_length,
2597 );
2598 for (place, english) in places.into_iter().zip(english) {
2599 if let (Some((key, index)), Some(english)) = (place, english) {
2600 self.groupings.get_mut(&key).expect("a grouping").elements[index].english =
2601 Some(english);
2602 }
2603 }
2604 keyboard.text.add_heading(&heading)?;
2605 }
2606 Ok(())
2607 }
2608
2609 pub fn extract_note_grouping(
2616 &mut self,
2617 (right, left): KeyPair,
2618 keyboard: &mut BrailleKeyboard,
2619 ) -> Result<()> {
2620 let Some(measure) = right.or(left).map(|key| key.measure) else {
2621 return Err(Error::Notation(
2622 "Measure must be defined for leftKey or rightKey".to_string(),
2623 ));
2624 };
2625 let number = number_to_braille(&measure.to_string(), false, false)?;
2626 let right_braille = self.braille_from_key(right)?;
2627 let left_braille = self.braille_from_key(left)?;
2628 keyboard.add_note_groupings(&number, &right_braille, &left_braille)
2629 }
2630
2631 fn braille_from_key(&mut self, key: Option<SegmentKey>) -> Result<String> {
2632 let Some(key) = key else {
2633 return Ok(String::new());
2634 };
2635 let contexts = if key.hand == Some(Hand::Left) {
2636 &self.left_contexts
2637 } else {
2638 &self.right_contexts
2639 };
2640 let grouping = self.groupings.get_mut(&key).expect("a grouping");
2641 if key.affinity == Affinity::Inaccord {
2642 let mut voices = Vec::new();
2643 let (descending, clefs, upper) = (
2644 grouping.descending_chords,
2645 grouping.show_clef_signs,
2646 grouping.upper_first_in_note_fingering,
2647 );
2648 for element in &mut grouping.elements {
2649 let StreamElement::Stream(voice) = &element.element else {
2650 continue;
2651 };
2652 let mut inner = extract_braille_elements(voice, element.leaf, contexts);
2653 inner.descending_chords = descending;
2654 inner.show_clef_signs = clefs;
2655 inner.upper_first_in_note_fingering = upper;
2656 voices.push(transcribe_note_grouping(&mut inner, true));
2657 element.voice_english = voice_english_of(voice, element.leaf, &inner);
2658 }
2659 Ok(voices.join(&symbol("full_inaccord")))
2660 } else {
2661 Ok(transcribe_note_grouping(grouping, true))
2662 }
2663 }
2664
2665 pub fn english(&self) -> String {
2668 let mut pairs = String::new();
2669 for (right, left) in self.combined_keys() {
2670 let right_full = right.map_or_else(String::new, |key| {
2671 format!(
2672 "Measure {} Right, {} {}:\n{}",
2673 key.measure,
2674 key.affinity.name(),
2675 key.ordinal + 1,
2676 grouping_english(&self.groupings[&key])
2677 )
2678 });
2679 let left_full = left.map_or_else(String::new, |key| {
2680 format!(
2681 "\nMeasure {} Left, {} {}:\n{}",
2682 key.measure,
2683 key.affinity.name(),
2684 key.ordinal + 1,
2685 grouping_english(&self.groupings[&key])
2686 )
2687 });
2688 pairs.push_str(&[right_full, left_full, "====\n".to_string()].join("\n"));
2689 }
2690 [
2691 "---begin grand segment---".to_string(),
2692 "<music21.braille.segment BrailleGrandSegment>\n===".to_string(),
2693 pairs,
2694 "---end grand segment---".to_string(),
2695 ]
2696 .join("\n")
2697 }
2698}
2699
2700#[cfg(test)]
2701mod tests {
2702 use super::*;
2703 use crate::chord::Chord;
2704
2705 fn element(element: StreamElement) -> BrailleElement {
2706 BrailleElement {
2707 element,
2708 offset: 0.0,
2709 affinity: Affinity::NoteGrouping,
2710 sort_order: 0,
2711 english: None,
2712 context: NoteContext::default(),
2713 outgoing_key: None,
2714 leaf: 0,
2715 voice_english: Vec::new(),
2716 }
2717 }
2718
2719 #[test]
2722 fn octave_after_exception_sign() -> Result<()> {
2723 let short = Duration::new(1.0 / 64.0)?;
2724 let mut short_note = Note::from_name("E4")?;
2725 short_note.set_duration(short.clone());
2726 let short_chord = Chord::new("E4 G4")?.with_duration(short);
2727 for unbrailled in [
2728 StreamElement::Note(short_note),
2729 StreamElement::Chord(short_chord),
2730 ] {
2731 for (show_leading_octave, expected) in [(true, "⠐⠹⠜⠦⠐⠹"), (false, "⠹⠜⠦⠐⠹")]
2732 {
2733 let mut grouping = BrailleElementGrouping {
2734 elements: vec![
2735 element(StreamElement::Note(Note::from_name("C4")?)),
2736 element(unbrailled.clone()),
2737 element(StreamElement::Note(Note::from_name("C4")?)),
2738 ],
2739 ..BrailleElementGrouping::default()
2740 };
2741 let mut transcriber = NoteGroupingTranscriber {
2742 show_leading_octave,
2743 ..NoteGroupingTranscriber::default()
2744 };
2745 assert_eq!(transcriber.transcribe_group(&mut grouping), expected);
2746 }
2747 }
2748 Ok(())
2749 }
2750}