Skip to main content

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