Skip to main content

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