1use std::collections::HashMap;
17
18use crate::defaults::FloatType;
19use crate::error::{Error, Result};
20use crate::meter::TimeSignature;
21use crate::search::difflib;
22use crate::stream::{Stream, StreamElement, StreamEvent, StreamKind};
23
24pub use crate::search::difflib::{Opcode, OpcodeTag};
25
26#[derive(Clone, Copy, Debug, PartialEq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub struct MeasureRelationship {
31 pub flagged_measure_part: usize,
33 pub flagged_measure_index: usize,
35 pub correct_measure_part: usize,
37 pub correct_measure_index: usize,
39 pub correction_probability: FloatType,
41}
42
43#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub struct PriorsIntegrationScore {
48 pub total: usize,
50 pub horizontal: usize,
52 pub vertical: usize,
54 pub ignored: usize,
56}
57
58pub const PROBABILITY_ON_EQUALITY: FloatType = 0.9675;
60pub const PROBABILITY_ON_OMISSION: FloatType = 0.009;
62pub const PROBABILITY_ON_ADDITION: FloatType = 0.004;
65
66pub fn measure_hash(measure: &Stream) -> Result<String> {
89 let flat = !measure
90 .events()
91 .iter()
92 .any(|event| matches!(event.element(), StreamElement::Stream(_)));
93 let chordified;
94 let source = if flat {
95 measure
96 } else {
97 chordified = measure.chordify()?;
98 &chordified
99 };
100 let mut hash = String::new();
101 for event in source.events() {
102 let element = event.element();
103 if !is_general_note(element) {
104 continue;
105 }
106 let length = element.quarter_length();
107 let code = if length == 0.0 {
108 Some(hash_quarter_length(0.015625))
109 } else {
110 match element {
111 StreamElement::Note(_)
112 | StreamElement::Chord(_)
113 | StreamElement::ChordSymbol(_) => {
114 let code = hash_quarter_length(length);
115 Some(if code.is_multiple_of(2) {
116 code
117 } else {
118 code + 1
119 })
120 }
121 StreamElement::Rest(_) => {
122 let code = hash_quarter_length(length);
123 Some(if code.is_multiple_of(2) {
124 code + 1
125 } else {
126 code
127 })
128 }
129 _ => None,
130 }
131 };
132 if let Some(code) = code {
133 hash.push(char::from(code));
134 }
135 }
136 Ok(hash)
137}
138
139fn is_general_note(element: &StreamElement) -> bool {
141 matches!(
142 element,
143 StreamElement::Note(_)
144 | StreamElement::Chord(_)
145 | StreamElement::Rest(_)
146 | StreamElement::Unpitched(_)
147 | StreamElement::PercussionChord(_)
148 | StreamElement::ChordSymbol(_)
149 )
150}
151
152fn hash_quarter_length(length: FloatType) -> u8 {
155 if length == 0.0 {
156 return 1;
157 }
158 let code = ((length * 256.0).log2() * 10.0) as i64;
159 code.clamp(1, 127) as u8
160}
161
162pub fn measure_difference(own: &str, other: &str) -> FloatType {
166 difference(&chars(own), &chars(other))
167}
168
169fn chars(hash: &str) -> Vec<char> {
170 hash.chars().collect()
171}
172
173fn difference(own: &[char], other: &[char]) -> FloatType {
174 let ratio = difflib::ratio(own, other);
175 1.0 - if ratio == 1.0 { 0.0 } else { ratio }
176}
177
178pub fn opcodes(own: &str, other: &str) -> Vec<Opcode> {
181 difflib::opcodes(&chars(own), &chars(other))
182}
183
184pub fn probability_based_on_changes(own: &str, other: &str) -> FloatType {
190 changes(&chars(own), &chars(other))
191}
192
193fn changes(own: &[char], other: &[char]) -> FloatType {
194 let mut probability = 0.0;
195 for (index, step) in difflib::opcodes(own, other).iter().enumerate() {
196 let one = match step.tag {
197 OpcodeTag::Equal => PROBABILITY_ON_EQUALITY.powf((step.j2 - step.j1) as FloatType),
198 OpcodeTag::Replace => substitute(&other[step.j1..step.j2], &own[step.i1..step.i2]),
199 OpcodeTag::Insert => PROBABILITY_ON_OMISSION.powf((step.j2 - step.j1) as FloatType),
200 OpcodeTag::Delete => PROBABILITY_ON_ADDITION.powf((step.i2 - step.i1) as FloatType),
201 };
202 if index == 0 {
203 probability = one;
204 } else {
205 probability *= one;
206 }
207 }
208 probability
209}
210
211pub fn probability_on_substitute(source: &str, destination: &str) -> FloatType {
216 substitute(&chars(source), &chars(destination))
217}
218
219fn substitute(source: &[char], destination: &[char]) -> FloatType {
220 let (mut source, mut destination) = (source, destination);
221 let mut probability = 1.0;
222 if source.len() > destination.len() {
223 let additions = source.len() - destination.len();
224 probability = PROBABILITY_ON_ADDITION.powf(additions as FloatType);
225 source = &source[..source.len() - additions];
226 } else if source.len() < destination.len() {
227 let omissions = destination.len() - source.len();
228 probability = PROBABILITY_ON_OMISSION.powf(omissions as FloatType);
229 destination = &destination[..destination.len() - omissions];
230 }
231 for (from, to) in source.iter().zip(destination) {
232 probability *= probability_from_one_char_sub(*from, *to);
233 }
234 probability
235}
236
237pub fn probability_from_one_char_sub(source: char, destination: char) -> FloatType {
243 let difference = source as i64 - destination as i64;
244 let distance = difference.abs() as FloatType;
245 if difference == 0 {
246 1.0
247 } else if distance % 10.0 == 0.0 {
248 0.0165_f64.powf(distance / 10.0)
249 } else if difference == 6 {
250 PROBABILITY_ON_ADDITION
251 } else if difference == -6 {
252 PROBABILITY_ON_OMISSION
253 } else if distance % 2.0 != 0.0 {
254 0.003
255 } else {
256 PROBABILITY_ON_OMISSION * PROBABILITY_ON_ADDITION
257 }
258}
259
260fn best(probabilities: &[FloatType]) -> (usize, FloatType) {
263 let most = probabilities
264 .iter()
265 .copied()
266 .fold(FloatType::NEG_INFINITY, FloatType::max);
267 let at = probabilities.iter().position(|&p| p == most).unwrap_or(0);
268 (at, most)
269}
270
271#[derive(Clone, Debug, PartialEq)]
274#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
275pub struct SinglePart {
276 part_number: usize,
277 hashed_notes: Vec<String>,
278 incorrect_measures: Vec<usize>,
279 probability_distribution: Option<Vec<FloatType>>,
280 index_array: Option<Vec<i64>>,
281}
282
283impl SinglePart {
284 pub fn part_number(&self) -> usize {
286 self.part_number
287 }
288
289 pub fn hashed_notes(&self) -> &[String] {
293 &self.hashed_notes
294 }
295
296 pub fn incorrect_measures(&self) -> &[usize] {
299 &self.incorrect_measures
300 }
301
302 pub fn horizontal_probability_dist(&mut self) -> &[FloatType] {
309 if self.probability_distribution.is_none() {
310 let count = self.hashed_notes.len();
311 let hashes: Vec<Vec<char>> = self.hashed_notes.iter().map(|hash| chars(hash)).collect();
312 let mut all = vec![0.0; count * 2];
313 let mut indices = vec![0_i64; count * 2];
314 for i in 0..count {
315 for k in 0..count {
316 let at = count + k - i;
317 indices[at] = k as i64 - i as i64;
318 if i == k {
319 all[at] = count as FloatType;
320 } else if difference(&hashes[i], &hashes[k]) == 1.0 {
321 all[at] += 1.0;
322 }
323 }
324 }
325 if !indices.is_empty() {
326 indices.remove(0);
327 all.remove(0);
328 }
329 self.index_array = Some(indices);
330 self.probability_distribution =
331 Some(all.iter().map(|value| value / count as FloatType).collect());
332 }
333 self.probability_distribution.as_deref().unwrap_or_default()
334 }
335
336 pub fn index_array(&self) -> Option<&[i64]> {
340 self.index_array.as_deref()
341 }
342
343 pub fn probability_distribution(&mut self, source: usize, destination: usize) -> FloatType {
346 let count = self.hashed_notes.len();
347 let index = source + count - 1 - destination;
348 self.horizontal_probability_dist()[index]
349 }
350
351 pub fn run_horizontal_search(&mut self, i: usize) -> MeasureRelationship {
356 self.horizontal_probability_dist();
357 let incorrect = self.incorrect_measures[i];
358 let own = chars(&self.hashed_notes[incorrect]);
359 let mut probabilities = Vec::with_capacity(self.hashed_notes.len());
360 for k in 0..self.hashed_notes.len() {
361 if self.incorrect_measures.contains(&k) {
362 probabilities.push(0.0);
363 } else {
364 let likely = changes(&own, &chars(&self.hashed_notes[k]));
365 probabilities.push(likely * self.probability_distribution(k, incorrect));
366 }
367 }
368 let (at, most) = best(&probabilities);
369 MeasureRelationship {
370 flagged_measure_part: self.part_number,
371 flagged_measure_index: incorrect,
372 correct_measure_part: self.part_number,
373 correct_measure_index: at,
374 correction_probability: most,
375 }
376 }
377
378 pub fn run_horizontal_correction_model(&mut self) -> Vec<MeasureRelationship> {
381 (0..self.incorrect_measures.len())
382 .map(|i| self.run_horizontal_search(i))
383 .collect()
384 }
385}
386
387#[derive(Clone, Debug)]
406pub struct ScoreCorrector {
407 score: Stream,
408 single_parts: Vec<SinglePart>,
409 part_events: Vec<usize>,
411 distribution: Option<Vec<Vec<FloatType>>>,
412 slices: HashMap<usize, Vec<String>>,
415}
416
417impl ScoreCorrector {
418 pub fn new(score: Stream) -> Result<Self> {
425 let part_events: Vec<usize> = score
426 .events()
427 .iter()
428 .enumerate()
429 .filter_map(|(index, event)| match event.element() {
430 StreamElement::Stream(inner)
431 if matches!(inner.kind(), StreamKind::Part | StreamKind::PartStaff) =>
432 {
433 Some(index)
434 }
435 _ => None,
436 })
437 .collect();
438 let mut corrector = Self {
439 score,
440 single_parts: Vec::new(),
441 part_events,
442 distribution: None,
443 slices: HashMap::new(),
444 };
445 for part_number in 0..corrector.part_events.len() {
446 let hashed_notes = corrector.hash_part(part_number)?;
447 corrector.single_parts.push(SinglePart {
448 part_number,
449 hashed_notes,
450 incorrect_measures: Vec::new(),
451 probability_distribution: None,
452 index_array: None,
453 });
454 corrector.find_incorrect_measures(part_number, true)?;
455 }
456 Ok(corrector)
457 }
458
459 pub fn score(&self) -> &Stream {
461 &self.score
462 }
463
464 pub fn into_score(self) -> Stream {
466 self.score
467 }
468
469 pub fn single_parts(&self) -> &[SinglePart] {
471 &self.single_parts
472 }
473
474 pub fn single_part_mut(&mut self, part: usize) -> &mut SinglePart {
476 &mut self.single_parts[part]
477 }
478
479 pub fn all_hashes(&self) -> Vec<&[String]> {
481 self.single_parts
482 .iter()
483 .map(SinglePart::hashed_notes)
484 .collect()
485 }
486
487 pub fn all_incorrect_measures(&self) -> Vec<&[usize]> {
489 self.single_parts
490 .iter()
491 .map(SinglePart::incorrect_measures)
492 .collect()
493 }
494
495 fn part(&self, part: usize) -> &Stream {
496 match self.score.events()[self.part_events[part]].element() {
497 StreamElement::Stream(inner) => inner,
498 _ => unreachable!("a part's event holds the part"),
499 }
500 }
501
502 fn measure_events(part: &Stream) -> Vec<usize> {
504 part.events()
505 .iter()
506 .enumerate()
507 .filter_map(|(index, event)| match event.element() {
508 StreamElement::Stream(inner) if inner.kind() == StreamKind::Measure => Some(index),
509 _ => None,
510 })
511 .collect()
512 }
513
514 fn hash_part(&self, part: usize) -> Result<Vec<String>> {
515 let part = self.part(part);
516 Self::measure_events(part)
517 .into_iter()
518 .map(|index| match part.events()[index].element() {
519 StreamElement::Stream(measure) => measure_hash(measure),
520 _ => unreachable!("a measure's event holds the measure"),
521 })
522 .collect()
523 }
524
525 pub fn find_incorrect_measures(&mut self, part: usize, run_fast: bool) -> Result<&[usize]> {
535 let stream = self.part(part);
536 let measures = Self::measure_events(stream);
537 let meter_of = |index: usize| -> Option<TimeSignature> {
538 let event = &stream.events()[index];
539 let StreamElement::Stream(measure) = event.element() else {
540 return None;
541 };
542 measure
543 .events()
544 .iter()
545 .take_while(|inner| inner.offset() == 0.0)
546 .find_map(|inner| match inner.element() {
547 StreamElement::TimeSignature(meter) => Some(meter.clone()),
548 _ => None,
549 })
550 .or_else(|| stream.time_signature_at(event.offset()))
551 };
552 let common_time = || TimeSignature::new(4, 4);
553 let mut meter = if run_fast {
554 match measures.first().and_then(|&index| meter_of(index)) {
555 Some(meter) => meter,
556 None => common_time()?,
557 }
558 } else {
559 common_time()?
560 };
561 let mut incorrect = Vec::new();
562 for (i, &index) in measures.iter().enumerate() {
563 if !run_fast {
564 meter = meter_of(index).ok_or_else(|| {
565 Error::Omr("'NoneType' object has no attribute 'barDuration'".to_string())
566 })?;
567 }
568 if stream.events()[index].element().quarter_length() != meter.bar_quarter_length() {
569 incorrect.push(i);
570 }
571 }
572 self.single_parts[part].incorrect_measures = incorrect;
573 Ok(&self.single_parts[part].incorrect_measures)
574 }
575
576 pub fn run_horizontal_correction_model(&mut self) -> Vec<Vec<MeasureRelationship>> {
579 self.single_parts
580 .iter_mut()
581 .map(SinglePart::run_horizontal_correction_model)
582 .collect()
583 }
584
585 pub fn vertical_probability_dist(&mut self) -> Result<&[Vec<FloatType>]> {
595 if self.distribution.is_none() {
596 let parts = self.single_parts.len();
597 let mut distribution = Vec::with_capacity(parts);
598 let hashes: Vec<Vec<Vec<char>>> = self
599 .single_parts
600 .iter()
601 .map(|part| part.hashed_notes.iter().map(|hash| chars(hash)).collect())
602 .collect();
603 for i in 0..parts {
604 let own = &hashes[i];
605 let mut sums = vec![0.0; parts];
606 for (k, hash) in own.iter().enumerate() {
607 for (other, sum) in sums.iter_mut().enumerate() {
608 if other == i {
609 *sum += 1.0;
610 continue;
611 }
612 let their = hashes[other]
613 .get(k)
614 .ok_or_else(|| Error::Omr("list index out of range".to_string()))?;
615 if difference(hash, their) == 1.0 {
616 *sum += 1.0;
617 }
618 }
619 }
620 if own.is_empty() {
621 return Err(Error::Omr("division by zero".to_string()));
622 }
623 distribution.push(
624 sums.iter()
625 .map(|sum| sum / own.len() as FloatType)
626 .collect(),
627 );
628 }
629 self.distribution = Some(distribution);
630 }
631 Ok(self.distribution.as_deref().unwrap_or_default())
632 }
633
634 pub fn run_vertical_search(
645 &mut self,
646 measure: usize,
647 part: usize,
648 ) -> Result<MeasureRelationship> {
649 self.vertical_probability_dist()?;
650 if !self.slices.contains_key(&measure) {
651 let mut hashes = Vec::with_capacity(self.single_parts.len());
652 for number in 0..self.single_parts.len() {
653 let stream = self.part(number);
654 let index = *Self::measure_events(stream)
655 .get(measure)
656 .ok_or_else(|| Error::Omr("list index out of range".to_string()))?;
657 let StreamElement::Stream(inner) = stream.events()[index].element() else {
658 unreachable!("a measure's event holds the measure");
659 };
660 hashes.push(measure_hash(inner)?);
661 }
662 self.slices.insert(measure, hashes);
663 }
664 let hashes = &self.slices[&measure];
665 let distribution = self.distribution.as_deref().unwrap_or_default();
666 let mut probabilities = Vec::with_capacity(hashes.len());
667 for (k, hash) in hashes.iter().enumerate() {
668 if k == part || self.single_parts[k].incorrect_measures.contains(&measure) {
669 probabilities.push(0.0);
670 } else {
671 let likely = probability_based_on_changes(&hashes[part], hash);
672 probabilities.push(likely * distribution[part][k]);
673 }
674 }
675 let (at, most) = best(&probabilities);
676 Ok(MeasureRelationship {
677 flagged_measure_part: part,
678 flagged_measure_index: measure,
679 correct_measure_part: at,
680 correct_measure_index: measure,
681 correction_probability: most,
682 })
683 }
684
685 pub fn run_vertical_correction_model(&mut self) -> Result<Vec<Vec<MeasureRelationship>>> {
692 self.vertical_probability_dist()?;
693 let mut all = Vec::with_capacity(self.single_parts.len());
694 for part in 0..self.single_parts.len() {
695 let flagged = self.single_parts[part].incorrect_measures.clone();
696 all.push(
697 flagged
698 .into_iter()
699 .map(|measure| self.run_vertical_search(measure, part))
700 .collect::<Result<Vec<_>>>()?,
701 );
702 }
703 Ok(all)
704 }
705
706 pub fn substitute_measure(
725 &mut self,
726 source_measure: usize,
727 source_part: usize,
728 destination_measure: usize,
729 destination_part: usize,
730 ) -> Result<()> {
731 let source = if (source_part, source_measure) == (destination_part, destination_measure) {
732 Stream::with_kind(StreamKind::Measure)
733 } else {
734 let stream = self.part(source_part);
735 let index = Self::measure_events(stream)[source_measure];
736 match stream.events()[index].element() {
737 StreamElement::Stream(inner) => (**inner).clone(),
738 _ => unreachable!("a measure's event holds the measure"),
739 }
740 };
741 let destination = self.part(destination_part);
742 let index = Self::measure_events(destination)[destination_measure];
743 let StreamElement::Stream(measure) = destination.events()[index].element() else {
744 unreachable!("a measure's event holds the measure");
745 };
746 let old_pitches: Vec<crate::pitch::Pitch> = measure
747 .events()
748 .iter()
749 .filter_map(|event| match event.element() {
750 StreamElement::Note(note) => Some(note.pitch().clone()),
751 _ => None,
752 })
753 .collect();
754 let mut elements: Vec<StreamEvent> = source.events().to_vec();
757 if let Some(barline) = source.left_barline() {
758 let at = elements
759 .iter()
760 .position(|event| event.offset() > 0.0 || event.element().class_sort_order() > -5)
761 .unwrap_or(elements.len());
762 elements.insert(
763 at,
764 StreamEvent::new(0.0, StreamElement::Barline(barline.clone())),
765 );
766 }
767 if let Some(barline) = source.right_barline() {
768 elements.push(StreamEvent::new(
769 0.0,
770 StreamElement::Barline(barline.clone()),
771 ));
772 }
773 let mut contents = Stream::with_kind(StreamKind::Measure);
774 let (mut left, mut pitch_index) = (None, 0);
775 for event in elements {
776 let mut element = event.element().clone();
777 if let StreamElement::Note(note) = &mut element
778 && let Some(old) = old_pitches.get(pitch_index)
779 {
780 let mut pitch = note.pitch().clone();
781 pitch.set_octave(old.octave());
782 pitch.take_name_of(old)?;
783 note.set_pitch(pitch);
784 pitch_index += 1;
785 }
786 let offset = contents.end_offset();
787 match element {
788 StreamElement::Barline(barline) if offset == 0.0 && left.is_none() => {
790 left = Some(barline);
791 }
792 element => contents.push(element),
793 }
794 }
795 let mut replaced = measure.with_events(contents.events().to_vec());
796 replaced.set_left_barline(left);
797 replaced.set_right_barline(None);
798 let path = [self.part_events[destination_part], index];
799 self.score.replace_nested(&path, replaced);
800 Ok(())
801 }
802
803 pub fn generate_corrected_score(
813 &mut self,
814 horizontal: &[Vec<MeasureRelationship>],
815 vertical: &[Vec<MeasureRelationship>],
816 ) -> Result<PriorsIntegrationScore> {
817 let mut score = PriorsIntegrationScore::default();
818 for part in 0..self.single_parts.len() {
819 let out_of_range = || Error::Omr("list index out of range".to_string());
820 let across = horizontal.get(part).ok_or_else(out_of_range)?;
821 for h in across {
822 for v in vertical.get(part).ok_or_else(out_of_range)? {
823 if h.flagged_measure_part != v.flagged_measure_part
824 || h.flagged_measure_index != v.flagged_measure_index
825 {
826 continue;
827 }
828 score.total += 1;
829 let source = if h.correction_probability > v.correction_probability {
830 score.horizontal += 1;
831 h
832 } else {
833 score.vertical += 1;
834 v
835 };
836 self.substitute_measure(
837 source.correct_measure_index,
838 source.correct_measure_part,
839 h.flagged_measure_index,
840 h.flagged_measure_part,
841 )?;
842 }
843 }
844 self.single_parts[part].hashed_notes = self.hash_part(part)?;
845 }
846 Ok(score)
847 }
848
849 pub fn run(&mut self) -> Result<PriorsIntegrationScore> {
856 let horizontal = self.run_horizontal_correction_model();
857 let vertical = self.run_vertical_correction_model()?;
858 self.generate_corrected_score(&horizontal, &vertical)
859 }
860}
861
862#[cfg(test)]
863mod tests {
864 use super::*;
865
866 #[test]
867 fn lengths_are_hashed_as_music21_hashes_them() {
868 assert_eq!(hash_quarter_length(1.0), 80);
869 assert_eq!(hash_quarter_length(0.015625), 20);
870 assert_eq!(hash_quarter_length(0.0), 1);
871 assert_eq!(hash_quarter_length(1000.0), 127);
872 }
873
874 #[test]
875 fn a_rhythm_misread_is_as_likely_as_music21_has_it() {
876 assert_eq!(probability_from_one_char_sub('P', 'P'), 1.0);
878 assert_eq!(probability_from_one_char_sub('Z', 'P'), 0.0165);
879 assert_eq!(
880 probability_from_one_char_sub('V', 'P'),
881 PROBABILITY_ON_ADDITION
882 );
883 assert_eq!(probability_from_one_char_sub('Q', 'P'), 0.003);
884 assert_eq!(measure_difference("PPPP", "PPPP"), 1.0);
885 assert_eq!(measure_difference("", ""), 1.0);
886 assert_eq!(measure_difference("PP", "ZZ"), 1.0);
887 }
888}