Skip to main content

music21_rs/figuredbass/
realizer.rs

1//! Realizing a figured bass line: music21's `figuredBass.realizer`.
2//!
3//! A [`FiguredBassLine`] is a run of bass notes, each with its figures, in a
4//! key and a meter. Realizing it makes a [`Segment`] of each note as the
5//! line is written -- a note cut at a barline is two -- finds every pair of
6//! voicings each segment may move to the next by, and drops the voicings
7//! that lead nowhere, leaving a [`Realization`]: every way of voicing the
8//! whole line that keeps the rules, which it can count, list, pick from,
9//! and write out as a score.
10
11use crate::chord::Chord;
12use crate::clef::Clef;
13use crate::defaults::FloatType;
14use crate::duration::Duration;
15use crate::error::{Error, Result};
16use crate::figuredbass::Notation;
17use crate::figuredbass::rules::Rules;
18use crate::figuredbass::scale::{FiguredBassMode, FiguredBassScale};
19use crate::figuredbass::segment::{Possibility, Segment};
20use crate::key::{Key, KeySignature};
21use crate::makenotation::{make_part_notation_by, op_frac};
22use crate::meter::TimeSignature;
23use crate::note::Note;
24use crate::pitch::Pitch;
25use crate::stream::{Stream, StreamElement, StreamKind};
26
27/// One thing a bass line is realized over.
28#[derive(Clone, Debug, PartialEq)]
29enum Element {
30    /// A bass note and the figures written under it.
31    Figured {
32        bass: Pitch,
33        quarter_length: FloatType,
34        notation: Option<String>,
35    },
36    /// A bass note and the names of the notes above it, as music21 reads a
37    /// chord symbol or a roman numeral.
38    Named {
39        bass: Pitch,
40        quarter_length: FloatType,
41        pitch_names: Vec<String>,
42    },
43}
44
45/// One note or rest of a part laid over the line: where it starts, how long
46/// it lasts, and its pitch where it is a note.
47#[derive(Clone, Debug, PartialEq)]
48struct Sounding {
49    offset: FloatType,
50    quarter_length: FloatType,
51    pitch: Option<Pitch>,
52}
53
54impl Sounding {
55    fn end(&self) -> FloatType {
56        op_frac(self.offset + self.quarter_length)
57    }
58}
59
60/// One written note of the bass line: a bass note, or one of the pieces a
61/// barline cut it into.
62#[derive(Clone, Debug)]
63struct Piece {
64    offset: FloatType,
65    note: Note,
66    notation: Option<String>,
67}
68
69impl Piece {
70    fn quarter_length(&self) -> FloatType {
71        self.note.duration().map_or(1.0, Duration::quarter_length)
72    }
73}
74
75/// What sounds in one part during a moment of the line.
76#[derive(Clone, Copy, Debug, PartialEq)]
77enum Voice {
78    /// A note of the part laid over the line of that number, by its place
79    /// there.
80    Upper(usize, usize),
81    /// A piece of the bass line, by its place there.
82    Bass(usize),
83}
84
85/// A stretch of the line through which every part holds one note: its
86/// start, its end, and what each part sounds, highest first.
87type Moment = ((FloatType, FloatType), Vec<Voice>);
88
89/// A bass line and its figures, in a key and a meter.
90///
91/// ```
92/// use music21_rs::Pitch;
93/// use music21_rs::figuredbass::realizer::FiguredBassLine;
94/// use music21_rs::figuredbass::rules::Rules;
95///
96/// let mut line = FiguredBassLine::default();
97/// line.add_element(Pitch::from_name("C3")?, 1.0, None);
98/// line.add_element(Pitch::from_name("D3")?, 1.0, Some("4,3"));
99/// line.add_element(Pitch::from_name("C3")?, 2.0, None);
100/// let realization = line.realize(&Rules::default(), 4, &Pitch::from_name("B5")?)?;
101/// assert_eq!(realization.num_solutions(), 30);
102/// # Ok::<(), music21_rs::Error>(())
103/// ```
104#[derive(Clone, Debug, PartialEq)]
105#[must_use]
106pub struct FiguredBassLine {
107    scale: FiguredBassScale,
108    time: TimeSignature,
109    elements: Vec<Element>,
110    overlaid: Vec<Vec<Sounding>>,
111}
112
113impl Default for FiguredBassLine {
114    /// A line in C major and common time, with nothing in it yet.
115    fn default() -> Self {
116        Self::new(FiguredBassScale::default())
117    }
118}
119
120/// music21's meter for a line that names none.
121fn common_time() -> TimeSignature {
122    TimeSignature::new(4, 4).expect("four quarters make a bar")
123}
124
125impl FiguredBassLine {
126    /// An empty line in common time whose figures are read in `scale`.
127    pub fn new(scale: FiguredBassScale) -> Self {
128        Self {
129            scale,
130            time: common_time(),
131            elements: Vec::new(),
132            overlaid: Vec::new(),
133        }
134    }
135
136    /// An empty line in `key` and common time. The key must be in one of
137    /// the five modes a figured bass is read in.
138    ///
139    /// # Errors
140    ///
141    /// A key in another mode.
142    pub fn in_key(key: &Key) -> Result<Self> {
143        let tonic = key.pitch_from_degree(1)?;
144        let mode = FiguredBassMode::from_name(key.mode())?;
145        Ok(Self::new(FiguredBassScale::new(tonic, mode)?))
146    }
147
148    /// An empty line in `key` and `time`: music21's
149    /// `FiguredBassLine(inKey, inTime)`.
150    ///
151    /// # Errors
152    ///
153    /// A key in a mode no figured bass is read in.
154    pub fn in_key_and_time(key: &Key, time: TimeSignature) -> Result<Self> {
155        let mut line = Self::in_key(key)?;
156        line.time = time;
157        Ok(line)
158    }
159
160    /// The scale its figures are read in.
161    pub fn scale(&self) -> &FiguredBassScale {
162        &self.scale
163    }
164
165    /// The meter it is written in: music21's `inTime`.
166    pub fn time_signature(&self) -> &TimeSignature {
167        &self.time
168    }
169
170    /// Adds a bass note lasting `quarter_length`, with the figures written
171    /// under it, if any: music21's `addElement` of a note.
172    pub fn add_element(&mut self, bass: Pitch, quarter_length: FloatType, notation: Option<&str>) {
173        self.elements.push(Element::Figured {
174            bass,
175            quarter_length,
176            notation: notation.map(str::to_string),
177        });
178    }
179
180    /// Adds a bass note with the names of the notes above it rather than
181    /// figures, as music21 reads a chord symbol or a roman numeral into a
182    /// line. A chord lasting nothing is realized as lasting a quarter, as
183    /// music21's is.
184    pub fn add_chord(&mut self, bass: Pitch, quarter_length: FloatType, pitch_names: Vec<String>) {
185        self.elements.push(Element::Named {
186            bass,
187            quarter_length: if quarter_length == 0.0 {
188                1.0
189            } else {
190                quarter_length
191            },
192            pitch_names,
193        });
194    }
195
196    /// How many bass notes it has.
197    pub fn len(&self) -> usize {
198        self.elements.len()
199    }
200
201    /// Whether it has none.
202    pub fn is_empty(&self) -> bool {
203        self.elements.is_empty()
204    }
205
206    /// Lays a written part over the line, to be kept as it is by every
207    /// realization: music21's `overlayPart`.
208    ///
209    /// Each note of the part holds one part of the voicing to its pitch
210    /// while it sounds, the first part laid over being the highest. Where
211    /// it moves and the bass does not, the line gains a segment of no
212    /// length, into which only the parts laid over may move. As in music21,
213    /// the first segment of the line is not held to the parts laid over it.
214    ///
215    /// Only the part's notes and rests are read, at their offsets from its
216    /// start.
217    pub fn overlay_part(&mut self, part: &Stream) {
218        let flat = part.flatten();
219        let mut sounding: Vec<Sounding> = flat
220            .events()
221            .iter()
222            .filter_map(|event| {
223                let pitch = match event.element() {
224                    StreamElement::Note(note) => Some(note.pitch().clone()),
225                    StreamElement::Chord(_)
226                    | StreamElement::Rest(_)
227                    | StreamElement::Unpitched(_)
228                    | StreamElement::PercussionChord(_)
229                    | StreamElement::ChordSymbol(_) => None,
230                    _ => return None,
231                };
232                Some(Sounding {
233                    offset: event.offset(),
234                    quarter_length: event.element().quarter_length(),
235                    pitch,
236                })
237            })
238            .collect();
239        sounding.sort_by(|left, right| left.offset.total_cmp(&right.offset));
240        self.overlaid.push(sounding);
241    }
242
243    /// The bass line written out as a part: a bass clef, the key signature,
244    /// the meter and the bass notes one after another, each with its
245    /// figures under it as lyrics, cut into measures with the notes that
246    /// run past a barline tied, tuplets completed and bracketed and the
247    /// notes beamed. music21's `generateBassLine`.
248    ///
249    /// ```
250    /// use music21_rs::figuredbass::realizer::FiguredBassLine;
251    /// use music21_rs::{Key, Pitch, TimeSignature};
252    ///
253    /// let key = Key::from_tonic("B")?;
254    /// let mut line = FiguredBassLine::in_key_and_time(&key, TimeSignature::new(3, 4)?)?;
255    /// line.add_element(Pitch::from_name("B2")?, 1.0, None);
256    /// line.add_element(Pitch::from_name("C#3")?, 1.0, Some("6"));
257    /// line.add_element(Pitch::from_name("D#3")?, 2.5, Some("6"));
258    /// let written = line.generate_bass_line()?;
259    /// assert_eq!(written.measures().len(), 2);
260    /// assert_eq!(written.notes().len(), 4);
261    /// # Ok::<(), music21_rs::Error>(())
262    /// ```
263    ///
264    /// # Errors
265    ///
266    /// A line holding chords rather than bass notes and figures, figures
267    /// that cannot be read, or a length no duration has.
268    pub fn generate_bass_line(&self) -> Result<Stream> {
269        let mut line = Stream::with_kind(StreamKind::Part);
270        line.push(Clef::bass());
271        line.push(self.scale.key_signature().clone());
272        line.push(self.time.clone());
273        for element in &self.elements {
274            let Element::Figured {
275                bass,
276                quarter_length,
277                notation,
278            } = element
279            else {
280                return Err(Error::FiguredBass(
281                    "a bass line is written from bass notes and their figures, not from chords"
282                        .to_string(),
283                ));
284            };
285            line.push(bass_note(bass, *quarter_length, notation.as_deref())?);
286        }
287        notated(&line)
288    }
289
290    /// The bass line as its written notes, each with the figures of the
291    /// bass note it is, or is a piece of.
292    fn pieces(&self) -> Result<Vec<Piece>> {
293        // Where each bass note starts and stops.
294        let mut spans: Vec<(FloatType, FloatType, Option<&str>)> = Vec::new();
295        let mut start = 0.0;
296        for element in &self.elements {
297            if let Element::Figured {
298                quarter_length,
299                notation,
300                ..
301            } = element
302            {
303                let stop = op_frac(start + quarter_length);
304                spans.push((start, stop, notation.as_deref()));
305                start = stop;
306            }
307        }
308        let written = self.generate_bass_line()?.flatten();
309        Ok(written
310            .events()
311            .iter()
312            .filter_map(|event| match event.element() {
313                StreamElement::Note(note) => {
314                    let offset = event.offset();
315                    let notation = spans
316                        .iter()
317                        .find(|(start, stop, _)| *start <= offset + 1e-9 && offset < stop - 1e-9)
318                        .or(spans.last())
319                        .and_then(|(_, _, notation)| *notation);
320                    Some(Piece {
321                        offset,
322                        note: note.clone(),
323                        notation: notation.map(str::to_string),
324                    })
325                }
326                _ => None,
327            })
328            .collect())
329    }
330
331    /// The moments of the line and the parts laid over it, in order:
332    /// music21's `checker.extractHarmonies` over the parts and the bass,
333    /// or its `createOffsetMapping` of the bass where no part is laid over.
334    fn moments(&self, pieces: &[Piece]) -> Vec<Moment> {
335        let bass: Vec<(Sounding, Voice)> = pieces
336            .iter()
337            .enumerate()
338            .map(|(index, piece)| {
339                let sounding = Sounding {
340                    offset: piece.offset,
341                    quarter_length: piece.quarter_length(),
342                    pitch: None,
343                };
344                (sounding, Voice::Bass(index))
345            })
346            .collect();
347        let mut parts: Vec<Vec<(Sounding, Voice)>> = self
348            .overlaid
349            .iter()
350            .enumerate()
351            .map(|(part, sounding)| {
352                sounding
353                    .iter()
354                    .enumerate()
355                    .map(|(index, each)| (each.clone(), Voice::Upper(part, index)))
356                    .collect()
357            })
358            .collect();
359        parts.push(bass);
360
361        // The first part's notes, those starting and ending together sharing
362        // a moment.
363        let mut moments: Vec<Moment> = Vec::new();
364        for (sounding, voice) in &parts[0] {
365            let key = (sounding.offset, sounding.end());
366            match moments.iter_mut().find(|(held, _)| *held == key) {
367                Some((_, voices)) => voices.push(*voice),
368                None => moments.push((key, vec![*voice])),
369            }
370        }
371        // Each part after it cuts the moments where its own notes change.
372        for part in &parts[1..] {
373            moments.sort_by(|left, right| compare_spans(left.0, right.0));
374            let mut cut: Vec<Moment> = Vec::new();
375            for ((start, end), voices) in &moments {
376                for (sounding, voice) in part {
377                    if !overlaps(sounding, *start, *end) {
378                        continue;
379                    }
380                    let begins = if sounding.offset < *start {
381                        *start
382                    } else {
383                        sounding.offset
384                    };
385                    let ends = if sounding.offset + sounding.quarter_length > *end {
386                        *end
387                    } else {
388                        sounding.end()
389                    };
390                    let mut together = voices.clone();
391                    together.push(*voice);
392                    match cut.iter_mut().find(|(held, _)| *held == (begins, ends)) {
393                        Some((_, held)) => *held = together,
394                        None => cut.push(((begins, ends), together)),
395                    }
396                }
397            }
398            moments = cut;
399        }
400        moments.sort_by(|left, right| compare_spans(left.0, right.0));
401        moments
402    }
403
404    /// A segment for each bass note as the line is written, voiced in
405    /// `num_parts` parts under `rules`, no part above `max_pitch`: music21's
406    /// `retrieveSegments`.
407    ///
408    /// A bass note cut by a barline is a segment for each piece, and a part
409    /// laid over the line holds its part of each segment to its pitch. A
410    /// line of chords is a segment for each chord.
411    ///
412    /// # Errors
413    ///
414    /// Figures the line's scale cannot read, or a part laid over the line
415    /// that sounds something other than a note where a segment needs its
416    /// pitch.
417    pub fn segments(
418        &self,
419        rules: &Rules,
420        num_parts: usize,
421        max_pitch: &Pitch,
422    ) -> Result<Vec<Segment>> {
423        Ok(self
424            .written_segments(rules, num_parts, max_pitch)?
425            .into_iter()
426            .map(|(segment, _)| segment)
427            .collect())
428    }
429
430    /// [`FiguredBassLine::segments`], each with the bass note written under
431    /// it.
432    fn written_segments(
433        &self,
434        rules: &Rules,
435        num_parts: usize,
436        max_pitch: &Pitch,
437    ) -> Result<Vec<(Segment, Note)>> {
438        if self.elements.is_empty() {
439            return Ok(Vec::new());
440        }
441        // music21 reads a line holding any chord as a line of chords, one
442        // segment each, with no bass line written.
443        if self
444            .elements
445            .iter()
446            .any(|element| matches!(element, Element::Named { .. }))
447        {
448            return self
449                .elements
450                .iter()
451                .map(|element| match element {
452                    Element::Figured {
453                        bass,
454                        quarter_length,
455                        notation,
456                    } => Ok((
457                        Segment::new(
458                            bass.clone(),
459                            *quarter_length,
460                            &Notation::parse(notation.as_deref().unwrap_or(""))?,
461                            &self.scale,
462                            rules.clone(),
463                            num_parts,
464                            max_pitch.clone(),
465                        )?
466                        .overlaid(),
467                        bass_note(bass, *quarter_length, notation.as_deref())?,
468                    )),
469                    Element::Named {
470                        bass,
471                        quarter_length,
472                        pitch_names,
473                    } => Ok((
474                        Segment::from_pitch_names(
475                            bass.clone(),
476                            *quarter_length,
477                            pitch_names.clone(),
478                            rules.clone(),
479                            num_parts,
480                            max_pitch.clone(),
481                        )?,
482                        Note::from_pitch(Pitch::from_name(bass.name_with_octave())?)
483                            .with_duration(Duration::new(*quarter_length)?),
484                    )),
485                })
486                .collect();
487        }
488
489        let pieces = self.pieces()?;
490        let moments = self.moments(&pieces);
491        let segment_of = |piece: &Piece, quarter_length: FloatType| -> Result<Segment> {
492            Ok(Segment::new(
493                piece.note.pitch().clone(),
494                quarter_length,
495                &Notation::parse(piece.notation.as_deref().unwrap_or(""))?,
496                &self.scale,
497                rules.clone(),
498                num_parts,
499                max_pitch.clone(),
500            )?
501            .overlaid())
502        };
503        let bass_of = |voices: &[Voice]| -> Result<usize> {
504            match voices.last() {
505                Some(Voice::Bass(index)) => Ok(*index),
506                _ => Err(Error::FiguredBass(
507                    "a moment of the line has no bass note".to_string(),
508                )),
509            }
510        };
511
512        let mut made: Vec<(Segment, Note)> = Vec::new();
513        let Some((_, first)) = moments.first() else {
514            return Ok(made);
515        };
516        let first = &pieces[bass_of(first)?];
517        made.push((
518            segment_of(first, pieces[0].quarter_length())?,
519            first.note.clone(),
520        ));
521        // The last piece of the bass a segment has taken its length from.
522        let mut reached = 0;
523        for ((start, _), voices) in &moments[1..] {
524            let piece = &pieces[bass_of(voices)?];
525            let before = &pieces[reached];
526            let quarter_length = if *start == op_frac(before.offset + before.quarter_length()) {
527                reached += 1;
528                let Some(next) = pieces.get(reached) else {
529                    return Err(Error::FiguredBass(
530                        "the parts laid over the line run past its last bass note".to_string(),
531                    ));
532                };
533                next.quarter_length()
534            } else {
535                // The bass holds while a part laid over it moves: the parts
536                // not laid over stay where they were.
537                if let Some((previous, _)) = made.last_mut() {
538                    previous
539                        .rules
540                        .parts_to_check
541                        .extend(voices.len()..=num_parts);
542                }
543                0.0
544            };
545            let mut segment = segment_of(piece, quarter_length)?;
546            for (number, voice) in voices[..voices.len() - 1].iter().enumerate() {
547                let Voice::Upper(part, index) = voice else {
548                    continue;
549                };
550                let Some(pitch) = self.overlaid[*part][*index].pitch.clone() else {
551                    return Err(Error::FiguredBass(
552                        "only a note of a part laid over the line can hold a part to its pitch"
553                            .to_string(),
554                    ));
555                };
556                segment.rules.part_pitch_limits.push((number + 1, pitch));
557            }
558            made.push((segment, piece.note.clone()));
559        }
560        Ok(made)
561    }
562
563    /// Every way of voicing the line in `num_parts` parts that keeps
564    /// `rules`, no part above `max_pitch`: music21's `realize`.
565    ///
566    /// # Errors
567    ///
568    /// A line with nothing in it, figures its scale cannot read, or a
569    /// resolution that fails.
570    pub fn realize(
571        &self,
572        rules: &Rules,
573        num_parts: usize,
574        max_pitch: &Pitch,
575    ) -> Result<Realization> {
576        if self.elements.is_empty() {
577            return Err(Error::FiguredBass(
578                "No (bassNote, notationString) pairs to realize.".to_string(),
579            ));
580        }
581        let (segments, bass_notes) = self
582            .written_segments(rules, num_parts, max_pitch)?
583            .into_iter()
584            .unzip();
585        let mut realization = Realization::of(segments)?;
586        realization.key_signature = self.scale.key_signature().clone();
587        realization.time = self.time.clone();
588        realization.bass_notes = bass_notes;
589        Ok(realization)
590    }
591}
592
593/// Which of two spans comes first, by its start and then by its end.
594fn compare_spans(
595    left: (FloatType, FloatType),
596    right: (FloatType, FloatType),
597) -> std::cmp::Ordering {
598    left.0.total_cmp(&right.0).then(left.1.total_cmp(&right.1))
599}
600
601/// Whether a note sounds during a span: music21's `getElementsByOffset`
602/// taking in what begins before the span and leaving out what only touches
603/// either end of it.
604fn overlaps(sounding: &Sounding, start: FloatType, end: FloatType) -> bool {
605    let stop = sounding.end();
606    if sounding.offset > end || stop < start {
607        return false;
608    }
609    if start == end && sounding.quarter_length == 0.0 {
610        return true;
611    }
612    sounding.offset != end && stop != start
613}
614
615/// A bass note with its figures written under it as lyrics, one a line,
616/// padded to one width: music21's `addLyricsToBassNote`.
617fn bass_note(bass: &Pitch, quarter_length: FloatType, notation: Option<&str>) -> Result<Note> {
618    let mut note = Note::from_pitch(bass.clone()).with_duration(Duration::new(quarter_length)?);
619    let notation = Notation::parse(notation.unwrap_or(""))?;
620    let figures = notation.figure_strings();
621    let width = figures
622        .iter()
623        .map(|figure| figure.chars().count())
624        .max()
625        .unwrap_or(0);
626    for figure in figures {
627        note.add_lyric(&format!("{figure:>width$}"), None, true)?;
628    }
629    Ok(note)
630}
631
632/// A part cut into measures and written out: music21's `makeNotation` with
633/// `cautionaryNotImmediateRepeat=False`, which is how the realizer writes
634/// every part it makes.
635fn notated(part: &Stream) -> Result<Stream> {
636    let mut made = part.clone();
637    make_part_notation_by(&mut made, false)?;
638    Ok(made)
639}
640
641/// Where each voicing of one segment may go in the next, in the order the
642/// voicings were found.
643type Movements = Vec<(Possibility, Vec<Possibility>)>;
644
645/// Every way of voicing a figured bass line: music21's `Realization`.
646#[derive(Clone, Debug)]
647#[must_use]
648pub struct Realization {
649    segments: Vec<Segment>,
650    /// For each segment but the last, where its voicings may go.
651    movements: Vec<Movements>,
652    /// The voicings of a line of one segment.
653    single: Vec<Possibility>,
654    /// What each chord that fell back to an ordinary resolution warned.
655    fallbacks: Vec<String>,
656    /// The key signature and the meter a realization is written in.
657    key_signature: KeySignature,
658    time: TimeSignature,
659    /// The bass note written under each segment.
660    bass_notes: Vec<Note>,
661    keyboard_style: bool,
662}
663
664impl Realization {
665    /// The realization of `segments`, a line's segments in order. Written
666    /// out, it is in C major and common time, each segment's bass lasting
667    /// as long as the segment; a realization made by
668    /// [`FiguredBassLine::realize`] is written as its line is.
669    ///
670    /// # Errors
671    ///
672    /// No segments, or a resolution that fails.
673    pub fn of(mut segments: Vec<Segment>) -> Result<Self> {
674        if segments.is_empty() {
675            return Err(Error::FiguredBass(
676                "No (bassNote, notationString) pairs to realize.".to_string(),
677            ));
678        }
679        let mut movements: Vec<Movements> = Vec::new();
680        let mut fallbacks = Vec::new();
681        let mut single = Vec::new();
682        if segments.len() == 1 {
683            single = segments[0].all_correct_single_possibilities()?;
684        }
685        for index in 0..segments.len().saturating_sub(1) {
686            let (before, after) = segments.split_at_mut(index + 1);
687            let found = before[index].all_correct_consecutive_possibilities(&mut after[0])?;
688            fallbacks.extend(found.fallback);
689            let mut moves: Movements = Vec::new();
690            for (from, to) in found.pairs {
691                match moves.iter_mut().find(|(known, _)| *known == from) {
692                    Some((_, targets)) => targets.push(to),
693                    None => moves.push((from, vec![to])),
694                }
695            }
696            movements.push(moves);
697        }
698        trim(&mut movements);
699        let bass_notes = segments
700            .iter()
701            .map(|segment| {
702                Ok(Note::from_pitch(segment.bass().clone())
703                    .with_duration(Duration::new(segment.quarter_length())?))
704            })
705            .collect::<Result<Vec<Note>>>()?;
706        Ok(Self {
707            segments,
708            movements,
709            single,
710            fallbacks,
711            key_signature: KeySignature::new(0),
712            time: common_time(),
713            bass_notes,
714            keyboard_style: true,
715        })
716    }
717
718    /// The segments realized, one for each bass note.
719    pub fn segments(&self) -> &[Segment] {
720        &self.segments
721    }
722
723    /// What each chord that found no resolution of its own warned, in
724    /// order.
725    pub fn fallbacks(&self) -> &[String] {
726        &self.fallbacks
727    }
728
729    /// How many ways there are of voicing the whole line: music21's
730    /// `getNumSolutions`.
731    pub fn num_solutions(&self) -> usize {
732        if self.segments.len() == 1 {
733            return self.single.len();
734        }
735        let mut paths: Vec<(&Possibility, usize)> = Vec::new();
736        for (depth, moves) in self.movements.iter().rev().enumerate() {
737            paths = moves
738                .iter()
739                .map(|(from, targets)| {
740                    let count = if depth == 0 {
741                        targets.len()
742                    } else {
743                        targets
744                            .iter()
745                            .map(|target| {
746                                paths
747                                    .iter()
748                                    .find(|(known, _)| *known == target)
749                                    .map_or(0, |(_, count)| *count)
750                            })
751                            .sum()
752                    };
753                    (from, count)
754                })
755                .collect();
756        }
757        paths.iter().map(|(_, count)| count).sum()
758    }
759
760    /// Every way of voicing the whole line, a voicing for each bass note:
761    /// music21's `getAllPossibilityProgressions`, in its order.
762    pub fn all_possibility_progressions(&self) -> Vec<Vec<Possibility>> {
763        if self.segments.len() == 1 {
764            return self
765                .single
766                .iter()
767                .map(|voicing| vec![voicing.clone()])
768                .collect();
769        }
770        let Some(first) = self.movements.first() else {
771            return Vec::new();
772        };
773        let mut progressions: Vec<Vec<Possibility>> = first
774            .iter()
775            .flat_map(|(from, targets)| {
776                targets
777                    .iter()
778                    .map(move |target| vec![from.clone(), target.clone()])
779            })
780            .collect();
781        for moves in &self.movements[1..] {
782            progressions = progressions
783                .into_iter()
784                .flat_map(|progression| {
785                    let last = progression.last().cloned().unwrap_or_default();
786                    moves
787                        .iter()
788                        .find(|(from, _)| *from == last)
789                        .map(|(_, targets)| targets.clone())
790                        .unwrap_or_default()
791                        .into_iter()
792                        .map(move |target| {
793                            let mut longer = progression.clone();
794                            longer.push(target);
795                            longer
796                        })
797                })
798                .collect();
799        }
800        progressions
801    }
802
803    /// One way of voicing the whole line, each voicing chosen by `choose`,
804    /// which is given how many there are to choose from and answers the
805    /// index of one: music21's `getRandomPossibilityProgression`, with the
806    /// choosing left to the caller.
807    ///
808    /// # Errors
809    ///
810    /// A line with no way of voicing it, or a choice out of range.
811    pub fn possibility_progression_by(
812        &self,
813        mut choose: impl FnMut(usize) -> usize,
814    ) -> Result<Vec<Possibility>> {
815        let mut pick = |options: &[Possibility]| -> Result<Possibility> {
816            let index = choose(options.len());
817            options.get(index).cloned().ok_or_else(|| {
818                Error::FiguredBass(format!("no voicing {index} of {}", options.len()))
819            })
820        };
821        if self.segments.len() == 1 {
822            return Ok(vec![pick(&self.single)?]);
823        }
824        if self.num_solutions() == 0 {
825            return Err(Error::FiguredBass("Zero solutions".to_string()));
826        }
827        let firsts: Vec<Possibility> = self.movements[0]
828            .iter()
829            .map(|(from, _)| from.clone())
830            .collect();
831        let mut previous = pick(&firsts)?;
832        let mut progression = vec![previous.clone()];
833        for moves in &self.movements {
834            let targets = moves
835                .iter()
836                .find(|(from, _)| *from == previous)
837                .map(|(_, targets)| targets.as_slice())
838                .unwrap_or_default();
839            previous = pick(targets)?;
840            progression.push(previous.clone());
841        }
842        Ok(progression)
843    }
844
845    /// Whether realizations are written in keyboard style, the upper parts
846    /// as chords on one staff over the bass, as they are to begin with, or
847    /// in chorale style with a staff for each part: music21's
848    /// `keyboardStyleOutput`.
849    pub fn keyboard_style_output(&self) -> bool {
850        self.keyboard_style
851    }
852
853    /// Says whether realizations are written in keyboard style or in
854    /// chorale style.
855    pub fn set_keyboard_style_output(&mut self, keyboard_style: bool) {
856        self.keyboard_style = keyboard_style;
857    }
858
859    /// One way of voicing the line written out as a score, a voicing for
860    /// each segment: music21's `generateRealizationFromPossibilityProgression`.
861    ///
862    /// In keyboard style the score is two parts, the upper parts of each
863    /// voicing as a chord under a treble clef and the bass under a bass
864    /// clef; in chorale style each part has a staff of its own, under the
865    /// clef that fits it best. Every part opens with the key signature and
866    /// the meter of the line, is cut into measures, and has the notes that
867    /// run past a barline tied, its tuplets bracketed and its notes beamed.
868    ///
869    /// ```
870    /// use music21_rs::Pitch;
871    /// use music21_rs::figuredbass::realizer::FiguredBassLine;
872    /// use music21_rs::figuredbass::rules::Rules;
873    ///
874    /// let mut line = FiguredBassLine::default();
875    /// line.add_element(Pitch::from_name("C3")?, 2.0, None);
876    /// line.add_element(Pitch::from_name("G2")?, 2.0, Some("7"));
877    /// line.add_element(Pitch::from_name("C3")?, 4.0, None);
878    /// let realization = line.realize(&Rules::default(), 4, &Pitch::from_name("B5")?)?;
879    /// let voicings = realization.all_possibility_progressions();
880    /// let score = realization.generate_realization_from_possibility_progression(&voicings[0])?;
881    /// assert_eq!(score.parts().len(), 2);
882    /// assert_eq!(score.parts()[0].measures().len(), 2);
883    /// # Ok::<(), music21_rs::Error>(())
884    /// ```
885    ///
886    /// # Errors
887    ///
888    /// A progression with fewer voicings than the line has segments, or a
889    /// voicing that is no chord.
890    pub fn generate_realization_from_possibility_progression(
891        &self,
892        progression: &[Possibility],
893    ) -> Result<Stream> {
894        if progression.len() < self.segments.len() {
895            return Err(Error::FiguredBass(format!(
896                "a progression of {} voicings does not voice a line of {} segments",
897                progression.len(),
898                self.segments.len()
899            )));
900        }
901        let opening = |part: &mut Stream| {
902            part.push(self.key_signature.clone());
903            part.push(self.time.clone());
904        };
905        let mut score = Stream::with_kind(StreamKind::Score);
906
907        let mut bass = Stream::with_kind(StreamKind::Part);
908        opening(&mut bass);
909        for note in &self.bass_notes {
910            bass.push(note.clone());
911        }
912        bass.insert(0.0, Clef::bass());
913
914        if self.keyboard_style {
915            let mut right = Stream::with_kind(StreamKind::Part);
916            opening(&mut right);
917            for (segment, voicing) in self.segments.iter().zip(progression) {
918                let upper = &voicing[..voicing.len().saturating_sub(1)];
919                let chord =
920                    Chord::new(upper)?.with_duration(Duration::new(segment.quarter_length())?);
921                right.push(chord);
922            }
923            right.insert(0.0, Clef::treble());
924            score.insert(0.0, notated(&right)?);
925        } else {
926            let count = progression
927                .first()
928                .map_or(0, |voicing| voicing.len().saturating_sub(1));
929            for number in 0..count {
930                let mut part = Stream::with_kind(StreamKind::Part);
931                opening(&mut part);
932                for (segment, voicing) in self.segments.iter().zip(progression) {
933                    let Some(pitch) = voicing.get(number) else {
934                        return Err(Error::FiguredBass(format!(
935                            "a voicing of {} parts has no part {}",
936                            voicing.len(),
937                            number + 1
938                        )));
939                    };
940                    part.push(
941                        Note::from_pitch(pitch.clone())
942                            .with_duration(Duration::new(segment.quarter_length())?),
943                    );
944                }
945                let clef = Clef::best_for(&part.pitches(), true);
946                part.insert(0.0, clef);
947                score.insert(0.0, notated(&part)?);
948            }
949        }
950
951        score.insert(0.0, notated(&bass)?);
952        Ok(score)
953    }
954
955    /// Every way of voicing the line written out one after another in one
956    /// score: music21's `generateAllRealizations`. Each realization keeps
957    /// its own measure numbers and its closing barline.
958    ///
959    /// # Errors
960    ///
961    /// A line with no way of voicing it, or a realization that cannot be
962    /// written.
963    pub fn generate_all_realizations(&self) -> Result<Stream> {
964        let progressions = self.all_possibility_progressions();
965        if progressions.is_empty() {
966            return Err(Error::FiguredBass("Zero solutions".to_string()));
967        }
968        let mut written = progressions
969            .iter()
970            .map(|progression| self.generate_realization_from_possibility_progression(progression));
971        let first = written
972            .next()
973            .ok_or_else(|| Error::FiguredBass("Zero solutions".to_string()))??;
974        gathered(first, written)
975    }
976
977    /// One way of voicing the line written out as a score, each voicing
978    /// chosen by `choose`, which is given how many there are to choose from
979    /// and answers the index of one: music21's `generateRandomRealization`,
980    /// with the choosing left to the caller.
981    ///
982    /// # Errors
983    ///
984    /// A line with no way of voicing it, or a choice out of range.
985    pub fn generate_random_realization(
986        &self,
987        choose: impl FnMut(usize) -> usize,
988    ) -> Result<Stream> {
989        let progression = self.possibility_progression_by(choose)?;
990        self.generate_realization_from_possibility_progression(&progression)
991    }
992
993    /// So many ways of voicing the line, each chosen by `choose`, written
994    /// out one after another in one score: music21's
995    /// `generateRandomRealizations`. Asked for more than there are, it
996    /// answers every one of them in order, as [`generate_all_realizations`]
997    /// does. One way may be chosen more than once.
998    ///
999    /// [`generate_all_realizations`]: Realization::generate_all_realizations
1000    ///
1001    /// # Errors
1002    ///
1003    /// A line with no way of voicing it, or a choice out of range.
1004    pub fn generate_random_realizations(
1005        &self,
1006        amount: usize,
1007        mut choose: impl FnMut(usize) -> usize,
1008    ) -> Result<Stream> {
1009        if amount > self.num_solutions() {
1010            return self.generate_all_realizations();
1011        }
1012        let first = self.generate_random_realization(&mut choose)?;
1013        let mut rest = Vec::new();
1014        for _ in 1..amount {
1015            rest.push(self.generate_random_realization(&mut choose));
1016        }
1017        gathered(first, rest.into_iter())
1018    }
1019}
1020
1021/// Several realizations as one score: the parts of the first, each followed
1022/// by what the same part of every later realization holds. The parts are
1023/// put one after another as music21 puts them, each starting where the one
1024/// before it ended.
1025fn gathered(first: Stream, rest: impl Iterator<Item = Result<Stream>>) -> Result<Stream> {
1026    let mut parts: Vec<Stream> = first
1027        .events()
1028        .iter()
1029        .filter_map(|event| event.element().as_stream().cloned())
1030        .collect();
1031    // music21 appends each part to the score before anything is added to
1032    // it, so each starts where the one before ended as it then stood.
1033    let starts: Vec<FloatType> = parts
1034        .iter()
1035        .scan(0.0, |reached, part| {
1036            let start = *reached;
1037            *reached = op_frac(start + part.end_offset());
1038            Some(start)
1039        })
1040        .collect();
1041    for realization in rest {
1042        let realization = realization?;
1043        let held = realization
1044            .events()
1045            .iter()
1046            .filter_map(|event| event.element().as_stream());
1047        for (part, more) in parts.iter_mut().zip(held) {
1048            for event in more.events() {
1049                part.push(event.element().clone());
1050            }
1051        }
1052    }
1053    let mut score = Stream::with_kind(StreamKind::Score);
1054    for (start, part) in starts.into_iter().zip(parts) {
1055        score.insert(start, part);
1056    }
1057    Ok(score)
1058}
1059
1060/// Drops every move that leads to a voicing with nowhere to go, from the
1061/// end of the line back, as music21's `_trimAllMovements` does.
1062fn trim(movements: &mut [Movements]) {
1063    if movements.len() < 2 {
1064        return;
1065    }
1066    for index in (1..movements.len()).rev() {
1067        movements[index].retain(|(_, targets)| !targets.is_empty());
1068        let (before, after) = movements.split_at_mut(index);
1069        let reachable = &after[0];
1070        for (_, targets) in &mut before[index - 1] {
1071            targets.retain(|target| reachable.iter().any(|(from, _)| from == target));
1072        }
1073    }
1074    movements[0].retain(|(_, targets)| !targets.is_empty());
1075}
1076
1077#[cfg(test)]
1078mod tests {
1079    use super::*;
1080
1081    fn line(key: &str, notes: &[(&str, FloatType, Option<&str>)]) -> FiguredBassLine {
1082        let key = Key::from_tonic(key).unwrap();
1083        let mut line = FiguredBassLine::in_key(&key).unwrap();
1084        for (bass, length, notation) in notes {
1085            line.add_element(Pitch::from_name(*bass).unwrap(), *length, *notation);
1086        }
1087        line
1088    }
1089
1090    /// A line, the key it is in, how many ways it voices, and the first
1091    /// voicing of the first way and the last voicing of the last.
1092    type Case = (
1093        &'static str,
1094        &'static str,
1095        Vec<(&'static str, FloatType, Option<&'static str>)>,
1096        usize,
1097        [&'static str; 4],
1098        [&'static str; 4],
1099    );
1100
1101    fn names(voicing: &[Pitch]) -> Vec<String> {
1102        voicing.iter().map(Pitch::name_with_octave).collect()
1103    }
1104
1105    /// Every count and voicing here is music21's own.
1106    #[test]
1107    fn a_line_realizes_as_music21_s_does() {
1108        let top = Pitch::from_name("B5").unwrap();
1109        let cases: [Case; 6] = [
1110            (
1111                "intro",
1112                "C",
1113                vec![
1114                    ("C3", 1.0, None),
1115                    ("D3", 1.0, Some("4,3")),
1116                    ("C3", 2.0, None),
1117                ],
1118                30,
1119                ["G3", "G3", "E3", "C3"],
1120                ["G5", "E5", "C5", "C3"],
1121            ),
1122            (
1123                "minor",
1124                "a",
1125                vec![
1126                    ("A2", 1.0, None),
1127                    ("B2", 1.0, Some("6")),
1128                    ("C3", 1.0, Some("6")),
1129                    ("D3", 1.0, Some("6")),
1130                    ("E3", 1.0, None),
1131                    ("A2", 2.0, None),
1132                ],
1133                2829,
1134                ["A3", "E3", "C3", "A2"],
1135                ["A5", "E5", "C5", "A2"],
1136            ),
1137            (
1138                "dominant seventh",
1139                "C",
1140                vec![("G2", 1.0, Some("7")), ("C3", 1.0, None)],
1141                7,
1142                ["F3", "D3", "B2", "G2"],
1143                ["E5", "C5", "C5", "C3"],
1144            ),
1145            (
1146                "diminished seventh",
1147                "c",
1148                vec![("B2", 1.0, Some("-7")), ("C3", 1.0, None)],
1149                35,
1150                ["Abb3", "F3", "D3", "B2"],
1151                ["G5", "G5", "Eb5", "C3"],
1152            ),
1153            (
1154                "German sixth",
1155                "C",
1156                vec![("Ab2", 1.0, Some("#6,b5,3")), ("G2", 1.0, None)],
1157                7,
1158                ["F#3", "Eb3", "C3", "Ab2"],
1159                ["G5", "D5", "B4", "G2"],
1160            ),
1161            (
1162                "one chord",
1163                "C",
1164                vec![("C3", 1.0, None)],
1165                21,
1166                ["G3", "E3", "C3", "C3"],
1167                ["G5", "G5", "E5", "C3"],
1168            ),
1169        ];
1170        for (label, key, notes, count, first, last) in cases {
1171            let realization = line(key, &notes)
1172                .realize(&Rules::default(), 4, &top)
1173                .unwrap();
1174            assert_eq!(realization.num_solutions(), count, "{label}");
1175            let progressions = realization.all_possibility_progressions();
1176            assert_eq!(progressions.len(), count, "{label}");
1177            assert_eq!(names(&progressions[0][0]), first, "{label}: first");
1178            let final_voicing = progressions.last().unwrap().last().unwrap();
1179            assert_eq!(names(final_voicing), last, "{label}: last");
1180        }
1181
1182        let limited = Rules {
1183            part_movement_limits: vec![(1, 2), (2, 12), (3, 12)],
1184            ..Rules::default()
1185        };
1186        let realization = line(
1187            "C",
1188            &[
1189                ("C3", 1.0, None),
1190                ("D3", 1.0, Some("4,3")),
1191                ("C3", 2.0, None),
1192            ],
1193        )
1194        .realize(&limited, 4, &top)
1195        .unwrap();
1196        assert_eq!(realization.num_solutions(), 20);
1197
1198        let picked = line("C", &[("G2", 1.0, Some("7")), ("C3", 1.0, None)])
1199            .realize(&Rules::default(), 4, &top)
1200            .unwrap()
1201            .possibility_progression_by(|_| 0)
1202            .unwrap();
1203        assert_eq!(names(&picked[0]), ["F3", "D3", "B2", "G2"]);
1204        assert_eq!(names(&picked[1]), ["E3", "C3", "C3", "C3"]);
1205    }
1206
1207    /// Every count here is music21's own.
1208    #[test]
1209    fn a_line_is_realized_as_it_is_written() {
1210        let top = Pitch::from_name("B5").unwrap();
1211        let rules = Rules::default();
1212
1213        // A bass note cut by a barline is a segment for each piece.
1214        let key = Key::from_tonic("B").unwrap();
1215        let meter = TimeSignature::new(3, 4).unwrap();
1216        let mut tied = FiguredBassLine::in_key_and_time(&key, meter).unwrap();
1217        tied.add_element(Pitch::from_name("B2").unwrap(), 1.0, None);
1218        tied.add_element(Pitch::from_name("C#3").unwrap(), 1.0, Some("6"));
1219        tied.add_element(Pitch::from_name("D#3").unwrap(), 2.5, Some("6,#4"));
1220        tied.add_element(Pitch::from_name("E3").unwrap(), 0.5, None);
1221        let realization = tied.realize(&rules, 4, &top).unwrap();
1222        let lengths: Vec<FloatType> = realization
1223            .segments()
1224            .iter()
1225            .map(Segment::quarter_length)
1226            .collect();
1227        assert_eq!(lengths, [1.0, 1.0, 1.0, 1.5, 0.5]);
1228        assert_eq!(realization.num_solutions(), 4748);
1229
1230        // The figures are written under the bass, padded to one width, and
1231        // not again under the piece tied on.
1232        let written = tied.generate_bass_line().unwrap();
1233        let sung: Vec<Vec<String>> = written
1234            .notes()
1235            .iter()
1236            .map(|(_, element)| match element {
1237                StreamElement::Note(note) => {
1238                    note.lyrics().iter().map(|lyric| lyric.text()).collect()
1239                }
1240                _ => Vec::new(),
1241            })
1242            .collect();
1243        assert_eq!(
1244            sung,
1245            [
1246                vec![String::new()],
1247                vec!["6".to_string()],
1248                vec![" 6".to_string(), "#4".to_string()],
1249                Vec::new(),
1250                vec![String::new()]
1251            ]
1252        );
1253    }
1254
1255    /// Every count here is music21's own.
1256    #[test]
1257    fn a_part_laid_over_the_line_is_kept() {
1258        let top = Pitch::from_name("B5").unwrap();
1259        let mut held = line(
1260            "C",
1261            &[
1262                ("C3", 1.0, None),
1263                ("D3", 1.0, Some("6")),
1264                ("E3", 2.0, Some("6")),
1265            ],
1266        );
1267        let mut melody = Stream::with_kind(StreamKind::Part);
1268        for name in ["E5", "F5", "G5", "C5"] {
1269            melody.push(
1270                Note::from_name(name)
1271                    .unwrap()
1272                    .with_duration(Duration::quarter()),
1273            );
1274        }
1275        held.overlay_part(&melody);
1276        let segments = held.segments(&Rules::default(), 4, &top).unwrap();
1277        let lengths: Vec<FloatType> = segments.iter().map(Segment::quarter_length).collect();
1278        // The melody moves once while the bass holds.
1279        assert_eq!(lengths, [1.0, 1.0, 2.0, 0.0]);
1280        assert!(segments[0].rules.part_pitch_limits.is_empty());
1281        assert_eq!(segments[2].rules.parts_to_check, [2, 3, 4]);
1282        let realization = held.realize(&Rules::default(), 4, &top).unwrap();
1283        assert_eq!(realization.num_solutions(), 9);
1284        for progression in realization.all_possibility_progressions() {
1285            let melody: Vec<String> = progression[1..]
1286                .iter()
1287                .map(|voicing| voicing[0].name_with_octave())
1288                .collect();
1289            assert_eq!(melody, ["F5", "G5", "C5"]);
1290        }
1291    }
1292
1293    #[test]
1294    fn realizations_are_written_one_after_another() {
1295        let top = Pitch::from_name("B5").unwrap();
1296        let mut realization = line("C", &[("G2", 2.0, Some("7")), ("C3", 2.0, None)])
1297            .realize(&Rules::default(), 4, &top)
1298            .unwrap();
1299        let count = realization.num_solutions();
1300        let all = realization.generate_all_realizations().unwrap();
1301        assert_eq!(all.parts().len(), 2);
1302        assert_eq!(all.parts()[0].measures().len(), count);
1303        // Chosen by the caller, and as many as asked for.
1304        let chosen = realization.generate_random_realizations(3, |_| 0).unwrap();
1305        assert_eq!(chosen.parts()[1].measures().len(), 3);
1306        assert_eq!(
1307            realization
1308                .generate_random_realizations(count + 1, |_| 0)
1309                .unwrap()
1310                .parts()[0]
1311                .measures()
1312                .len(),
1313            count
1314        );
1315        // A staff for each part in chorale style.
1316        realization.set_keyboard_style_output(false);
1317        let chorale = realization.generate_random_realization(|_| 0).unwrap();
1318        assert_eq!(chorale.parts().len(), 4);
1319        // A line of chords has no bass line to write.
1320        let mut chords = FiguredBassLine::default();
1321        chords.add_chord(
1322            Pitch::from_name("C3").unwrap(),
1323            1.0,
1324            vec!["C".to_string(), "E".to_string(), "G".to_string()],
1325        );
1326        assert!(chords.generate_bass_line().is_err());
1327    }
1328}