1use 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#[derive(Clone, Debug, PartialEq)]
29enum Element {
30 Figured {
32 bass: Pitch,
33 quarter_length: FloatType,
34 notation: Option<String>,
35 },
36 Named {
39 bass: Pitch,
40 quarter_length: FloatType,
41 pitch_names: Vec<String>,
42 },
43}
44
45#[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#[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#[derive(Clone, Copy, Debug, PartialEq)]
77enum Voice {
78 Upper(usize, usize),
81 Bass(usize),
83}
84
85type Moment = ((FloatType, FloatType), Vec<Voice>);
88
89#[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 fn default() -> Self {
116 Self::new(FiguredBassScale::default())
117 }
118}
119
120fn common_time() -> TimeSignature {
122 TimeSignature::new(4, 4).expect("four quarters make a bar")
123}
124
125impl FiguredBassLine {
126 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 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 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 pub fn scale(&self) -> &FiguredBassScale {
162 &self.scale
163 }
164
165 pub fn time_signature(&self) -> &TimeSignature {
167 &self.time
168 }
169
170 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 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 pub fn len(&self) -> usize {
198 self.elements.len()
199 }
200
201 pub fn is_empty(&self) -> bool {
203 self.elements.is_empty()
204 }
205
206 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 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 fn pieces(&self) -> Result<Vec<Piece>> {
293 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 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 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 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 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 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 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 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 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 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
593fn 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
601fn 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
615fn 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
632fn notated(part: &Stream) -> Result<Stream> {
636 let mut made = part.clone();
637 make_part_notation_by(&mut made, false)?;
638 Ok(made)
639}
640
641type Movements = Vec<(Possibility, Vec<Possibility>)>;
644
645#[derive(Clone, Debug)]
647#[must_use]
648pub struct Realization {
649 segments: Vec<Segment>,
650 movements: Vec<Movements>,
652 single: Vec<Possibility>,
654 fallbacks: Vec<String>,
656 key_signature: KeySignature,
658 time: TimeSignature,
659 bass_notes: Vec<Note>,
661 keyboard_style: bool,
662}
663
664impl Realization {
665 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 pub fn segments(&self) -> &[Segment] {
720 &self.segments
721 }
722
723 pub fn fallbacks(&self) -> &[String] {
726 &self.fallbacks
727 }
728
729 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 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 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 pub fn keyboard_style_output(&self) -> bool {
850 self.keyboard_style
851 }
852
853 pub fn set_keyboard_style_output(&mut self, keyboard_style: bool) {
856 self.keyboard_style = keyboard_style;
857 }
858
859 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 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 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 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
1021fn 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 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
1060fn 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 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 #[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, ¬es)
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 #[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 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 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 #[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 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 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 realization.set_keyboard_style_output(false);
1317 let chorale = realization.generate_random_realization(|_| 0).unwrap();
1318 assert_eq!(chorale.parts().len(), 4);
1319 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}