Skip to main content

music21_rs/abc/
write.rs

1//! Writing ABC: a score as the text `from_abc` reads back.
2//!
3//! music21 has no ABC writer, so there is no text of its to match. What is
4//! matched is its *reader*: wherever ABC can be written two ways, the one
5//! written is the one music21's `abcFormat` reads back as the score it was
6//! written from, and the notes on what is where below are about that reader.
7
8use std::collections::BTreeMap;
9use std::fmt::Write as _;
10
11use super::read::class_sort_order;
12use crate::articulations::{Articulation, ArticulationKind, Finger};
13use crate::bar::{Barline, BarlineType, Ending, RepeatDirection};
14use crate::chord::Chord;
15use crate::chordsymbol::ChordSymbol;
16use crate::clef::Clef;
17use crate::defaults::{FloatType, IntegerType, UnsignedIntegerType};
18use crate::duration::{Duration, TupletType, limited_fraction};
19use crate::error::{Error, Result};
20use crate::expressions::{ArpeggioType, Expression, FermataType, OrnamentKind};
21use crate::key::KeySignature;
22use crate::metadata::Metadata;
23use crate::meter::TimeSignature;
24use crate::notation::{BeamType, Beams, Lyric, Placement, TieType};
25use crate::note::Note;
26use crate::pitch::Pitch;
27use crate::repeat::RepeatExpressionKind;
28use crate::rest::Rest;
29use crate::spanner::SpannerKind;
30use crate::stream::{Stream, StreamElement, StreamKind};
31use crate::tempo::MetronomeMark;
32
33fn abc_error(message: impl Into<String>) -> Error {
34    Error::Abc(message.into())
35}
36
37/// How [`to_abc`] writes a score.
38#[derive(Clone, Debug, PartialEq, Eq)]
39pub struct ExportOptions {
40    /// The unit note length of the `L:` field as a fraction of a whole note,
41    /// `(1, 8)` for an eighth. `None` takes the one the tune's lengths are
42    /// shortest to write against.
43    pub unit_length: Option<(UnsignedIntegerType, UnsignedIntegerType)>,
44    /// How many measures go on a line of music.
45    pub measures_per_line: usize,
46}
47
48impl Default for ExportOptions {
49    fn default() -> Self {
50        Self {
51            unit_length: None,
52            measures_per_line: 4,
53        }
54    }
55}
56
57/// Writes a score as ABC.
58///
59/// A score is one tune and an opus one tune for each score it holds, under
60/// the `X:` number its metadata gives it. A part alone, or a stream of loose
61/// notes, is a tune of one voice. The header carries the title, composer and
62/// origin, the meter, the unit note length, the tempo and the key with its
63/// mode; a score of several parts has a `V:` voice for each, and a part
64/// whose measures hold voices of their own has one for each of those. A
65/// voice's clef is said on its `V:` line, unless it is a treble clef the
66/// notes fit, which is what a reader takes where none is said, and a part's
67/// name goes there too; a tune of one voice has a `V:` line only for those.
68/// A part opening with an instrument that has a General MIDI program says so
69/// with abc2midi's `%%MIDI program`, before its music.
70///
71/// What ABC has no way to say is refused with [`Error::Abc`] rather than
72/// left out: a microtone, a tuplet inside a tuplet, an unpitched stroke, a
73/// chord symbol or a dynamic standing where no note starts, a pedal with its
74/// bounces and gaps, an octave line, a glissando, a tremolo between notes, a
75/// rehearsal mark or a metric modulation. A barline inside a measure is
76/// drawn and not heard, and is written as nothing.
77///
78/// Three things are written as music21's reader takes them, since
79/// [`from_abc`](crate::abc::from_abc) is that reader. An accidental is
80/// written on every note that shows one and carries no further into the
81/// bar. A tuplet's count of notes takes in any grace notes written among
82/// them. And a field changing the meter, the key or the tempo at a barline
83/// stands on lines of its own after that barline, where two fields together
84/// are read as belonging to the measure that follows.
85///
86/// ```
87/// use music21_rs::abc::{ExportOptions, from_abc, to_abc};
88///
89/// let tune = "X:1\nT:Scale\nM:4/4\nL:1/4\nK:G\nGABc|defg|a4|]\n";
90/// let score = from_abc(tune)?;
91/// let written = to_abc(&score, &ExportOptions::default())?;
92/// assert_eq!(
93///     written,
94///     "X:1\nT:Scale\nM:4/4\nL:1/4\nK:G\nG A B c | d e f g | a4 |]\n"
95/// );
96/// // What was written reads back as the score it was written from.
97/// assert_eq!(to_abc(&from_abc(&written)?, &ExportOptions::default())?, written);
98/// # Ok::<(), music21_rs::Error>(())
99/// ```
100pub fn to_abc(stream: &Stream, options: &ExportOptions) -> Result<String> {
101    if stream.kind() == StreamKind::Opus {
102        let mut tunes = Vec::new();
103        for (index, event) in stream.events().iter().enumerate() {
104            let score = event
105                .element()
106                .as_stream()
107                .ok_or_else(|| abc_error("an opus holds something that is not a score"))?;
108            tunes.push(Tune::new(score, index as IntegerType + 1)?.write(options)?);
109        }
110        return Ok(tunes.join("\n"));
111    }
112    Tune::new(stream, 1)?.write(options)
113}
114
115const EPSILON: FloatType = 1e-6;
116
117/// A length as a fraction, where it is one a note could have.
118fn fraction(value: FloatType) -> Result<(i128, i128)> {
119    limited_fraction(value, 4096)
120        .filter(|(numerator, denominator)| {
121            (*numerator as FloatType / *denominator as FloatType - value).abs() < 1e-7
122        })
123        .ok_or_else(|| {
124            abc_error(format!(
125                "ABC cannot write a length of {value} quarter notes"
126            ))
127        })
128}
129
130/// A length against the unit note length, as it follows a note.
131fn length_text(written: FloatType, unit: FloatType) -> Result<String> {
132    let (numerator, denominator) = fraction(written / unit)?;
133    Ok(match (numerator, denominator) {
134        (1, 1) => String::new(),
135        (numerator, 1) => numerator.to_string(),
136        (1, 2) => "/".to_string(),
137        (1, denominator) => format!("/{denominator}"),
138        (numerator, denominator) => format!("{numerator}/{denominator}"),
139    })
140}
141
142/// Text that can stand in a field: on one line.
143fn plain(text: &str) -> String {
144    text.replace(['\n', '\r'], " ").trim().to_string()
145}
146
147/// Text that can stand between quotation marks.
148fn quoted(text: &str) -> String {
149    plain(text).replace('"', "'")
150}
151
152/// How far each of the seven letters, C to B, is moved by a signature.
153type Alters = [IntegerType; 7];
154
155fn letter_index(letter: char) -> usize {
156    match letter {
157        'C' => 0,
158        'D' => 1,
159        'E' => 2,
160        'F' => 3,
161        'G' => 4,
162        'A' => 5,
163        _ => 6,
164    }
165}
166
167fn alters_of(signature: &KeySignature) -> Alters {
168    let mut alters = [0; 7];
169    for pitch in signature.altered_pitches().unwrap_or_default() {
170        alters[letter_index(pitch.step().as_char())] = pitch.alter().round() as IntegerType;
171    }
172    alters
173}
174
175/// The signature a key or key signature element writes.
176fn signature_of(element: &StreamElement) -> Option<KeySignature> {
177    match element {
178        StreamElement::Key(key) => Some(key.key_signature()),
179        StreamElement::KeySignature(signature) => Some(signature.clone()),
180        _ => None,
181    }
182}
183
184fn abc_tonic(pitch: &Pitch) -> String {
185    pitch.name().replace('-', "b")
186}
187
188/// The clef as ABC names one.
189fn clef_name(clef: &Clef) -> Result<String> {
190    let mut name = match (clef.sign(), clef.line()) {
191        (Some("G"), Some(2) | None) => "treble".to_string(),
192        (Some("G"), Some(line)) => format!("treble{line}"),
193        (Some("F"), Some(4) | None) => "bass".to_string(),
194        (Some("F"), Some(line)) => format!("bass{line}"),
195        (Some("C"), Some(3) | None) => "alto".to_string(),
196        (Some("C"), Some(4)) => "tenor".to_string(),
197        (Some("C"), Some(line)) => format!("alto{line}"),
198        (Some("percussion"), _) => "perc".to_string(),
199        (Some("none"), _) | (None, _) => "none".to_string(),
200        (Some(sign), _) => {
201            return Err(abc_error(format!("ABC has no {sign} clef")));
202        }
203    };
204    match clef.octave_change() {
205        0 => {}
206        1 => name.push_str("+8"),
207        -1 => name.push_str("-8"),
208        other => {
209            return Err(abc_error(format!(
210                "ABC has no clef moved by {other} octaves"
211            )));
212        }
213    }
214    Ok(name)
215}
216
217/// The data of a `K:` field for a key or a key signature, with a clef after
218/// it where one is to be named.
219fn key_field(element: &StreamElement, clef: Option<&Clef>) -> Result<String> {
220    let clef = clef.map(clef_name).transpose()?;
221    let mode = match element {
222        StreamElement::Key(key) => match key.mode() {
223            "major" => Some(""),
224            "minor" => Some("m"),
225            "dorian" => Some("dor"),
226            "phrygian" => Some("phr"),
227            "lydian" => Some("lyd"),
228            "mixolydian" => Some("mix"),
229            "aeolian" => Some("aeo"),
230            "ionian" => Some("ion"),
231            "locrian" => Some("loc"),
232            _ => None,
233        },
234        _ => None,
235    };
236    let mut text = match (element, mode) {
237        (StreamElement::Key(key), Some(mode)) => {
238            // A clef after a bare tonic would be read as the mode.
239            let mode = if mode.is_empty() && clef.is_some() {
240                " maj"
241            } else {
242                mode
243            };
244            format!("{}{mode}", abc_tonic(key.tonic_pitch()))
245        }
246        _ => {
247            // A signature with no mode: the major tonic it would have, and
248            // then every accidental said, which leaves no mode to read.
249            let signature = signature_of(element)
250                .ok_or_else(|| abc_error("a key field written for what is no key"))?;
251            let tonic = match signature.sharps() {
252                Some(sharps) => {
253                    let key = KeySignature::new(sharps).as_key("major");
254                    abc_tonic(key.tonic_pitch())
255                }
256                None => "C".to_string(),
257            };
258            let mut text = format!("{tonic} exp");
259            for pitch in signature.altered_pitches()? {
260                let _ = write!(
261                    text,
262                    " {}{}",
263                    accidental_marks(pitch.alter().round() as IntegerType)?,
264                    pitch.step().as_char().to_ascii_lowercase()
265                );
266            }
267            text
268        }
269    };
270    if let Some(clef) = clef {
271        let _ = write!(text, " clef={clef}");
272    }
273    Ok(text)
274}
275
276fn accidental_marks(alter: IntegerType) -> Result<&'static str> {
277    Ok(match alter {
278        -2 => "__",
279        -1 => "_",
280        0 => "=",
281        1 => "^",
282        2 => "^^",
283        other => {
284            return Err(abc_error(format!(
285                "ABC has no accidental moving a note by {other} semitones"
286            )));
287        }
288    })
289}
290
291fn meter_field(meter: &TimeSignature) -> String {
292    match meter.symbol() {
293        Some("common") => "C".to_string(),
294        Some("cut") => "C|".to_string(),
295        _ => meter.ratio_string(),
296    }
297}
298
299fn number_text(number: FloatType) -> String {
300    if number.fract() == 0.0 {
301        format!("{number:.0}")
302    } else {
303        number.to_string()
304    }
305}
306
307/// The data of a `Q:` field, or nothing for a mark that says nothing.
308fn tempo_field(mark: &MetronomeMark) -> Result<Option<String>> {
309    let mut parts = Vec::new();
310    if let Some(text) = mark.text().filter(|_| !mark.text_implicit()) {
311        parts.push(format!("\"{}\"", quoted(text)));
312    }
313    if let Some(number) = mark.number().filter(|_| !mark.number_implicit()) {
314        let (numerator, denominator) = fraction(mark.referent().quarter_length() / 4.0)?;
315        parts.push(format!("{numerator}/{denominator}={}", number_text(number)));
316    }
317    Ok((!parts.is_empty()).then(|| parts.join(" ")))
318}
319
320/// One element where a voice holds it: its offset in its measure and its
321/// place among the leaves of the score, which is how a spanner names it.
322#[derive(Clone, Copy, Debug)]
323struct Held<'a> {
324    offset: FloatType,
325    element: &'a StreamElement,
326    leaf: usize,
327}
328
329/// One measure of one voice, or the whole of a voice that has no measures.
330#[derive(Clone, Debug, Default)]
331struct Bar<'a> {
332    measure: Option<&'a Stream>,
333    held: Vec<Held<'a>>,
334    /// A voice its measure does not have is silent for this long.
335    filler: Option<FloatType>,
336}
337
338/// What ABC calls a voice: a part, or one voice of a part's measures.
339#[derive(Clone, Debug)]
340struct Voice<'a> {
341    part: &'a Stream,
342    bars: Vec<Bar<'a>>,
343    measured: bool,
344    /// Which voice of its part this is.
345    within: usize,
346}
347
348/// The General MIDI program of the instrument a part opens with: the first
349/// standing in it no later than its first note.
350fn program_of(part: &Stream) -> Option<u8> {
351    let leaves = part.leaves();
352    let first_note = leaves
353        .iter()
354        .filter(|(_, element)| is_sounding(element))
355        .map(|(offset, _)| *offset)
356        .fold(FloatType::INFINITY, FloatType::min);
357    leaves.iter().find_map(|(offset, element)| match element {
358        StreamElement::Instrument(instrument) if *offset <= first_note + EPSILON => {
359            instrument.midi_program()
360        }
361        _ => None,
362    })
363}
364
365fn is_sounding(element: &StreamElement) -> bool {
366    matches!(
367        element,
368        StreamElement::Note(_)
369            | StreamElement::Chord(_)
370            | StreamElement::Rest(_)
371            | StreamElement::Unpitched(_)
372            | StreamElement::PercussionChord(_)
373    )
374}
375
376fn is_grace(element: &StreamElement) -> bool {
377    is_sounding(element) && element.duration().is_some_and(Duration::is_grace)
378}
379
380/// The elements of a stream and of every stream inside it, in the order
381/// `Stream::leaves` counts them.
382fn gather<'a>(stream: &'a Stream, base: FloatType, leaf: &mut usize, out: &mut Vec<Held<'a>>) {
383    for event in stream.events() {
384        match event.element() {
385            StreamElement::Stream(inner) => gather(inner, base + event.offset(), leaf, out),
386            element => {
387                out.push(Held {
388                    offset: base + event.offset(),
389                    element,
390                    leaf: *leaf,
391                });
392                *leaf += 1;
393            }
394        }
395    }
396}
397
398fn sort_held(held: &mut [Held<'_>]) {
399    held.sort_by(|left, right| {
400        left.offset
401            .total_cmp(&right.offset)
402            .then(class_sort_order(left.element).cmp(&class_sort_order(right.element)))
403            .then(is_grace(right.element).cmp(&is_grace(left.element)))
404            .then(left.leaf.cmp(&right.leaf))
405    });
406}
407
408/// The voices a part is written as, counting its leaves on from `leaf`.
409fn voices_of<'a>(part: &'a Stream, leaf: &mut usize) -> Result<Vec<Voice<'a>>> {
410    let has_measures = part.events().iter().any(|event| {
411        event
412            .element()
413            .as_stream()
414            .is_some_and(|inner| inner.kind() == StreamKind::Measure)
415    });
416    if !has_measures {
417        let mut held = Vec::new();
418        gather(part, 0.0, leaf, &mut held);
419        return Ok(vec![Voice {
420            part,
421            bars: vec![Bar {
422                measure: None,
423                held,
424                filler: None,
425            }],
426            measured: false,
427            within: 0,
428        }]);
429    }
430
431    // Each measure as what stands in it outside any voice, and its voices.
432    type Split<'a> = (&'a Stream, Vec<Held<'a>>, Vec<Vec<Held<'a>>>);
433    let mut measures: Vec<Split<'a>> = Vec::new();
434    let mut loose: Vec<Held<'a>> = Vec::new();
435    for event in part.events() {
436        match event.element() {
437            StreamElement::Stream(measure) => {
438                let mut common = Vec::new();
439                let mut voices = Vec::new();
440                for inner in measure.events() {
441                    match inner.element() {
442                        StreamElement::Stream(voice) => {
443                            let mut held = Vec::new();
444                            gather(voice, inner.offset(), leaf, &mut held);
445                            voices.push(held);
446                        }
447                        element => {
448                            common.push(Held {
449                                offset: inner.offset(),
450                                element,
451                                leaf: *leaf,
452                            });
453                            *leaf += 1;
454                        }
455                    }
456                }
457                measures.push((measure, common, voices));
458            }
459            element => {
460                loose.push(Held {
461                    offset: event.offset(),
462                    element,
463                    leaf: *leaf,
464                });
465                *leaf += 1;
466            }
467        }
468    }
469    if loose.iter().any(|held| is_sounding(held.element)) {
470        return Err(abc_error(
471            "a part holds notes outside its measures as well as inside them",
472        ));
473    }
474
475    let count = measures
476        .iter()
477        .map(|(_, _, voices)| voices.len())
478        .max()
479        .unwrap_or(0)
480        .max(1);
481    let mut voices: Vec<Voice<'a>> = (0..count)
482        .map(|within| Voice {
483            part,
484            bars: Vec::new(),
485            measured: true,
486            within,
487        })
488        .collect();
489    for (index, (measure, common, inner)) in measures.into_iter().enumerate() {
490        let length = common
491            .iter()
492            .chain(inner.iter().flatten())
493            .map(|held| held.offset + held.element.quarter_length())
494            .fold(0.0, FloatType::max);
495        let plain_measure = inner.is_empty();
496        let mut inner = inner.into_iter();
497        for (within, voice) in voices.iter_mut().enumerate() {
498            let own = inner.next();
499            let mut held = Vec::new();
500            if within == 0 {
501                held.extend(common.iter().copied());
502                // What stands in the part before any measure is written at
503                // the head of the first.
504                if index == 0 {
505                    held.extend(loose.iter().filter(|held| held.offset < EPSILON).copied());
506                }
507            } else {
508                // The clef, key and meter standing outside the voices are
509                // every voice's: each voice of ABC states its own.
510                let shared = |held: &&Held<'a>| {
511                    matches!(
512                        held.element,
513                        StreamElement::Clef(_)
514                            | StreamElement::Key(_)
515                            | StreamElement::KeySignature(_)
516                            | StreamElement::TimeSignature(_)
517                    )
518                };
519                held.extend(common.iter().filter(shared).copied());
520                if index == 0 {
521                    held.extend(
522                        loose
523                            .iter()
524                            .filter(|held| held.offset < EPSILON)
525                            .filter(shared)
526                            .copied(),
527                    );
528                }
529            }
530            let filler = match own {
531                Some(own) => {
532                    held.extend(own);
533                    None
534                }
535                None if within == 0 && plain_measure => None,
536                None => Some(length),
537            };
538            if within > 0 || !plain_measure || index == 0 {
539                sort_held(&mut held);
540            }
541            voice.bars.push(Bar {
542                measure: Some(measure),
543                held,
544                filler,
545            });
546        }
547    }
548    Ok(voices)
549}
550
551/// The meter a measure states at its start.
552fn meter_of<'a>(bar: &Bar<'a>) -> Option<&'a TimeSignature> {
553    bar.held.iter().find_map(|held| match held.element {
554        StreamElement::TimeSignature(meter) if held.offset < EPSILON => Some(meter),
555        _ => None,
556    })
557}
558
559/// How long what a measure holds lasts.
560fn length_of(bar: &Bar<'_>) -> FloatType {
561    bar.held
562        .iter()
563        .map(|held| held.offset + held.element.quarter_length())
564        .fold(0.0, FloatType::max)
565}
566
567/// What the measure after gives the last note of a measure: the second half
568/// of a note music21 cut at the barline, written back onto the first.
569#[derive(Clone, Debug, Default)]
570struct Tail {
571    /// The length of the second half, in quarter notes.
572    extra: FloatType,
573    /// Whether the note is tied on past its second half.
574    tied: bool,
575    /// The slurs and hairpins closing on the second half.
576    closes: String,
577}
578
579/// How one measure is written where it is one of two music21 cut from one.
580#[derive(Clone, Debug, Default)]
581struct Plan {
582    /// The meter is the cut's to bring, not the text's.
583    drop_meter: bool,
584    /// The last note takes on the first of the next measure.
585    tail: Option<Tail>,
586    /// The first note is written on the measure before.
587    skip_head: bool,
588}
589
590fn first_sounding(bar: &Bar<'_>) -> Option<usize> {
591    bar.held.iter().position(|held| is_sounding(held.element))
592}
593
594fn last_sounding(bar: &Bar<'_>) -> Option<usize> {
595    bar.held
596        .iter()
597        .rposition(|held| is_sounding(held.element) && !is_grace(held.element))
598}
599
600/// The notes of a note or a chord.
601fn notes_of(element: &StreamElement) -> Option<&[Note]> {
602    match element {
603        StreamElement::Note(note) => Some(std::slice::from_ref(note)),
604        StreamElement::Chord(chord) => Some(chord.notes()),
605        _ => None,
606    }
607}
608
609/// Whether the note a measure ends with and the one the next begins with
610/// are the two halves of one, cut at the barline: the same pitches, tied
611/// from the one to the other. The answer inside is whether writing them
612/// apart would be read differently: a chord's ties are not read at all, and
613/// the second half does not show an accidental the first does.
614fn cut_note(first: &Bar<'_>, second: &Bar<'_>) -> Option<bool> {
615    let before = first.held[last_sounding(first)?];
616    let after = second.held[first_sounding(second)?];
617    if is_grace(after.element)
618        || after.offset > EPSILON
619        || (before.offset + before.element.quarter_length() - length_of(first)).abs() > EPSILON
620    {
621        return None;
622    }
623    let is_chord = matches!(before.element, StreamElement::Chord(_));
624    if is_chord != matches!(after.element, StreamElement::Chord(_)) {
625        return None;
626    }
627    let (ending, starting) = (notes_of(before.element)?, notes_of(after.element)?);
628    let said = |element: &StreamElement| {
629        element
630            .duration()
631            .and_then(Duration::said_tuplets)
632            .is_some_and(|tuplets| !tuplets.is_empty())
633    };
634    if ending.len() != starting.len() || said(before.element) || said(after.element) {
635        return None;
636    }
637    let tie = |note: &Note| note.tie().map(crate::notation::Tie::tie_type);
638    let halves = ending.iter().zip(starting).all(|(one, other)| {
639        one.pitch().name_with_octave() == other.pitch().name_with_octave()
640            && matches!(tie(one), Some(TieType::Start | TieType::Continue))
641            && matches!(tie(other), Some(TieType::Stop | TieType::Continue))
642            && other.lyrics().is_empty()
643    });
644    if !halves {
645        return None;
646    }
647    let hidden = ending.iter().zip(starting).any(|(one, other)| {
648        shows_accidental(one.pitch())
649            && other
650                .pitch()
651                .written_accidental()
652                .is_some_and(|accidental| accidental.display_status() == Some(false))
653    });
654    Some(is_chord || hidden)
655}
656
657/// What a note-like element is written from.
658struct Sounding<'a> {
659    duration: Duration,
660    /// The written length before any tuplet, in quarter notes.
661    written: FloatType,
662    /// A tuplet said on the note: so many notes in the time of so many.
663    tuplet: Option<(UnsignedIntegerType, UnsignedIntegerType)>,
664    bracket: Option<TupletType>,
665    grace: bool,
666    held: Held<'a>,
667}
668
669fn sounding_of<'a>(held: Held<'a>) -> Result<Option<Sounding<'a>>> {
670    let duration = match held.element {
671        StreamElement::Note(note) => note.duration().cloned().unwrap_or_else(Duration::quarter),
672        StreamElement::Chord(chord) => chord.duration().cloned().unwrap_or_else(Duration::quarter),
673        StreamElement::Rest(rest) => rest.duration().clone(),
674        StreamElement::Unpitched(_) | StreamElement::PercussionChord(_) => {
675            return Err(abc_error("ABC has no unpitched notes"));
676        }
677        _ => return Ok(None),
678    };
679    let grace = duration.is_grace();
680    let said = duration.said_tuplets().unwrap_or(&[]);
681    if said.len() > 1 {
682        return Err(abc_error("ABC cannot write a tuplet inside a tuplet"));
683    }
684    let (tuplet, bracket) = match said.first().filter(|_| !grace) {
685        Some(tuplet) => {
686            let multiplier = tuplet.multiplier();
687            let numerator = *multiplier.numer().unwrap_or(&0) as FloatType;
688            let denominator = *multiplier.denom().unwrap_or(&1) as FloatType;
689            let normal = FloatType::from(tuplet.actual()) * numerator / denominator;
690            if normal < 1.0 || (normal - normal.round()).abs() > EPSILON {
691                return Err(abc_error(format!(
692                    "ABC cannot write {} notes in the time of {normal}",
693                    tuplet.actual()
694                )));
695            }
696            (
697                Some((tuplet.actual(), normal.round() as UnsignedIntegerType)),
698                tuplet.tuplet_type(),
699            )
700        }
701        None => (None, None),
702    };
703    let written = if grace {
704        duration
705            .written_values()
706            .iter()
707            .filter_map(|value| {
708                Some(
709                    value
710                        .duration_type()?
711                        .quarter_length_with_dots(value.dots()),
712                )
713            })
714            .sum()
715    } else if tuplet.is_some() {
716        duration.quarter_length_no_tuplets()
717    } else {
718        duration.quarter_length()
719    };
720    Ok(Some(Sounding {
721        duration,
722        written,
723        tuplet,
724        bracket,
725        grace,
726        held,
727    }))
728}
729
730/// The marker opening a tuplet: `p` notes in the time of `q`, for the next
731/// `count` notes.
732fn tuplet_marker(actual: u32, normal: u32, count: usize) -> String {
733    let usual = match actual {
734        2 | 4 | 8 => Some(3),
735        3 | 6 => Some(2),
736        _ => None,
737    };
738    let mut marker = format!("({actual}");
739    let says_normal = usual != Some(normal);
740    if says_normal {
741        let _ = write!(marker, ":{normal}");
742    }
743    if count != actual as usize {
744        if !says_normal {
745            marker.push(':');
746        }
747        let _ = write!(marker, ":{count}");
748    }
749    marker
750}
751
752/// Where the tuplet markers of a voice go: before which note-like element,
753/// counted through the voice, and what each says.
754fn tuplet_markers(
755    tokens: &[Sounding<'_>],
756    opens_on: &dyn Fn(&Sounding<'_>) -> bool,
757) -> BTreeMap<usize, String> {
758    let mut markers = BTreeMap::new();
759    let mut index = 0;
760    while index < tokens.len() {
761        let Some((actual, normal)) = tokens[index].tuplet else {
762            index += 1;
763            continue;
764        };
765        // Grace notes before the first note are written before the marker
766        // and are none of its notes, unless a slur opens on one of them:
767        // music21 closes whichever of a slur and a tuplet it met last, so
768        // the marker then goes first, and counts them.
769        let mut start = index;
770        while start > 0 && tokens[start - 1].grace {
771            start -= 1;
772        }
773        if !tokens[start..index].iter().any(opens_on) {
774            start = index;
775        }
776        let mut last = index;
777        let mut filled = tokens[index].written;
778        loop {
779            // A marker with no count takes as many notes as it names, and
780            // fewer where they already fill the time: a quarter and an
781            // eighth are a whole triplet of eighths.
782            let whole = filled / FloatType::from(actual);
783            let filled_up = whole > 0.0 && (whole.log2() - whole.log2().round()).abs() < 1e-9;
784            let complete = last - start + 1 >= actual as usize || (filled_up && last > index);
785            if complete
786                || matches!(
787                    tokens[last].bracket,
788                    Some(TupletType::Stop | TupletType::StartStop)
789                )
790            {
791                break;
792            }
793            let mut next = last + 1;
794            while next < tokens.len() && tokens[next].grace {
795                next += 1;
796            }
797            // music21 reads one digit for the count.
798            if next >= tokens.len()
799                || tokens[next].tuplet != Some((actual, normal))
800                || matches!(
801                    tokens[next].bracket,
802                    Some(TupletType::Start | TupletType::StartStop)
803                )
804                || next - start + 1 > 9
805            {
806                break;
807            }
808            filled += tokens[next].written;
809            last = next;
810        }
811        markers.insert(start, tuplet_marker(actual, normal, last - start + 1));
812        index = last + 1;
813    }
814    markers
815}
816
817/// How a note, chord or rest is played.
818fn articulations_of(element: &StreamElement) -> &[Articulation] {
819    match element {
820        StreamElement::Note(note) => note.articulations(),
821        StreamElement::Chord(chord) => chord.articulations(),
822        StreamElement::Rest(rest) => rest.articulations(),
823        _ => &[],
824    }
825}
826
827/// Whether an articulation is written as one character, which music21
828/// reads as a token of its own.
829fn is_lettered(articulation: &Articulation) -> bool {
830    matches!(
831        articulation.kind(),
832        ArticulationKind::Staccato
833            | ArticulationKind::UpBow
834            | ArticulationKind::DownBow
835            | ArticulationKind::Accent
836            | ArticulationKind::StrongAccent
837            | ArticulationKind::Tenuto
838    )
839}
840
841/// The decoration an articulation is written as.
842fn articulation_text(articulation: &Articulation) -> Result<String> {
843    Ok(match articulation.kind() {
844        ArticulationKind::Staccato => ".".to_string(),
845        ArticulationKind::UpBow => "u".to_string(),
846        ArticulationKind::DownBow => "v".to_string(),
847        // The three letters the header gives these meanings to.
848        ArticulationKind::Accent => "K".to_string(),
849        ArticulationKind::StrongAccent => "k".to_string(),
850        ArticulationKind::Tenuto => "M".to_string(),
851        ArticulationKind::Staccatissimo => "!wedge!".to_string(),
852        ArticulationKind::BreathMark => "!breath!".to_string(),
853        ArticulationKind::OpenString => "!open!".to_string(),
854        ArticulationKind::Stopped => "!+!".to_string(),
855        ArticulationKind::SnapPizzicato => "!snap!".to_string(),
856        ArticulationKind::Fingering => match articulation.finger() {
857            Some(Finger::Number(number)) if (0..=5).contains(number) => format!("!{number}!"),
858            _ => return Err(abc_error("ABC writes fingers nought to five and no others")),
859        },
860        other => {
861            return Err(abc_error(format!(
862                "ABC has no decoration for {}",
863                other.class_name()
864            )));
865        }
866    })
867}
868
869fn expression_text(expression: &Expression) -> Result<&'static str> {
870    Ok(match expression {
871        Expression::Fermata(fermata) => match fermata.fermata_type() {
872            FermataType::Inverted => "!fermata!",
873            FermataType::Upright => "!invertedfermata!",
874        },
875        Expression::Arpeggio(ArpeggioType::Normal) => "!arpeggio!",
876        Expression::Arpeggio(_) => {
877            return Err(abc_error("ABC has one arpeggio sign, the plain one"));
878        }
879        Expression::Ornament(ornament) => match ornament.kind() {
880            OrnamentKind::Trill | OrnamentKind::HalfStepTrill | OrnamentKind::WholeStepTrill => {
881                "!trill!"
882            }
883            OrnamentKind::Mordent
884            | OrnamentKind::HalfStepMordent
885            | OrnamentKind::WholeStepMordent => "!lowermordent!",
886            OrnamentKind::InvertedMordent
887            | OrnamentKind::HalfStepInvertedMordent
888            | OrnamentKind::WholeStepInvertedMordent => "!uppermordent!",
889            OrnamentKind::Turn => "!turn!",
890            OrnamentKind::InvertedTurn => "!invertedturn!",
891            other => {
892                return Err(abc_error(format!(
893                    "ABC has no decoration for {}",
894                    other.class_name()
895                )));
896            }
897        },
898    })
899}
900
901fn decorations(articulations: &[Articulation], expressions: &[Expression]) -> Result<String> {
902    let mut text = String::new();
903    for expression in expressions {
904        text.push_str(expression_text(expression)?);
905    }
906    // The long ones first, so the single letters stand against the note.
907    let mut written = articulations
908        .iter()
909        .map(articulation_text)
910        .collect::<Result<Vec<_>>>()?;
911    written.sort_by_key(|text| std::cmp::Reverse(text.len()));
912    text.push_str(&written.concat());
913    Ok(text)
914}
915
916/// The start of a spanner as ABC opens one.
917fn spanner_open(kind: SpannerKind) -> Result<&'static str> {
918    Ok(match kind {
919        SpannerKind::Slur => "(",
920        SpannerKind::Crescendo => "!crescendo(!",
921        SpannerKind::Diminuendo => "!diminuendo(!",
922        other => {
923            return Err(abc_error(format!("ABC has no {}", other.class_name())));
924        }
925    })
926}
927
928fn spanner_close(kind: SpannerKind) -> &'static str {
929    match kind {
930        SpannerKind::Crescendo => "!crescendo)!",
931        SpannerKind::Diminuendo => "!diminuendo)!",
932        _ => ")",
933    }
934}
935
936/// What reading the tune keeps from one note to the next, across voices.
937struct Reading {
938    /// The signature music21 would read an unmarked note under here.
939    alters: Alters,
940}
941
942/// One measure as it is written: the lines of fields before and after its
943/// music, the music, and what its notes are sung to.
944#[derive(Default)]
945struct Written<'a> {
946    /// Fields standing after the barline that opens the measure.
947    opening: Vec<String>,
948    music: String,
949    /// Fields standing before the barline that closes it.
950    closing: Vec<String>,
951    /// The syllables of each note a `w:` line counts.
952    sung: Vec<&'a [Lyric]>,
953    /// Whether the music begins with a note, with nothing before it that
954    /// music21 counts as a token of its own.
955    starts_with_note: bool,
956}
957
958/// A tune: one score, its voices and what joins their notes.
959struct Tune<'a> {
960    metadata: Option<&'a Metadata>,
961    voices: Vec<Voice<'a>>,
962    /// The parts in order, each with how many voices it is written as.
963    parts: Vec<(&'a Stream, usize)>,
964    opens: BTreeMap<usize, Vec<SpannerKind>>,
965    closes: BTreeMap<usize, Vec<SpannerKind>>,
966    number: IntegerType,
967}
968
969impl<'a> Tune<'a> {
970    fn new(stream: &'a Stream, number: IntegerType) -> Result<Self> {
971        let mut leaf = 0;
972        let mut voices = Vec::new();
973        let mut parts = Vec::new();
974        let holds_parts = stream.events().iter().any(|event| {
975            event.element().as_stream().is_some_and(|inner| {
976                matches!(inner.kind(), StreamKind::Part | StreamKind::PartStaff)
977            })
978        });
979        if holds_parts {
980            for event in stream.events() {
981                match event.element() {
982                    StreamElement::Stream(part) => {
983                        let made = voices_of(part, &mut leaf)?;
984                        parts.push((part.as_ref(), made.len()));
985                        voices.extend(made);
986                    }
987                    element if is_sounding(element) => {
988                        return Err(abc_error(
989                            "a score holds notes outside its parts as well as inside them",
990                        ));
991                    }
992                    _ => leaf += 1,
993                }
994            }
995        } else if stream.kind() == StreamKind::Measure {
996            // A measure alone is a tune of that one measure.
997            let mut held = Vec::new();
998            gather(stream, 0.0, &mut leaf, &mut held);
999            parts.push((stream, 1));
1000            voices.push(Voice {
1001                part: stream,
1002                bars: vec![Bar {
1003                    measure: None,
1004                    held,
1005                    filler: None,
1006                }],
1007                measured: false,
1008                within: 0,
1009            });
1010        } else {
1011            let made = voices_of(stream, &mut leaf)?;
1012            parts.push((stream, made.len()));
1013            voices.extend(made);
1014        }
1015
1016        // Where each slur and hairpin opens and closes, by leaf.
1017        let leaves = stream.leaves();
1018        let graced = |leaf: usize| {
1019            leaves
1020                .get(leaf)
1021                .is_some_and(|(_, element)| is_grace(element))
1022        };
1023        let mut opens: BTreeMap<usize, Vec<(usize, SpannerKind)>> = BTreeMap::new();
1024        let mut closes: BTreeMap<usize, Vec<(usize, SpannerKind)>> = BTreeMap::new();
1025        for spanner in stream.spanners() {
1026            spanner_open(spanner.kind())?;
1027            let spanned = spanner.spanned();
1028            let placed: Vec<usize> = spanned.iter().flatten().copied().collect();
1029            let (Some(first), Some(last)) = (placed.iter().min(), placed.iter().max()) else {
1030                continue;
1031            };
1032            // An element the score does not hold is the note a grace note
1033            // was copied from, which stood where the grace note stands.
1034            let mut start = *first;
1035            for _ in spanned.iter().take_while(|place| place.is_none()) {
1036                if start > 0 && graced(start - 1) {
1037                    start -= 1;
1038                }
1039            }
1040            let mut end = *last;
1041            for _ in spanned.iter().rev().take_while(|place| place.is_none()) {
1042                if graced(end + 1) {
1043                    end += 1;
1044                }
1045            }
1046            opens.entry(start).or_default().push((end, spanner.kind()));
1047            closes.entry(end).or_default().push((start, spanner.kind()));
1048        }
1049        // The spanner closing last opens first, and the one opened last
1050        // closes first.
1051        let ordered = |mut list: Vec<(usize, SpannerKind)>| {
1052            list.sort_by_key(|(other, _)| std::cmp::Reverse(*other));
1053            list.into_iter().map(|(_, kind)| kind).collect::<Vec<_>>()
1054        };
1055        Ok(Self {
1056            metadata: stream.metadata(),
1057            voices,
1058            parts,
1059            opens: opens
1060                .into_iter()
1061                .map(|(leaf, list)| (leaf, ordered(list)))
1062                .collect(),
1063            closes: closes
1064                .into_iter()
1065                .map(|(leaf, list)| (leaf, ordered(list)))
1066                .collect(),
1067            number,
1068        })
1069    }
1070
1071    /// The unit note length in quarter notes: the one asked for, or the
1072    /// one that leaves the least to write after the notes.
1073    fn unit(&self, options: &ExportOptions) -> Result<(FloatType, String)> {
1074        if let Some((numerator, denominator)) = options.unit_length {
1075            if numerator == 0 || denominator == 0 {
1076                return Err(abc_error("the unit note length cannot be nought"));
1077            }
1078            return Ok((
1079                4.0 * FloatType::from(numerator) / FloatType::from(denominator),
1080                format!("{numerator}/{denominator}"),
1081            ));
1082        }
1083        const UNITS: [(FloatType, &str); 6] = [
1084            (0.5, "1/8"),
1085            (1.0, "1/4"),
1086            (0.25, "1/16"),
1087            (2.0, "1/2"),
1088            (0.125, "1/32"),
1089            (4.0, "1/1"),
1090        ];
1091        // The unit that leaves the least to write after the notes.
1092        let mut written = Vec::new();
1093        for held in self
1094            .voices
1095            .iter()
1096            .flat_map(|voice| &voice.bars)
1097            .flat_map(|bar| &bar.held)
1098        {
1099            if let Some(sounding) = sounding_of(*held)? {
1100                written.push(sounding.written);
1101            }
1102        }
1103        let mut place = 0;
1104        let mut least = usize::MAX;
1105        for (index, (unit, _)) in UNITS.iter().enumerate() {
1106            let cost: usize = written
1107                .iter()
1108                .map(|length| length_text(*length, *unit).map_or(8, |text| text.len()))
1109                .sum();
1110            if cost < least {
1111                least = cost;
1112                place = index;
1113            }
1114        }
1115        Ok((UNITS[place].0, UNITS[place].1.to_string()))
1116    }
1117
1118    fn write(&self, options: &ExportOptions) -> Result<String> {
1119        let (unit, unit_text) = self.unit(options)?;
1120        let mut out = String::new();
1121        let number = self
1122            .metadata
1123            .and_then(|metadata| metadata.first_text("number"))
1124            .and_then(|text| text.trim().parse::<IntegerType>().ok())
1125            .unwrap_or(self.number);
1126        let _ = writeln!(out, "X:{number}");
1127        if let Some(metadata) = self.metadata {
1128            for (name, tag) in [
1129                ("title", 'T'),
1130                ("alternativeTitle", 'T'),
1131                ("composer", 'C'),
1132                ("localeOfComposition", 'O'),
1133            ] {
1134                for value in metadata.get(name) {
1135                    let _ = writeln!(out, "{tag}:{}", plain(value.text()));
1136                }
1137            }
1138        }
1139
1140        // The letters ABC leaves to be given a meaning, given the meanings
1141        // music21 reads them with.
1142        for (kind, field) in [
1143            (ArticulationKind::Accent, "U:K=!accent!"),
1144            (ArticulationKind::StrongAccent, "U:k=!marcato!"),
1145            (ArticulationKind::Tenuto, "U:M=!tenuto!"),
1146        ] {
1147            let used = self
1148                .voices
1149                .iter()
1150                .flat_map(|voice| &voice.bars)
1151                .flat_map(|bar| &bar.held)
1152                .flat_map(|held| articulations_of(held.element))
1153                .any(|articulation| articulation.kind() == kind);
1154            if used {
1155                let _ = writeln!(out, "{field}");
1156            }
1157        }
1158
1159        // What every voice opens with goes in the header, and what only
1160        // some do after their own `V:` line.
1161        let first = self.voices.first();
1162        let opening = |voice: &Voice<'a>| -> Opening<'a> {
1163            Opening::of(voice.bars.first().map_or(&[], |bar| bar.held.as_slice()))
1164        };
1165        let shared = first.map(opening).unwrap_or_default();
1166        let openings: Vec<Opening<'a>> = self.voices.iter().map(opening).collect();
1167        let all = |same: &dyn Fn(&Opening<'a>) -> bool| openings.iter().all(same);
1168        let shared_meter = all(&|other| other.meter == shared.meter);
1169        let shared_tempo = all(&|other| other.tempos == shared.tempos);
1170
1171        if shared_meter {
1172            let _ = writeln!(
1173                out,
1174                "M:{}",
1175                shared.meter.map_or("none".to_string(), meter_field)
1176            );
1177        }
1178        let _ = writeln!(out, "L:{unit_text}");
1179        if shared_tempo {
1180            for mark in &shared.tempos {
1181                if let Some(field) = tempo_field(mark)? {
1182                    let _ = writeln!(out, "Q:{field}");
1183                }
1184            }
1185        }
1186        let clef_of = |voice: &Voice<'a>, opening: &Opening<'a>| -> Option<&'a Clef> {
1187            let clef = opening.clef?;
1188            let pitches: Vec<Pitch> = voice
1189                .bars
1190                .iter()
1191                .flat_map(|bar| &bar.held)
1192                .flat_map(|held| match held.element {
1193                    StreamElement::ChordSymbol(symbol) => symbol.pitches().unwrap_or_default(),
1194                    element => element.pitches(),
1195                })
1196                .collect();
1197            // A clef is left unsaid only where it is treble, ABC's own
1198            // default, and fits the notes best, which is what music21's
1199            // reader takes where none is written.
1200            let unsaid = clef.kind() == crate::clef::ClefKind::TrebleClef
1201                && *clef == Clef::best_for(&pitches, false);
1202            (!unsaid).then_some(clef)
1203        };
1204        // A voice's opening clef is said on its `V:` line: music21's reader
1205        // takes a bass clef named in the header's `K:` as lowering every
1206        // note two octaves, and one on a `V:` line as the clef alone.
1207        let key_line = |opening: &Opening<'a>| -> Result<String> {
1208            match opening.key {
1209                Some(key) => key_field(key, None),
1210                None => Ok("none".to_string()),
1211            }
1212        };
1213        let voice_line =
1214            |index: usize, voice: &Voice<'a>, opening: &Opening<'a>| -> Result<String> {
1215                let mut line = format!("V:{}", index + 1);
1216                if let Some(clef) = clef_of(voice, opening) {
1217                    let _ = write!(line, " clef={}", clef_name(clef)?);
1218                }
1219                if let Some(name) = voice.part.name().filter(|_| voice.within == 0) {
1220                    let _ = write!(line, " name=\"{}\"", quoted(name));
1221                }
1222                Ok(line)
1223            };
1224        let mut reading = Reading { alters: [0; 7] };
1225        let several = self.voices.len() > 1;
1226        // A first voice stating no key leaves the key to the voices that
1227        // state one, each after its own `V:` line.
1228        let key_heads = !several || shared.key.is_some();
1229        if !key_heads {
1230        } else if first.is_some() {
1231            let _ = writeln!(out, "K:{}", key_line(&shared)?);
1232            reading.alters = shared
1233                .key
1234                .and_then(signature_of)
1235                .map_or([0; 7], |signature| alters_of(&signature));
1236        } else {
1237            let _ = writeln!(out, "K:none");
1238        }
1239
1240        if self.parts.iter().any(|(_, voices)| *voices > 1) {
1241            // Voices of one part share its staff.
1242            let mut next = 1;
1243            let groups: Vec<String> = self
1244                .parts
1245                .iter()
1246                .map(|(_, voices)| {
1247                    let ids: Vec<String> = (next..next + voices).map(|id| id.to_string()).collect();
1248                    next += voices;
1249                    if ids.len() > 1 {
1250                        format!("({})", ids.join(" "))
1251                    } else {
1252                        ids.concat()
1253                    }
1254                })
1255                .collect();
1256            let _ = writeln!(out, "%%score {}", groups.join(" "));
1257        }
1258        for (index, voice) in self.voices.iter().enumerate() {
1259            let opening = &openings[index];
1260            // A tune of one voice has a `V:` line only for a clef to say or
1261            // a part's name, which no reader takes for the part's but every
1262            // renderer draws.
1263            let line = voice_line(index, voice, opening)?;
1264            if several || line != "V:1" {
1265                let _ = writeln!(out, "{line}");
1266            }
1267            if several {
1268                if !shared_meter {
1269                    let _ = writeln!(
1270                        out,
1271                        "M:{}",
1272                        opening.meter.map_or("none".to_string(), meter_field)
1273                    );
1274                }
1275                if !shared_tempo {
1276                    for mark in &opening.tempos {
1277                        if let Some(field) = tempo_field(mark)? {
1278                            let _ = writeln!(out, "Q:{field}");
1279                        }
1280                    }
1281                }
1282                // The key is said again wherever the voice before left
1283                // another in force, or this voice opens in one of its own.
1284                let alters = opening
1285                    .key
1286                    .and_then(signature_of)
1287                    .map_or([0; 7], |signature| alters_of(&signature));
1288                let line = key_line(opening)?;
1289                let stated = opening.key.is_some();
1290                let differs = !key_heads || alters != reading.alters || line != key_line(&shared)?;
1291                if stated && differs && (index > 0 || !key_heads) {
1292                    let _ = writeln!(out, "K:{line}");
1293                }
1294                reading.alters = alters;
1295            }
1296            // The part's instrument, as abc2midi and abcjs take it: no
1297            // reader of ABC music21's or this crate's reads it back.
1298            if let Some(program) = program_of(voice.part) {
1299                let _ = writeln!(out, "%%MIDI program {program}");
1300            }
1301            self.write_voice(voice, unit, &unit_text, options, &mut reading, &mut out)?;
1302        }
1303        Ok(out)
1304    }
1305
1306    fn write_voice(
1307        &self,
1308        voice: &Voice<'a>,
1309        unit: FloatType,
1310        unit_text: &str,
1311        options: &ExportOptions,
1312        reading: &mut Reading,
1313        out: &mut String,
1314    ) -> Result<()> {
1315        // The tuplet markers, over the voice from end to end: a tuplet may
1316        // run across a barline.
1317        let mut tokens = Vec::new();
1318        let mut places = Vec::new();
1319        for (bar_index, bar) in voice.bars.iter().enumerate() {
1320            for (held_index, held) in bar.held.iter().enumerate() {
1321                if let Some(sounding) = sounding_of(*held)? {
1322                    places.push((bar_index, held_index));
1323                    tokens.push(sounding);
1324                }
1325            }
1326        }
1327        let opens_on = |sounding: &Sounding<'_>| self.opens.contains_key(&sounding.held.leaf);
1328        let markers: BTreeMap<(usize, usize), String> = tuplet_markers(&tokens, &opens_on)
1329            .into_iter()
1330            .map(|(token, marker)| (places[token], marker))
1331            .collect();
1332
1333        // Measures music21 cut in two are written as the one they were cut
1334        // from, so that it cuts them again: the barline that closed the long
1335        // measure is at the start of its second half, where no barline
1336        // written there would be read, and the meters the cut brought with
1337        // it are the cut's to bring again.
1338        let count = voice.bars.len();
1339        let mut joined = vec![false; count];
1340        let mut plans = vec![Plan::default(); count];
1341        if voice.measured {
1342            let mut meter: Option<&TimeSignature> = None;
1343            for index in 0..count {
1344                let before = meter;
1345                if let Some(own) = meter_of(&voice.bars[index]) {
1346                    meter = Some(own);
1347                }
1348                if index == 0 || joined[index - 1] {
1349                    continue;
1350                }
1351                let (first, second) = (&voice.bars[index - 1], &voice.bars[index]);
1352                let moved = second
1353                    .measure
1354                    .and_then(Stream::left_barline)
1355                    .is_some_and(|barline| {
1356                        barline.repeat_direction() != Some(RepeatDirection::Start)
1357                    });
1358                let closed = first.measure.and_then(Stream::right_barline).is_some()
1359                    || second.measure.and_then(Stream::right_barline).is_some();
1360                let bar_length = before.map(TimeSignature::bar_quarter_length);
1361                let full = bar_length.is_some_and(|bar| (length_of(first) - bar).abs() < EPSILON);
1362                // What is left of a cut measure is a whole bar or states the
1363                // meter that fits it.
1364                let remainder = meter_of(second).is_some()
1365                    || bar_length.is_some_and(|bar| (length_of(second) - bar).abs() < EPSILON);
1366                let cut = cut_note(first, second);
1367                if (moved || (cut == Some(true) && remainder))
1368                    && !closed
1369                    && full
1370                    && first.filler.is_none()
1371                    && second.filler.is_none()
1372                {
1373                    joined[index] = true;
1374                    plans[index].drop_meter = true;
1375                    if cut.is_some()
1376                        && let Some(head) = first_sounding(second)
1377                    {
1378                        let head = second.held[head];
1379                        plans[index].skip_head = true;
1380                        plans[index - 1].tail = Some(Tail {
1381                            extra: head.element.quarter_length(),
1382                            tied: notes_of(head.element).is_some_and(|notes| {
1383                                notes.iter().any(|note| {
1384                                    note.tie().map(crate::notation::Tie::tie_type)
1385                                        == Some(TieType::Continue)
1386                                })
1387                            }),
1388                            closes: self
1389                                .closes
1390                                .get(&head.leaf)
1391                                .map_or(&[][..], Vec::as_slice)
1392                                .iter()
1393                                .map(|kind| spanner_close(*kind))
1394                                .collect(),
1395                        });
1396                    }
1397                    // The measure after is given the old meter back.
1398                    if let (Some(next), Some(old)) = (voice.bars.get(index + 1), before)
1399                        && meter_of(next)
1400                            .is_some_and(|given| given.ratio_string() == old.ratio_string())
1401                    {
1402                        plans[index + 1].drop_meter = true;
1403                    }
1404                    meter = before;
1405                }
1406            }
1407        }
1408
1409        let mut written = Vec::new();
1410        for (index, bar) in voice.bars.iter().enumerate() {
1411            written.push(self.write_bar(
1412                voice,
1413                index,
1414                bar,
1415                unit,
1416                unit_text,
1417                &markers,
1418                reading,
1419                &plans[index],
1420            )?);
1421        }
1422        // The barline after each measure, with the ending the next opens.
1423        let barline_after = |index: usize| -> (String, bool) {
1424            if !voice.measured {
1425                return (String::new(), false);
1426            }
1427            let bar = &voice.bars[index];
1428            let right = if joined[index] {
1429                bar.measure.and_then(Stream::left_barline)
1430            } else {
1431                bar.measure.and_then(Stream::right_barline)
1432            };
1433            let next = voice.bars.get(index + 1).and_then(|bar| bar.measure);
1434            // After the last measure there is nothing to keep a barline off.
1435            let field_can_follow = written
1436                .get(index + 1)
1437                .is_none_or(|next| next.opening.is_empty() && next.starts_with_note);
1438            barline_text(
1439                right,
1440                next.and_then(Stream::left_barline),
1441                next.and_then(Stream::ending),
1442                false,
1443                field_can_follow,
1444            )
1445        };
1446
1447        let has_lyrics = written
1448            .iter()
1449            .any(|bar| bar.sung.iter().any(|lyrics| !lyrics.is_empty()));
1450        let per_line = options.measures_per_line.max(1);
1451        let last = written.len().saturating_sub(1);
1452        let mut line = String::new();
1453        let mut on_line = 0;
1454        let mut sung: Vec<&[Lyric]> = Vec::new();
1455        if voice.measured {
1456            let first = voice.bars.first().and_then(|bar| bar.measure);
1457            line.push_str(
1458                &barline_text(
1459                    None,
1460                    first.and_then(Stream::left_barline),
1461                    first.and_then(Stream::ending),
1462                    true,
1463                    false,
1464                )
1465                .0,
1466            );
1467        }
1468        for (index, bar) in written.iter().enumerate() {
1469            if !line.is_empty() && !line.ends_with(' ') && !bar.music.is_empty() {
1470                line.push(' ');
1471            }
1472            line.push_str(&bar.music);
1473            sung.extend(bar.sung.iter().copied());
1474            if joined.get(index + 1) == Some(&true) {
1475                continue;
1476            }
1477            on_line += 1;
1478            let next = written.get(index + 1);
1479            let (barline, keep_off) = barline_after(index);
1480            // The fields the next measure opens with. A barline that is not
1481            // the next measure's to take is kept off it by a field between
1482            // them, which music21 passes over: the unit note length again.
1483            let said_again = [format!("L:{unit_text}")];
1484            let opening: &[String] = match next {
1485                Some(next) if !next.opening.is_empty() => &next.opening,
1486                Some(_) if keep_off => &said_again,
1487                _ => &[],
1488            };
1489            let fields_follow = !opening.is_empty();
1490            // A word is not broken across lines of words.
1491            let mid_word = sung
1492                .iter()
1493                .rev()
1494                .find_map(|lyrics| lyrics.first())
1495                .is_some_and(|lyric| lyric.raw_text().ends_with('-'));
1496            let breaks = index == last
1497                || !bar.closing.is_empty()
1498                || fields_follow
1499                || (on_line >= per_line && !(has_lyrics && mid_word));
1500            if !breaks {
1501                if !barline.is_empty() {
1502                    let _ = write!(line, " {barline}");
1503                }
1504                continue;
1505            }
1506            let words = if has_lyrics {
1507                lyric_lines(&sung)
1508            } else {
1509                Vec::new()
1510            };
1511            sung.clear();
1512            on_line = 0;
1513            // Words and fields stand before the barline: after it and before
1514            // a note, music21 passes a field over.
1515            let before_bar = index != last && !words.is_empty() || !bar.closing.is_empty();
1516            if !before_bar && !barline.is_empty() {
1517                let _ = write!(line, " {barline}");
1518            }
1519            push_line(out, &line);
1520            line.clear();
1521            for words in words {
1522                let _ = writeln!(out, "w:{words}");
1523            }
1524            for field in &bar.closing {
1525                let _ = writeln!(out, "{field}");
1526            }
1527            if before_bar {
1528                line.push_str(&barline);
1529            }
1530            if !opening.is_empty() {
1531                push_line(out, &line);
1532                line.clear();
1533                for field in opening {
1534                    let _ = writeln!(out, "{field}");
1535                }
1536            }
1537        }
1538        push_line(out, &line);
1539        Ok(())
1540    }
1541
1542    #[allow(clippy::too_many_arguments)]
1543    fn write_bar(
1544        &self,
1545        voice: &Voice<'a>,
1546        index: usize,
1547        bar: &Bar<'a>,
1548        unit: FloatType,
1549        unit_text: &str,
1550        markers: &BTreeMap<(usize, usize), String>,
1551        reading: &mut Reading,
1552        plan: &Plan,
1553    ) -> Result<Written<'a>> {
1554        let mut written = Written::default();
1555        if let Some(length) = bar.filler {
1556            if length > EPSILON {
1557                written.music = format!("x{}", length_text(length, unit)?);
1558            }
1559            return Ok(written);
1560        }
1561        let end = bar
1562            .held
1563            .iter()
1564            .map(|held| held.offset + held.element.quarter_length())
1565            .fold(0.0, FloatType::max);
1566        let first = index == 0;
1567        let skipped = first_sounding(bar).filter(|_| plan.skip_head);
1568        let tailed = last_sounding(bar).filter(|_| plan.tail.is_some());
1569
1570        // How the key is said, where the measure states one: after the
1571        // barline, where the measure's own notes are read under it, or
1572        // before the barline closing it, where they are read under the old.
1573        let stated = bar
1574            .held
1575            .iter()
1576            .find(|held| held.offset < EPSILON && signature_of(held.element).is_some());
1577        let mut key_closes = false;
1578        let mut silent_key = None;
1579        if voice.measured && !first {
1580            let unmarked = unmarked_notes(bar, skipped);
1581            let fits = |alters: &Alters| {
1582                unmarked
1583                    .iter()
1584                    .all(|(letter, alter)| alters[*letter] == *alter)
1585            };
1586            match stated.and_then(|held| signature_of(held.element)) {
1587                Some(signature) => {
1588                    let alters = alters_of(&signature);
1589                    if !fits(&alters) && fits(&reading.alters) {
1590                        key_closes = true;
1591                    } else {
1592                        reading.alters = alters;
1593                    }
1594                }
1595                None if !fits(&reading.alters) => {
1596                    // The notes were read under a key the measure does not
1597                    // state: one said alone after a barline, which music21
1598                    // reads the notes under and then passes over.
1599                    let in_force = self.key_in_force(voice, index);
1600                    let candidates = in_force.into_iter().chain((-7..=7).map(KeySignature::new));
1601                    silent_key = candidates
1602                        .map(|signature| (alters_of(&signature), signature))
1603                        .find(|(alters, _)| fits(alters));
1604                }
1605                None => {}
1606            }
1607        }
1608
1609        // The fields the measure opens with. Those of the first measure are
1610        // in the header.
1611        let mut opening_clef = None;
1612        for held in &bar.held {
1613            if held.offset > EPSILON || first || !voice.measured {
1614                continue;
1615            }
1616            match held.element {
1617                StreamElement::TimeSignature(meter) if !plan.drop_meter => {
1618                    written.opening.push(format!("M:{}", meter_field(meter)));
1619                }
1620                StreamElement::MetronomeMark(mark) if end > EPSILON => {
1621                    if let Some(field) = tempo_field(mark)? {
1622                        written.opening.push(format!("Q:{field}"));
1623                    }
1624                }
1625                StreamElement::Clef(clef) => opening_clef = Some(clef),
1626                StreamElement::Key(_) | StreamElement::KeySignature(_) => {
1627                    let field = format!("K:{}", key_field(held.element, None)?);
1628                    if key_closes {
1629                        written.closing.push(field);
1630                    } else {
1631                        written.opening.push(field);
1632                    }
1633                }
1634                _ => {}
1635            }
1636        }
1637
1638        let mut music = Vec::new();
1639        let mut cursor = 0.0;
1640        // What waits for the next note: the chord symbol and the words and
1641        // signs written before it, and the grace notes leaning on it.
1642        let mut symbol: Option<String> = None;
1643        let mut before = String::new();
1644        let mut waiting_since: Option<FloatType> = None;
1645        let mut graces = String::new();
1646        let mut grace_slash = false;
1647        let mut grace_closes = String::new();
1648        let mut lead = String::new();
1649        let mut first_token = true;
1650        if let Some(clef) = opening_clef {
1651            lead.push_str(&format!("[K:clef={}]", clef_name(clef)?));
1652        }
1653
1654        for (held_index, held) in bar.held.iter().enumerate() {
1655            let at_start = held.offset < EPSILON;
1656            let header = at_start && (first || voice.measured);
1657            match held.element {
1658                StreamElement::Stream(_)
1659                | StreamElement::Instrument(_)
1660                | StreamElement::Variant(_) => {}
1661                StreamElement::Clef(clef) => {
1662                    if !header {
1663                        lead.push_str(&format!("[K:clef={}]", clef_name(clef)?));
1664                    }
1665                }
1666                StreamElement::TimeSignature(meter) => {
1667                    if !header {
1668                        self.field(
1669                            voice,
1670                            &mut music,
1671                            &mut lead,
1672                            format!("M:{}", meter_field(meter)),
1673                        );
1674                    }
1675                }
1676                StreamElement::Key(_) | StreamElement::KeySignature(_) => {
1677                    if !header {
1678                        let field = format!("K:{}", key_field(held.element, None)?);
1679                        self.field(voice, &mut music, &mut lead, field);
1680                        if !voice.measured
1681                            && let Some(signature) = signature_of(held.element)
1682                        {
1683                            reading.alters = alters_of(&signature);
1684                        }
1685                    }
1686                }
1687                StreamElement::MetronomeMark(mark) => {
1688                    let Some(field) = tempo_field(mark)? else {
1689                        continue;
1690                    };
1691                    if header && (first || end > EPSILON) {
1692                        // In the header, or among the measure's opening
1693                        // fields.
1694                    } else if voice.measured && (held.offset - end).abs() < EPSILON {
1695                        written.closing.push(format!("Q:{field}"));
1696                    } else {
1697                        self.field(voice, &mut music, &mut lead, format!("Q:{field}"));
1698                    }
1699                }
1700                StreamElement::TempoText(text) => {
1701                    let field = format!("Q:\"{}\"", quoted(text.text()));
1702                    self.field(voice, &mut music, &mut lead, field);
1703                }
1704                StreamElement::ChordSymbol(chord_symbol) => {
1705                    if symbol.is_some() {
1706                        return Err(abc_error("ABC writes one chord symbol on a note"));
1707                    }
1708                    symbol = Some(format!("\"{}\"", symbol_text(chord_symbol)));
1709                    waiting_since.get_or_insert(held.offset);
1710                }
1711                StreamElement::Dynamic(dynamic) => {
1712                    let value = dynamic.value();
1713                    if value.is_empty() || !value.chars().all(|c| c.is_ascii_alphabetic()) {
1714                        return Err(abc_error(format!("ABC has no dynamic {value:?}")));
1715                    }
1716                    let _ = write!(before, "!{value}!");
1717                    waiting_since.get_or_insert(held.offset);
1718                }
1719                StreamElement::TextExpression(expression) => {
1720                    let side = if expression.placement() == Some(Placement::Below) {
1721                        '_'
1722                    } else {
1723                        '^'
1724                    };
1725                    let _ = write!(before, "\"{side}{}\"", quoted(expression.content()));
1726                    waiting_since.get_or_insert(held.offset);
1727                }
1728                StreamElement::RepeatExpression(mark) => {
1729                    let sign = match mark.kind() {
1730                        RepeatExpressionKind::Coda => "!coda!".to_string(),
1731                        RepeatExpressionKind::Segno => "!segno!".to_string(),
1732                        RepeatExpressionKind::Fine => "!fine!".to_string(),
1733                        RepeatExpressionKind::DaCapo => "!D.C.!".to_string(),
1734                        RepeatExpressionKind::DalSegno => "!D.S.!".to_string(),
1735                        _ => format!("\"^{}\"", quoted(mark.text())),
1736                    };
1737                    before.push_str(&sign);
1738                    waiting_since.get_or_insert(held.offset);
1739                }
1740                // A barline inside a measure is drawn and not heard, and
1741                // music21's exporters write nothing for one either.
1742                // How the page is laid out is not the tune's to say.
1743                StreamElement::Barline(_)
1744                | StreamElement::Layout(_)
1745                | StreamElement::TextBox(_) => {}
1746                StreamElement::PedalObject(_) => {
1747                    return Err(abc_error("ABC has no pedal bounces or gaps"));
1748                }
1749                StreamElement::RehearsalMark(_) => {
1750                    return Err(abc_error("ABC has no rehearsal marks"));
1751                }
1752                StreamElement::Break(_) => {
1753                    return Err(abc_error(
1754                        "ABC writes no manuscript's line, page or column breaks",
1755                    ));
1756                }
1757                StreamElement::MetricModulation(_) => {
1758                    return Err(abc_error("ABC has no metric modulations"));
1759                }
1760                StreamElement::Unpitched(_) | StreamElement::PercussionChord(_) => {
1761                    return Err(abc_error("ABC has no unpitched notes"));
1762                }
1763                StreamElement::Note(_) | StreamElement::Chord(_) | StreamElement::Rest(_) => {
1764                    if skipped == Some(held_index) {
1765                        cursor = held.offset + held.element.quarter_length();
1766                        continue;
1767                    }
1768                    let tail = plan.tail.as_ref().filter(|_| tailed == Some(held_index));
1769                    let sounding = sounding_of(*held)?
1770                        .ok_or_else(|| abc_error("a note with nothing to write it from"))?;
1771                    let opens = self.opens.get(&held.leaf).map_or(&[][..], Vec::as_slice);
1772                    let closes: String = self
1773                        .closes
1774                        .get(&held.leaf)
1775                        .map_or(&[][..], Vec::as_slice)
1776                        .iter()
1777                        .map(|kind| spanner_close(*kind))
1778                        .collect();
1779                    let marker = markers.get(&(index, held_index));
1780                    if marker.is_some() || !opens.is_empty() {
1781                        first_token = false;
1782                    }
1783                    let mut opening = String::new();
1784                    for kind in opens {
1785                        opening.push_str(spanner_open(*kind)?);
1786                    }
1787                    if sounding.grace {
1788                        if graces.is_empty() {
1789                            lead.push_str(marker.map_or("", String::as_str));
1790                            lead.push_str(&opening);
1791                            grace_slash = sounding
1792                                .duration
1793                                .grace()
1794                                .is_some_and(crate::duration::Grace::slash);
1795                        } else if marker.is_some() || !opening.is_empty() {
1796                            // A slur opened on a later grace note is opened
1797                            // before the group.
1798                            lead.push_str(&opening);
1799                        }
1800                        graces.push_str(&self.note_text(&sounding, unit, &reading.alters, None)?);
1801                        grace_closes.push_str(&closes);
1802                        first_token = false;
1803                        continue;
1804                    }
1805
1806                    // A rest for time nothing fills.
1807                    if held.offset > cursor + EPSILON {
1808                        music.push(format!("x{}", length_text(held.offset - cursor, unit)?));
1809                        first_token = false;
1810                    } else if held.offset < cursor - EPSILON {
1811                        return Err(abc_error(
1812                            "notes sound together outside a chord; ABC writes one line to a voice",
1813                        ));
1814                    }
1815                    if waiting_since.is_some_and(|since| since < held.offset - EPSILON) {
1816                        return Err(abc_error(
1817                            "a chord symbol, dynamic or text stands where no note starts",
1818                        ));
1819                    }
1820                    waiting_since = None;
1821
1822                    let mut token = std::mem::take(&mut lead);
1823                    if !graces.is_empty() {
1824                        let _ = write!(
1825                            token,
1826                            "{{{}{}}}{}",
1827                            if grace_slash { "/" } else { "" },
1828                            std::mem::take(&mut graces),
1829                            std::mem::take(&mut grace_closes)
1830                        );
1831                        token.push_str(marker.map_or("", String::as_str));
1832                    } else if let Some(marker) = marker {
1833                        token.push_str(marker);
1834                    }
1835                    token.push_str(&opening);
1836                    if first_token
1837                        && token.is_empty()
1838                        && !articulations_of(held.element).iter().any(is_lettered)
1839                    {
1840                        written.starts_with_note = true;
1841                    }
1842                    first_token = false;
1843                    token.push_str(symbol.take().as_deref().unwrap_or(""));
1844                    token.push_str(&std::mem::take(&mut before));
1845                    token.push_str(&self.note_text(&sounding, unit, &reading.alters, tail)?);
1846                    token.push_str(&closes);
1847                    token.push_str(tail.map_or("", |tail| tail.closes.as_str()));
1848                    music.push(token);
1849                    cursor = held.offset + held.element.quarter_length();
1850
1851                    match held.element {
1852                        StreamElement::Note(note) => written.sung.push(note.lyrics()),
1853                        StreamElement::Chord(chord) => written.sung.push(chord.lyrics()),
1854                        _ => {}
1855                    }
1856                    // Notes under one beam are written without a space.
1857                    let beams = match held.element {
1858                        StreamElement::Note(note) => Some(note.beams()),
1859                        StreamElement::Chord(chord) => Some(chord.beams()),
1860                        _ => None,
1861                    };
1862                    if !beams.is_some_and(beamed_on) {
1863                        music.push(" ".to_string());
1864                    }
1865                }
1866            }
1867        }
1868        if symbol.is_some() || !before.is_empty() {
1869            return Err(abc_error(
1870                "a chord symbol, dynamic or text stands where no note starts",
1871            ));
1872        }
1873        // Grace notes with no note after them, and whatever else waited.
1874        if !graces.is_empty() {
1875            lead.push_str(&format!(
1876                "{{{}{graces}}}{grace_closes}",
1877                if grace_slash { "/" } else { "" }
1878            ));
1879        }
1880        if !lead.is_empty() {
1881            music.push(lead);
1882        }
1883        written.music = music.concat().trim().to_string();
1884        if written.music.is_empty() && voice.measured {
1885            // A measure of nothing is a space.
1886            written.music = "y".to_string();
1887        }
1888
1889        // One field alone after a barline and before a note is passed over
1890        // by music21; with the unit note length said again after it, it and
1891        // the measure are read together.
1892        if let Some((alters, signature)) =
1893            silent_key.filter(|_| written.opening.is_empty() && written.starts_with_note)
1894        {
1895            {
1896                written
1897                    .opening
1898                    .push(format!("K:{}", key_field(&signature.into(), None)?));
1899                reading.alters = alters;
1900                // The notes were written before the key was known to be
1901                // readable under; write them again.
1902                let again =
1903                    self.write_bar(voice, index, bar, unit, unit_text, markers, reading, plan)?;
1904                written.music = again.music;
1905            }
1906        } else if written.opening.len() == 1 {
1907            written.opening.push(format!("L:{unit_text}"));
1908        }
1909        if key_closes && let Some(signature) = stated.and_then(|held| signature_of(held.element)) {
1910            reading.alters = alters_of(&signature);
1911        }
1912        Ok(written)
1913    }
1914
1915    /// A field in the middle of the music: on a line of its own where the
1916    /// voice has no measures, in brackets where it has.
1917    fn field(&self, voice: &Voice<'a>, music: &mut Vec<String>, lead: &mut String, field: String) {
1918        if voice.measured {
1919            let _ = write!(lead, "[{field}]");
1920        } else {
1921            music.push(format!("\n{field}\n"));
1922        }
1923    }
1924
1925    /// The key signature in force at a measure of a voice.
1926    fn key_in_force(&self, voice: &Voice<'a>, index: usize) -> Option<KeySignature> {
1927        voice.bars[..=index]
1928            .iter()
1929            .rev()
1930            .flat_map(|bar| bar.held.iter().rev())
1931            .find_map(|held| signature_of(held.element))
1932    }
1933
1934    /// A note, chord or rest with its length, its decorations before it and
1935    /// its tie after it.
1936    fn note_text(
1937        &self,
1938        sounding: &Sounding<'a>,
1939        unit: FloatType,
1940        alters: &Alters,
1941        tail: Option<&Tail>,
1942    ) -> Result<String> {
1943        let length = length_text(sounding.written + tail.map_or(0.0, |tail| tail.extra), unit)?;
1944        let tied = |note: &Note| match tail {
1945            Some(tail) => tail.tied,
1946            None => matches!(
1947                note.tie().map(crate::notation::Tie::tie_type),
1948                Some(TieType::Start | TieType::Continue)
1949            ),
1950        };
1951        Ok(match sounding.held.element {
1952            StreamElement::Note(note) => {
1953                // A grace note carries its own decorations, inside the
1954                // braces: `{vfga}` bows the first grace note down.
1955                let marks = decorations(note.articulations(), note.expressions())?;
1956                format!(
1957                    "{marks}{}{length}{}",
1958                    pitch_text(note.pitch(), alters)?,
1959                    if tied(note) { "-" } else { "" }
1960                )
1961            }
1962            StreamElement::Chord(chord) => chord_text(chord, alters, &length, tied)?,
1963            StreamElement::Rest(rest) => rest_text(rest, &length)?,
1964            _ => String::new(),
1965        })
1966    }
1967}
1968
1969/// What a voice's first measure opens with.
1970#[derive(Clone, Default)]
1971struct Opening<'a> {
1972    meter: Option<&'a TimeSignature>,
1973    key: Option<&'a StreamElement>,
1974    clef: Option<&'a Clef>,
1975    tempos: Vec<&'a MetronomeMark>,
1976}
1977
1978impl<'a> Opening<'a> {
1979    fn of(held: &[Held<'a>]) -> Self {
1980        let mut opening = Self::default();
1981        for held in held.iter().filter(|held| held.offset < EPSILON) {
1982            match held.element {
1983                StreamElement::TimeSignature(meter) => opening.meter = Some(meter),
1984                StreamElement::Key(_) | StreamElement::KeySignature(_) => {
1985                    opening.key = Some(held.element);
1986                }
1987                StreamElement::Clef(clef) => opening.clef = Some(clef),
1988                StreamElement::MetronomeMark(mark) => opening.tempos.push(mark),
1989                _ => {}
1990            }
1991        }
1992        opening
1993    }
1994}
1995
1996/// Whether a note is beamed to the one after it.
1997fn beamed_on(beams: &Beams) -> bool {
1998    matches!(
1999        beams
2000            .beams()
2001            .first()
2002            .and_then(crate::notation::Beam::beam_type),
2003        Some(BeamType::Start | BeamType::Continue)
2004    )
2005}
2006
2007/// The notes of a measure music21 would read with no accidental written:
2008/// each letter and how far it is moved.
2009fn unmarked_notes(bar: &Bar<'_>, skipped: Option<usize>) -> Vec<(usize, IntegerType)> {
2010    let mut notes = Vec::new();
2011    let mut take = |pitch: &Pitch| {
2012        if !shows_accidental(pitch) {
2013            notes.push((
2014                letter_index(pitch.step().as_char()),
2015                pitch.alter().round() as IntegerType,
2016            ));
2017        }
2018    };
2019    for (index, held) in bar.held.iter().enumerate() {
2020        if skipped == Some(index) {
2021            continue;
2022        }
2023        match held.element {
2024            StreamElement::Note(note) => take(note.pitch()),
2025            StreamElement::Chord(chord) => {
2026                chord.notes().iter().map(Note::pitch).for_each(&mut take)
2027            }
2028            _ => {}
2029        }
2030    }
2031    notes
2032}
2033
2034/// Whether a pitch carries an accidental that is to be seen.
2035fn shows_accidental(pitch: &Pitch) -> bool {
2036    pitch
2037        .written_accidental()
2038        .is_some_and(|accidental| accidental.display_status() != Some(false))
2039}
2040
2041/// A pitch as ABC writes one: an accidental where it shows one or the key
2042/// in force would give it another, the letter, and the octave.
2043fn pitch_text(pitch: &Pitch, alters: &Alters) -> Result<String> {
2044    if pitch
2045        .microtone()
2046        .is_some_and(|microtone| microtone.alter() != 0.0)
2047    {
2048        return Err(abc_error("ABC has no microtones"));
2049    }
2050    let alter = pitch.alter();
2051    if alter.fract() != 0.0 {
2052        return Err(abc_error("ABC has no quarter-tone accidentals"));
2053    }
2054    let alter = alter as IntegerType;
2055    let letter = pitch.step().as_char();
2056    let marks = if shows_accidental(pitch) || alters[letter_index(letter)] != alter {
2057        accidental_marks(alter)?
2058    } else {
2059        ""
2060    };
2061    let octave = pitch.octave().unwrap_or(4);
2062    Ok(if octave >= 5 {
2063        format!(
2064            "{marks}{}{}",
2065            letter.to_ascii_lowercase(),
2066            "'".repeat((octave - 5) as usize)
2067        )
2068    } else {
2069        format!("{marks}{letter}{}", ",".repeat((4 - octave) as usize))
2070    })
2071}
2072
2073fn chord_text(
2074    chord: &Chord,
2075    alters: &Alters,
2076    length: &str,
2077    tied: impl Fn(&Note) -> bool,
2078) -> Result<String> {
2079    let mut text = decorations(chord.articulations(), chord.expressions())?;
2080    text.push('[');
2081    for note in chord.notes() {
2082        text.push_str(&pitch_text(note.pitch(), alters)?);
2083        // A tie written after the chord would be read onto the next note.
2084        if tied(note) {
2085            text.push('-');
2086        }
2087    }
2088    text.push(']');
2089    text.push_str(length);
2090    Ok(text)
2091}
2092
2093fn rest_text(rest: &Rest, length: &str) -> Result<String> {
2094    let marks = decorations(rest.articulations(), rest.expressions())?;
2095    let tied = matches!(
2096        rest.tie().map(crate::notation::Tie::tie_type),
2097        Some(TieType::Start | TieType::Continue)
2098    );
2099    Ok(format!(
2100        "{marks}{}{length}{}",
2101        if rest.hidden() { 'x' } else { 'z' },
2102        if tied { "-" } else { "" }
2103    ))
2104}
2105
2106/// A chord symbol as it is written between quotation marks: a flat as `b`.
2107fn symbol_text(symbol: &ChordSymbol) -> String {
2108    if symbol.is_no_chord() {
2109        return quoted(symbol.kind_text().unwrap_or("N.C."));
2110    }
2111    let figure: Vec<char> = symbol.figure().chars().collect();
2112    let mut text = String::new();
2113    for (index, character) in figure.iter().enumerate() {
2114        let after_letter = index > 0 && matches!(figure[index - 1], 'A'..='G');
2115        text.push(if *character == '-' && after_letter {
2116            'b'
2117        } else {
2118            *character
2119        });
2120    }
2121    quoted(&text)
2122}
2123
2124/// The barline between two measures, or before the first or after the last,
2125/// with the ending the second opens: from the barline closing the one and
2126/// the barline and ending opening the other. The second answer is whether
2127/// the measure after must be kept from taking the barline as its own.
2128///
2129/// One barline written is read as closing the measure before it and opening
2130/// the one after, unless it is a repeat, which is read on its own side only.
2131/// Two different ones are written one after the other.
2132fn barline_text(
2133    right: Option<&Barline>,
2134    left: Option<&Barline>,
2135    ending: Option<&Ending>,
2136    opens_tune: bool,
2137    field_can_follow: bool,
2138) -> (String, bool) {
2139    let token = |barline: Option<&Barline>| -> Option<&'static str> {
2140        let barline = barline?;
2141        match barline.repeat_direction() {
2142            Some(RepeatDirection::End) => Some(":|"),
2143            Some(RepeatDirection::Start) => Some("|:"),
2144            None => match barline.bar_type() {
2145                BarlineType::Double => Some("||"),
2146                BarlineType::Final => Some("|]"),
2147                BarlineType::HeavyLight => Some("[|"),
2148                BarlineType::Dotted | BarlineType::Dashed => Some(":"),
2149                _ => None,
2150            },
2151        }
2152    };
2153    let repeats = |barline: Option<&Barline>| {
2154        barline.is_some_and(|barline| barline.repeat_direction().is_some())
2155    };
2156    let ending = ending.filter(|ending| ending.starts() && !ending.numbers().is_empty());
2157    let mut keep_off = false;
2158    let mut text = match (token(right), token(left)) {
2159        (Some(":|"), Some("|:")) => "::".to_string(),
2160        (Some(closing), Some(opening)) if closing == opening => closing.to_string(),
2161        // A dotted barline before another would be read with it as a repeat.
2162        (Some(":"), Some(opening)) => format!(": {opening}"),
2163        (Some(closing), Some(opening)) => format!("{closing}{opening}"),
2164        (Some(closing), None) if repeats(right) || opens_tune => closing.to_string(),
2165        // A barline that is not a repeat is read as the next measure's too.
2166        // A field after it keeps it off, and so does a plain barline.
2167        (Some(closing), None) if field_can_follow && ending.is_none() => {
2168            keep_off = true;
2169            closing.to_string()
2170        }
2171        (Some(closing), None) => format!("{closing} |"),
2172        (None, Some(opening)) if opens_tune || repeats(left) => opening.to_string(),
2173        // The plain barline closes the measure before and the other opens
2174        // the one after.
2175        (None, Some(opening)) => format!("| {opening}"),
2176        // Nothing is written before the first measure but what it opens
2177        // with.
2178        (None, None) if opens_tune => String::new(),
2179        (None, None) => "|".to_string(),
2180    };
2181    if let Some(ending) = ending {
2182        let numbers = ending
2183            .numbers()
2184            .iter()
2185            .map(u32::to_string)
2186            .collect::<Vec<_>>()
2187            .join(",");
2188        if !numbers.is_empty() {
2189            // `|1` and `:|2` are the forms music21 reads; after any other
2190            // barline the ending is opened by its bracket.
2191            if matches!(text.as_str(), "|" | ":|") && matches!(numbers.as_str(), "1" | "2") {
2192                text.push_str(&numbers);
2193            } else if matches!(numbers.as_str(), "1" | "2") {
2194                let _ = write!(text, "[{numbers}");
2195            } else if text.is_empty() {
2196                let _ = write!(text, "|{numbers}");
2197            } else {
2198                text.push_str(&numbers);
2199            }
2200        }
2201    }
2202    (text, keep_off)
2203}
2204
2205/// The `w:` lines for the notes of one line of music, a line for each verse.
2206fn lyric_lines(sung: &[&[Lyric]]) -> Vec<String> {
2207    let mut numbers: Vec<IntegerType> = sung
2208        .iter()
2209        .flat_map(|lyrics| lyrics.iter().map(Lyric::number))
2210        .collect();
2211    numbers.sort_unstable();
2212    numbers.dedup();
2213    if numbers.is_empty() {
2214        // Notes with no words still take their place, so the next line of
2215        // words starts at its own notes.
2216        numbers.push(1);
2217    }
2218    numbers
2219        .into_iter()
2220        .map(|number| {
2221            let mut words: Vec<String> = Vec::new();
2222            // The syllables of the word being written, a skipped note an
2223            // empty one.
2224            let mut word: Option<Vec<String>> = None;
2225            for lyrics in sung {
2226                let lyric = lyrics.iter().find(|lyric| lyric.number() == number);
2227                match lyric {
2228                    None => match &mut word {
2229                        Some(syllables) => syllables.push(String::new()),
2230                        None => words.push("*".to_string()),
2231                    },
2232                    Some(lyric) => {
2233                        let raw = lyric.raw_text();
2234                        let continues = raw.len() > 1 && raw.ends_with('-');
2235                        let syllable = lyric.text().replace(' ', "~").replace('-', "\\-");
2236                        let syllable = if syllable.is_empty() {
2237                            "*".to_string()
2238                        } else {
2239                            syllable
2240                        };
2241                        let joins = raw.len() > 1 && raw.starts_with('-');
2242                        match &mut word {
2243                            Some(syllables) if joins => syllables.push(syllable),
2244                            _ => {
2245                                if let Some(syllables) = word.take() {
2246                                    words.push(syllables.join("-"));
2247                                }
2248                                word = Some(vec![syllable]);
2249                            }
2250                        }
2251                        if !continues && let Some(syllables) = word.take() {
2252                            words.push(syllables.join("-"));
2253                        }
2254                    }
2255                }
2256            }
2257            if let Some(mut syllables) = word.take() {
2258                // A word left open says so with the hyphen after it.
2259                syllables.push(String::new());
2260                while syllables.len() > 2 && syllables[syllables.len() - 2].is_empty() {
2261                    syllables.pop();
2262                }
2263                words.push(syllables.join("-"));
2264            }
2265            words.join(" ")
2266        })
2267        .collect()
2268}
2269
2270fn push_line(out: &mut String, line: &str) {
2271    // A field in the middle of a voice with no measures brings its own
2272    // line breaks.
2273    for piece in line.split('\n') {
2274        let piece = piece.trim();
2275        if !piece.is_empty() {
2276            out.push_str(piece);
2277            out.push('\n');
2278        }
2279    }
2280}
2281
2282#[cfg(test)]
2283mod tests {
2284    use super::*;
2285    use crate::abc::from_abc;
2286
2287    /// What a tune is written as, having checked that it reads back as the
2288    /// score it was written from.
2289    fn round_trip(tune: &str) -> String {
2290        let score = from_abc(tune).unwrap();
2291        let written = to_abc(&score, &ExportOptions::default()).unwrap();
2292        assert_eq!(
2293            format!("{:#?}", from_abc(&written).unwrap()),
2294            format!("{score:#?}"),
2295            "read back differently:\n{written}"
2296        );
2297        written
2298    }
2299
2300    #[test]
2301    fn a_tune_is_written_with_its_header() {
2302        let written = round_trip(
2303            "X:7\nT:Tune\nT:Other\nC:Anon\nO:Here\nM:6/8\nL:1/8\nQ:3/8=120\nK:Ador\nABc def|gfe dcB|A6|]\n",
2304        );
2305        assert_eq!(
2306            written,
2307            "X:7\nT:Tune\nT:Other\nC:Anon\nO:Here\nM:6/8\nL:1/8\nQ:3/8=120\nK:Ador\nABc def | gfe dcB | A6 |]\n"
2308        );
2309    }
2310
2311    #[test]
2312    fn accidentals_octaves_and_lengths_are_written() {
2313        let written = round_trip("X:1\nM:4/4\nL:1/4\nK:G\n^C,/ _e'3/2 =F f/4 z3/4|c4|C4|]\n");
2314        assert!(
2315            written.contains("L:1/8\nK:G\n^C, _e'3 =F2 f/ z3/2 | c8 | C8 |]"),
2316            "{written}"
2317        );
2318    }
2319
2320    /// A part of whole notes in measures, holding what `opening` gives its
2321    /// first measure.
2322    fn part_of(names: &[&str], opening: Vec<StreamElement>) -> Stream {
2323        let mut part = Stream::with_kind(StreamKind::Part);
2324        for (index, name) in names.iter().enumerate() {
2325            let mut measure = Stream::with_kind(StreamKind::Measure);
2326            if index == 0 {
2327                for element in &opening {
2328                    measure.insert(0.0, element.clone());
2329                }
2330            }
2331            measure.insert(
2332                0.0,
2333                Note::from_name(name)
2334                    .unwrap()
2335                    .with_duration(Duration::whole()),
2336            );
2337            part.insert(index as FloatType * 4.0, measure);
2338        }
2339        part
2340    }
2341
2342    #[test]
2343    fn a_part_says_its_name_and_its_instrument_s_program() {
2344        let mut part = part_of(
2345            &["C4", "D4"],
2346            vec![StreamElement::TimeSignature(
2347                TimeSignature::new(4, 4).unwrap(),
2348            )],
2349        );
2350        part.set_name(Some("Guitar".to_string()));
2351        let mut guitar = crate::instrument::Instrument::new();
2352        guitar.set_midi_program(Some(24));
2353        part.insert(0.0, StreamElement::Instrument(Box::new(guitar)));
2354        let written = to_abc(&part, &ExportOptions::default()).unwrap();
2355        assert!(
2356            written.contains(
2357                "V:1 name=\"Guitar\"
2358%%MIDI program 24
2359"
2360            ),
2361            "{written}"
2362        );
2363    }
2364
2365    #[test]
2366    fn a_clef_other_than_treble_is_said_even_where_the_notes_imply_it() {
2367        let low = part_of(
2368            &["C2", "G2"],
2369            vec![
2370                StreamElement::Clef(Clef::of_kind(crate::clef::ClefKind::BassClef)),
2371                StreamElement::TimeSignature(TimeSignature::new(4, 4).unwrap()),
2372            ],
2373        );
2374        let written = to_abc(&low, &ExportOptions::default()).unwrap();
2375        assert!(written.contains("clef=bass"), "{written}");
2376        let high = part_of(
2377            &["C5", "G5"],
2378            vec![
2379                StreamElement::Clef(Clef::of_kind(crate::clef::ClefKind::TrebleClef)),
2380                StreamElement::TimeSignature(TimeSignature::new(4, 4).unwrap()),
2381            ],
2382        );
2383        let written = to_abc(&high, &ExportOptions::default()).unwrap();
2384        assert!(!written.contains("clef="), "{written}");
2385    }
2386}