Skip to main content

music21_rs/braille/
segment.rs

1//! A part cut into segments of braille music, measure by measure and
2//! grouping by grouping: music21's `braille.segment` and
3//! `braille.noteGrouping`.
4//!
5//! music21 marks the notes of the part it transcribes with attributes for
6//! the time it transcribes them -- the slurs over them, how their beams
7//! read, a full measure's rest -- and writes each sign's English onto the
8//! element. Here each element of a segment is a [`BrailleElement`] that
9//! carries those beside a copy of the element.
10
11use 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/// Which kind of grouping an element goes in, in the order a measure's
33/// groupings are written: music21's `Affinity`.
34#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub enum Affinity {
36    /// Key and time signatures.
37    Signature = 3,
38    /// Tempo words.
39    TempoText = 4,
40    /// A metronome mark.
41    MetronomeMark = 5,
42    /// Text of more than one word.
43    LongTextExpression = 6,
44    /// Voices written in accord.
45    Inaccord = 7,
46    /// The first half of a group of notes split to fit a line.
47    SplitNoteGroupingA = 8,
48    /// Notes, rests, chords, dynamics, clefs, short texts.
49    NoteGrouping = 9,
50    /// The second half of a split group, and barlines.
51    SplitNoteGroupingB = 10,
52}
53
54impl Affinity {
55    /// music21's name for a grouping of this kind.
56    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/// The hand a keyboard part is played by.
71#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
72pub enum Hand {
73    /// The left hand: music21's `'left'`.
74    Left,
75    /// The right hand: music21's `'right'`.
76    Right,
77}
78
79/// Where a grouping stands: its measure, its place among the measure's
80/// groupings, its kind and its hand: music21's `SegmentKey`.
81#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
82pub struct SegmentKey {
83    /// The measure's number.
84    pub measure: IntegerType,
85    /// Which run of groupings in the measure, counting from nought.
86    pub ordinal: usize,
87    /// The kind of grouping.
88    pub affinity: Affinity,
89    /// The hand, at a keyboard.
90    pub hand: Option<Hand>,
91}
92
93/// An element of a segment with what music21 keeps on it while
94/// transcribing: where it stands, its affinity, how it sorts, its English,
95/// its note context, and a key signature's predecessor.
96#[derive(Clone, Debug)]
97pub struct BrailleElement {
98    /// A copy of the element, changed as music21 changes it.
99    pub element: StreamElement,
100    /// Its offset in its measure or voice.
101    pub offset: FloatType,
102    /// Its affinity.
103    pub affinity: Affinity,
104    /// How it sorts among elements at its offset: music21's
105    /// `classSortOrder` as braille sets it.
106    pub sort_order: i32,
107    /// Its English: music21's `editorial.brailleEnglish`.
108    pub english: Option<Vec<String>>,
109    /// Its slurs and beams, for a note.
110    pub context: NoteContext,
111    /// The key signature before it, for a key signature: music21's
112    /// `outgoingKeySig`.
113    pub outgoing_key: Option<Option<IntegerType>>,
114    /// Where its leaves start in the part, to find what music21 marked on
115    /// it.
116    pub leaf: usize,
117    /// For a voice, the English of each element it holds once written,
118    /// nothing for one not written.
119    pub voice_english: Vec<Option<Vec<String>>>,
120}
121
122/// A grouping of elements written together: music21's
123/// `BrailleElementGrouping`.
124#[derive(Clone, Debug)]
125pub struct BrailleElementGrouping {
126    /// The elements.
127    pub elements: Vec<BrailleElement>,
128    /// The key signature in force, as sharps.
129    pub key_signature: Option<IntegerType>,
130    /// The time signature in force.
131    pub time_signature: TimeSignature,
132    /// Whether chords are written from the top down; nothing reads as
133    /// upward, as in music21.
134    pub descending_chords: Option<bool>,
135    /// Whether clefs are written.
136    pub show_clef_signs: bool,
137    /// Whether a fingering's upper choice is written first.
138    pub upper_first_in_note_fingering: bool,
139    /// Whether the grouping ends with a music hyphen.
140    pub with_hyphen: bool,
141    /// How many times it is repeated after itself.
142    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
160/// music21's `repr` of an element, as a segment's English shows one it has
161/// not transcribed.
162pub(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            // A key's `str` is its tonic and mode alone.
222            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
288/// A pitch's name as music21 spells it, a flat as `-`.
289fn 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
303/// How music21 describes a key signature of so many sharps.
304fn 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
314/// A number as music21 keeps it, a whole one as an integer.
315fn 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
323/// A text as Python's `repr` writes it, in single quotes unless it holds
324/// one.
325fn 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
353/// The affinity and sort order braille gives an element, or nothing for one
354/// it cannot write: music21's `setAffinityCode`.
355fn 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
382/// Whether music21 counts an element a `GeneralNote`.
383fn 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
395/// Whether music21 counts an element a `NotRest`: a stream's `notes`.
396fn 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
407/// A measure's or voice's elements as music21 iterates it, each with its
408/// offset and where its leaves start in the part: its left barline, what
409/// it holds, its right barline at its end.
410fn 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
435/// The elements of a measure or voice braille writes, clefs before the
436/// note they stand before, sorted by offset and braille's sort order:
437/// music21's `extractBrailleElements`.
438pub 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
508/// The type of a note's first beam, as music21's `beams.getByNumber(1)`
509/// reads it.
510fn 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
524/// The whole beat a place falls in, as music21's `int(element.beat)`.
525fn 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
531/// Marks the notes of a measure that begin and continue a beamed group of
532/// three or more notes of one value shorter than an eighth, which braille
533/// writes as eighths after the first: music21's `prepareBeamedNotes`. The
534/// marks go in `contexts`, by element.
535pub 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    // The notes, and the notes and rests, by place in `items`.
541    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    // music21 compares lengths as exact fractions; a sixth summed one way and
562    // another differs as a float.
563    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            // music21 reads the note before the first as the last of the
606            // measure, as Python indexes a list from its end.
607            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/// The options music21's translation takes, as a segment reads them.
623#[derive(Clone, Copy, Debug, PartialEq, Eq)]
624pub struct SegmentOptions {
625    /// Whether a key signature cancels the one before it.
626    pub cancel_outgoing_key_sig: bool,
627    /// Whether chords are written from the top down; nothing reads as
628    /// upward until a clef says otherwise.
629    pub descending_chords: Option<bool>,
630    /// How many dummy rests open each segment.
631    pub dummy_rest_length: Option<usize>,
632    /// How many cells a line holds.
633    pub max_line_length: usize,
634    /// Whether clefs are written.
635    pub show_clef_signs: bool,
636    /// Whether a segment starts with its first measure's number.
637    pub show_first_measure_number: bool,
638    /// Which hand's sign starts the lines.
639    pub show_hand: Option<Hand>,
640    /// Whether a segment starts with a heading.
641    pub show_heading: bool,
642    /// Whether a long slur and a tie on one note are both written.
643    pub show_long_slurs_and_ties_together: Option<bool>,
644    /// Whether a short slur and a tie on one note are both written.
645    pub show_short_slurs_and_ties_together: bool,
646    /// Whether a long phrase's slur is written with brackets rather than
647    /// doubled.
648    pub slur_long_phrase_with_brackets: bool,
649    /// Whether octave marks are left out.
650    pub suppress_octave_marks: bool,
651    /// Whether a fingering's upper choice is written first.
652    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
675/// The most notes a slur written after each note covers: music21's
676/// `SEGMENT_MAXNOTESFORSHORTSLUR`.
677const MAX_NOTES_FOR_SHORT_SLUR: usize = 4;
678
679/// The most elements a segment holds before a double or final barline
680/// ends it: music21's `MAX_ELEMENTS_IN_SEGMENT`.
681const MAX_ELEMENTS_IN_SEGMENT: usize = 48;
682
683/// The slurs of a part as each note writes them, by leaf: short slurs after
684/// each note, long ones with brackets or doubled at their ends: music21's
685/// `prepareSlurredNotes`.
686pub 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    // The part's notes in music21's flattened order, by leaf.
696    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/// A part cut into segments of groupings, ready to transcribe: music21's
764/// `BrailleSegment`.
765#[derive(Clone, Debug)]
766pub struct BrailleSegment {
767    /// The groupings, by key.
768    pub groupings: BTreeMap<SegmentKey, BrailleElementGrouping>,
769    /// The options it is written with.
770    pub options: SegmentOptions,
771    /// Whether chords are written from the top down, as the clefs met so
772    /// far say.
773    pub descending_chords: Option<bool>,
774    /// Whether the segment ends in the middle of a measure.
775    pub end_hyphen: bool,
776    /// Whether it begins in the middle of a measure.
777    pub begins_mid_measure: bool,
778    /// What music21 marked on each of the part's leaves.
779    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
795/// A key signature's sharps, as braille reads it.
796fn 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
804/// The part's measures with where each one's leaves start.
805fn 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
822/// The meter standing at each measure of a part, as music21 finds a
823/// measure's context: the last a measure stated, or one in the part before
824/// it.
825fn 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
862/// A part's measures cut into segments of groupings: music21's
863/// `getRawSegments`. A segment ends after a double or final barline once it
864/// holds more than 48 elements.
865pub 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
953/// Whether two elements are equal as music21 compares them.
954fn elements_equal(left: &BrailleElement, right: &BrailleElement) -> bool {
955    super::equality::equal(&left.element, &right.element)
956}
957
958/// Whether two groupings hold equal elements: music21's
959/// `areGroupingsIdentical`.
960fn 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    /// Repeated measures counted, key signatures given their predecessors,
970    /// full measures' rests marked and each grouping told the signatures
971    /// and options in force: music21's `addGroupingAttributes`.
972    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    /// Runs of three or more notes with one articulation written with it
1062    /// doubled on the first and once on the last, and a staccato or tenuto
1063    /// over a tie written as a short slur: music21's `fixArticulations`.
1064    pub fn fix_articulations(&mut self) {
1065        // music21's `consolidate`: each run of note groupings with nothing
1066        // else between, as one.
1067        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, &notes, 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 &notes[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 &notes[start + 1..start + sequential] {
1165            let articulations = self.note_mut(place).articulations_mut();
1166            // music21 removes while iterating, which skips the element after
1167            // each one removed.
1168            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
1179/// A part's segments, each told its options, its groupings their
1180/// attributes and its articulations fixed: music21's `findSegments`.
1181pub 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/// Writes the elements of a grouping one after another, octaves where
1196/// needed: music21's `NoteGroupingTranscriber`.
1197#[derive(Clone, Debug)]
1198pub struct NoteGroupingTranscriber {
1199    /// Whether the first note shows its octave.
1200    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    /// A grouping in braille, its elements' English written onto them, and
1219    /// a music hyphen where it ends with one: music21's `transcribeGroup`.
1220    ///
1221    /// # Errors
1222    ///
1223    /// Empty text before something dotted after it, which music21 cannot
1224    /// look at the end of.
1225    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    /// A dot after a dynamic, a clef written out, or text not ending in a
1325    /// full stop, before the next element unless that is itself a dynamic
1326    /// or text: music21's `optionallyAddDotToPrevious`.
1327    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
1371/// A grouping in braille from a fresh transcriber: music21's
1372/// `transcribeNoteGrouping`.
1373///
1374/// # Errors
1375///
1376/// What [`NoteGroupingTranscriber::transcribe_group`] refuses.
1377pub 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
1388/// A measure of groupings split in two at the middle of its bar, or nearer
1389/// its start by `beat_division_offset` divisions of a beat, tied notes kept
1390/// with the first half and each half beamed again: music21's
1391/// `splitNoteGrouping`.
1392///
1393/// # Errors
1394///
1395/// An offset past the beat's divisions, or a meter that cannot be halved.
1396pub 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
1404/// A grouping and which of the split grouping's elements it holds.
1405type Half = (BrailleElementGrouping, Vec<usize>);
1406
1407/// An element of a half rebeamed: where it came from, as beamed, and its
1408/// marks for beginning and continuing a beamed group.
1409type Rebeamed = (usize, StreamElement, (bool, bool));
1410
1411/// [`split_note_grouping`], each half with where its elements came from.
1412fn 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            // music21's `BrailleSegmentException`, which the segment takes
1422            // as no split.
1423            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    // music21 iterates the measure it builds from the grouping, sorted by
1441    // offset and sort order.
1442    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    // Tied notes at the start of the second half go with the first, put
1466    // after what it holds.
1467    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    // Each half beamed as a measure of its own, filled out with a rest, and
1489    // its beamed notes marked again.
1490    let rebeam =
1491        |indices: &[usize], rest_at: FloatType, rest_length: FloatType| -> Result<Vec<Rebeamed>> {
1492            // music21 makes the rest with `quarterLength=`, which it passes over
1493            // when nought, leaving a quarter rest that may overfill the bar and
1494            // keep it from being beamed again.
1495            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            // The measure's events in the order a stream sorts them: by offset,
1498            // in the order they were put in.
1499            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    // music21 changes the grouping's own elements: their offsets, beams and
1530    // marks stay as the split left them.
1531    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    /// The segment in braille: its heading and first measure number, then
1553    /// each grouping in turn: music21's `transcribe`.
1554    ///
1555    /// # Errors
1556    ///
1557    /// Something too wide for a line, or that music21 cannot write.
1558    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    /// The segment's groupings in English, as music21's `__str__` lists
1598    /// them.
1599    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
1626/// A grouping's English, element by element: music21's
1627/// `BrailleElementGrouping.__str__`.
1628fn 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
1662/// The English of each element of a voice, from the grouping it was
1663/// written in, found by where each stands among the part's leaves.
1664fn 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
1690/// The English music21 writes on the key signature, time signature, tempo
1691/// words and metronome mark of a heading, in that order: on what it
1692/// transcribes on the way, which leaves out signatures with nothing to
1693/// write and a metronome mark with no number.
1694fn 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
1716/// The state of a segment being written.
1717struct 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        // music21 works through the keys still to write, or, with none
1748        // left, through a fresh list of them all that it then drops.
1749        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        // What music21 transcribes on its way to the heading takes its
1818        // English: the tempo words, the metronome mark with a number, and
1819        // the signatures unless there are none to write.
1820        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            // The halves hold the grouping's own elements in music21, so what
2013            // writing the first half says of each stays on it.
2014            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(&times) {
2053            for _ in 0..times {
2054                self.text.add_signatures(&symbol("repeat"))?;
2055            }
2056        } else if times >= 3 {
2057            let number = number_to_braille(&times.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/// A piano's two staves cut into segments of groupings for each hand,
2119/// written side by side: music21's `BrailleGrandSegment`.
2120#[derive(Clone, Debug)]
2121pub struct BrailleGrandSegment {
2122    /// The groupings of both hands, by key.
2123    pub groupings: BTreeMap<SegmentKey, BrailleElementGrouping>,
2124    /// How many cells a line holds.
2125    pub line_length: usize,
2126    /// What music21 marked on each leaf of the right hand's part.
2127    pub right_contexts: Vec<NoteContext>,
2128    /// What music21 marked on each leaf of the left hand's part.
2129    pub left_contexts: Vec<NoteContext>,
2130}
2131
2132impl BrailleGrandSegment {
2133    /// The keys of the two hands, paired measure by measure: music21's
2134    /// `yieldCombinedGroupingKeys`.
2135    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    /// The two hands in braille: a heading, then each measure's groupings
2204    /// side by side: music21's `transcribe`.
2205    ///
2206    /// # Errors
2207    ///
2208    /// Something too wide for a line, or that music21 cannot write.
2209    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        // The heading.
2219        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            // music21 looks the right hand's grouping up by a key that may be
2226            // nothing, which finds nothing rather than failing, and then
2227            // reads the missing key's affinity.
2228            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                        // music21 asks whether the grouping, not its element,
2247                        // is a key signature, so a lone signature is taken
2248                        // for a time signature, which a key signature cannot
2249                        // be written as.
2250                        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    /// The two hands' groupings in English, as music21's `__str__` lists
2354    /// them.
2355    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}