1pub mod traits;
12
13use crate::common::fraction::{Fraction, TupletInfo};
14use crate::errors::{CoreResult, ErrorSource};
15use std::cmp::Ordering;
16use std::collections::HashSet;
17use std::hash::{Hash, Hasher};
18use std::ops::{Add, Mul};
19use std::str::FromStr;
20pub use traits::{Interval, Invert, Transpose};
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29pub struct PitchClass(pub u8);
30
31impl PitchClass {
32 pub fn new(value: u8) -> Self {
34 Self(value % 12)
35 }
36
37 pub fn from_note_name(note: &str, accidentals: i32) -> CoreResult<Self> {
43 let base_pitch_class = match note.to_lowercase().as_str() {
44 "c" => 0,
45 "d" => 2,
46 "e" => 4,
47 "f" => 5,
48 "g" => 7,
49 "a" => 9,
50 "b" => 11,
51 _ => return Err(ErrorSource::Music(format!("Invalid note name: {note}"))),
52 };
53
54 let pitch_class = ((base_pitch_class + accidentals) % 12 + 12) % 12;
55 Ok(Self::new(pitch_class as u8))
56 }
57
58 pub fn to_note_name(&self) -> String {
62 let note_names = [
63 "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b",
64 ];
65 note_names[self.0 as usize].to_string()
66 }
67
68 pub fn interval_class_to(&self, other: &PitchClass) -> u8 {
73 let interval = self.interval_to(other);
74 std::cmp::min(interval, 12 - interval)
75 }
76}
77
78impl Transpose for PitchClass {
79 fn transpose(&self, semitones: i32) -> Self {
80 let new_value = ((self.0 as i32 + semitones) % 12 + 12) % 12;
81 Self(new_value as u8)
82 }
83}
84
85impl Invert<PitchClass> for PitchClass {
86 fn invert(&self, axis: &PitchClass) -> Self {
87 let inverted = ((axis.0 as i32 - self.0 as i32) % 12 + 12) % 12;
88 Self(inverted as u8)
89 }
90}
91
92impl Interval for PitchClass {
93 type Output = u8;
94 fn interval_to(&self, other: &PitchClass) -> Self::Output {
95 (((other.0 as i32 - self.0 as i32) % 12 + 12) % 12) as u8
96 }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
104pub struct Pitch {
105 pub pitch_class: PitchClass,
106 pub octave: i8,
107}
108
109impl Pitch {
110 pub fn from_midi(midi_note: u8) -> Self {
114 Self {
115 pitch_class: PitchClass::new(midi_note % 12),
116 octave: (midi_note / 12) as i8 - 1,
117 }
118 }
119
120 pub fn to_midi(&self) -> u8 {
124 ((self.octave + 1) as u8 * 12 + self.pitch_class.0).clamp(0, 127)
125 }
126
127 pub fn from_note_name(note: &str, accidentals: i32, octave: i8) -> CoreResult<Self> {
129 Ok(Self {
130 pitch_class: PitchClass::from_note_name(note, accidentals)?,
131 octave,
132 })
133 }
134
135 pub fn to_note_name(&self) -> String {
136 format!("{}{}", self.pitch_class.to_note_name(), self.octave)
137 }
138}
139
140impl Interval for Pitch {
141 type Output = i32;
142 fn interval_to(&self, other: &Pitch) -> Self::Output {
143 other.to_midi() as i32 - self.to_midi() as i32
144 }
145}
146
147impl Transpose for Pitch {
148 fn transpose(&self, semitones: i32) -> Self {
149 let new_midi = (self.to_midi() as i32 + semitones).clamp(0, 127) as u8;
150 Self::from_midi(new_midi)
151 }
152}
153
154impl Invert<PitchClass> for Pitch {
155 fn invert(&self, axis: &PitchClass) -> Self {
156 let inverted_pc = self.pitch_class.invert(axis);
157 Self {
158 pitch_class: inverted_pc,
159 octave: self.octave,
160 }
161 }
162}
163
164impl Invert<Pitch> for Pitch {
165 fn invert(&self, axis: &Pitch) -> Self {
166 let axis_midi = axis.to_midi() as i32;
167 let self_midi = self.to_midi() as i32;
168 let inverted_midi = (2 * axis_midi - self_midi).clamp(0, 127) as u8;
169 Self::from_midi(inverted_midi)
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)]
177pub struct PitchClassSet {
178 pub classes: HashSet<PitchClass>,
179}
180
181impl Hash for PitchClassSet {
182 fn hash<H: Hasher>(&self, state: &mut H) {
183 let mut classes: Vec<u8> = self.classes.iter().map(|pc| pc.0).collect();
185 classes.sort();
186 classes.hash(state);
187 }
188}
189
190impl Ord for PitchClassSet {
191 fn cmp(&self, other: &Self) -> Ordering {
192 let self_normal: Vec<u8> = self.classes.iter().map(|pc| pc.0).collect::<Vec<_>>();
194 let other_normal: Vec<u8> = other.classes.iter().map(|pc| pc.0).collect::<Vec<_>>();
195
196 let mut self_sorted = self_normal;
197 let mut other_sorted = other_normal;
198 self_sorted.sort();
199 other_sorted.sort();
200
201 self_sorted.cmp(&other_sorted)
202 }
203}
204
205impl PartialOrd for PitchClassSet {
206 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
207 Some(self.cmp(other))
208 }
209}
210
211impl PitchClassSet {
212 pub fn new(classes: Vec<PitchClass>) -> Self {
213 Self {
214 classes: classes.into_iter().collect(),
215 }
216 }
217
218 pub fn from_u8_values(values: Vec<u8>) -> Self {
219 let classes = values.into_iter().map(PitchClass::new).collect();
220 Self { classes }
221 }
222
223 pub fn from_pitches(pitches: &[Pitch]) -> Self {
224 let classes = pitches.iter().map(|p| p.pitch_class).collect();
225 Self { classes }
226 }
227
228 pub fn normal_form(&self) -> PitchClassSet {
234 if self.classes.is_empty() {
235 return PitchClassSet::new(Vec::new());
236 }
237
238 let mut classes: Vec<u8> = self.classes.iter().map(|pc| pc.0).collect();
239 classes.sort();
240
241 let mut best_rotation = Vec::new();
242 let mut best_span = 12; for i in 0..classes.len() {
245 let mut rotation = classes[i..].to_vec();
246 rotation.extend_from_slice(&classes[..i]);
247
248 let first = rotation[0];
250 let normalized: Vec<u8> = rotation
251 .iter()
252 .map(|&x| (((x as i32 - first as i32) % 12 + 12) % 12) as u8)
253 .collect();
254
255 let span = self.span(&normalized);
256 if span < best_span || (span == best_span && normalized < best_rotation) {
257 best_rotation = normalized;
258 best_span = span;
259 }
260 }
261
262 PitchClassSet::from_u8_values(best_rotation)
263 }
264
265 pub fn prime_form(&self) -> PitchClassSet {
271 if self.classes.is_empty() {
272 return PitchClassSet::new(Vec::new());
273 }
274
275 let normal = self.normal_form();
276 let inverted = self.invert(&PitchClass::new(0)).normal_form();
277
278 if inverted < normal {
279 inverted
280 } else {
281 normal
282 }
283 }
284
285 pub fn set_class(&self) -> Vec<PitchClassSet> {
290 let mut result = Vec::new();
291 let mut seen = std::collections::HashSet::new();
292
293 for t in 0..12 {
295 let transposed = self.transpose(t);
297 if seen.insert(transposed.clone()) {
298 result.push(transposed);
299 }
300
301 for axis in 0..12 {
303 let inverted = self.invert(&PitchClass::new(axis)).transpose(t);
304 if seen.insert(inverted.clone()) {
305 result.push(inverted);
306 }
307 }
308 }
309
310 result.sort();
312 result
313 }
314
315 fn span(&self, classes: &[u8]) -> u8 {
316 if classes.is_empty() {
317 return 0;
318 }
319 (((classes[classes.len() - 1] as i32 - classes[0] as i32) % 12 + 12) % 12) as u8
320 }
321
322 pub fn interval_class_vector(&self) -> [u8; 6] {
332 let mut icv = [0; 6];
333 let classes: Vec<PitchClass> = self.classes.iter().cloned().collect();
334
335 for i in 0..classes.len() {
336 for j in (i + 1)..classes.len() {
337 let pc1 = classes[i];
338 let pc2 = classes[j];
339 let ic = pc1.interval_class_to(&pc2);
340 if ic > 0 && ic <= 6 {
341 icv[ic as usize - 1] += 1;
342 }
343 }
344 }
345
346 icv
347 }
348}
349
350impl Transpose for PitchClassSet {
351 fn transpose(&self, semitones: i32) -> Self {
352 let classes = self
353 .classes
354 .iter()
355 .map(|pc| pc.transpose(semitones))
356 .collect();
357 Self { classes }
358 }
359}
360
361impl Invert<PitchClass> for PitchClassSet {
362 fn invert(&self, axis: &PitchClass) -> Self {
363 let classes = self.classes.iter().map(|pc| pc.invert(axis)).collect();
364 Self { classes }
365 }
366}
367
368#[derive(Debug, Clone, Copy, PartialEq)]
374pub struct Duration {
375 pub fraction: Fraction,
376}
377
378impl Duration {
379 pub fn from_fraction(numerator: u32, denominator: u32) -> Self {
380 Self {
381 fraction: Fraction::new(numerator, denominator),
382 }
383 }
384
385 pub fn whole() -> Self {
386 Self::from_fraction(1, 1)
387 }
388
389 pub fn half() -> Self {
390 Self::from_fraction(1, 2)
391 }
392
393 pub fn quarter() -> Self {
394 Self::from_fraction(1, 4)
395 }
396
397 pub fn eighth() -> Self {
398 Self::from_fraction(1, 8)
399 }
400
401 pub fn sixteenth() -> Self {
402 Self::from_fraction(1, 16)
403 }
404
405 pub fn with_dots(&self, dots: u32) -> Self {
410 Self {
411 fraction: self.fraction.with_dots(dots),
412 }
413 }
414
415 pub fn fraction(&self) -> &Fraction {
416 &self.fraction
417 }
418
419 pub fn to_tuplet_info(&self) -> Option<TupletInfo> {
421 self.fraction.to_tuplet_info()
422 }
423
424 pub fn is_tuplet(&self) -> bool {
426 !self.fraction.is_standard_binary()
427 }
428
429 pub fn to_fractional_string(&self) -> String {
430 format!("{}", self.fraction)
431 }
432
433 pub fn to_f64(&self) -> f64 {
434 self.fraction.to_f64()
435 }
436}
437
438impl<'b> Add<&'b Duration> for &Duration {
439 type Output = Duration;
440
441 fn add(self, other: &'b Duration) -> Self::Output {
442 Duration {
443 fraction: self.fraction + other.fraction,
444 }
445 }
446}
447
448impl Mul<u32> for &Duration {
449 type Output = Duration;
450
451 fn mul(self, rhs: u32) -> Self::Output {
452 Duration {
453 fraction: self.fraction * rhs,
454 }
455 }
456}
457
458impl Mul<Fraction> for &Duration {
459 type Output = Duration;
460
461 fn mul(self, rhs: Fraction) -> Self::Output {
462 Duration {
463 fraction: self.fraction * rhs,
464 }
465 }
466}
467
468#[derive(Debug, Clone, Copy, PartialEq)]
473pub enum Dynamic {
474 Pppp,
475 Ppp,
476 Pp,
477 P,
478 Mp,
479 Mf,
480 F,
481 Ff,
482 Fff,
483 Ffff,
484 Velocity(u8), }
486
487impl FromStr for Dynamic {
488 type Err = ErrorSource;
489
490 fn from_str(s: &str) -> Result<Self, Self::Err> {
491 match s {
492 "pppp" => Ok(Dynamic::Pppp),
493 "ppp" => Ok(Dynamic::Ppp),
494 "pp" => Ok(Dynamic::Pp),
495 "p" => Ok(Dynamic::P),
496 "mp" => Ok(Dynamic::Mp),
497 "mf" => Ok(Dynamic::Mf),
498 "f" => Ok(Dynamic::F),
499 "ff" => Ok(Dynamic::Ff),
500 "fff" => Ok(Dynamic::Fff),
501 "ffff" => Ok(Dynamic::Ffff),
502 _ => Err(ErrorSource::Argument(format!("Invalid dynamic: {s}"))),
503 }
504 }
505}
506
507impl Dynamic {
508 pub fn to_velocity(&self) -> u8 {
510 match self {
511 Dynamic::Pppp => 8,
512 Dynamic::Ppp => 16,
513 Dynamic::Pp => 32,
514 Dynamic::P => 48,
515 Dynamic::Mp => 64,
516 Dynamic::Mf => 80,
517 Dynamic::F => 96,
518 Dynamic::Ff => 112,
519 Dynamic::Fff => 120,
520 Dynamic::Ffff => 127,
521 Dynamic::Velocity(v) => *v,
522 }
523 }
524}
525
526#[derive(Debug, Clone, Copy, PartialEq)]
530pub enum Attribute {
531 Staccato,
532 Accent,
533 Tenuto,
534}
535
536impl FromStr for Attribute {
537 type Err = ErrorSource;
538
539 fn from_str(s: &str) -> Result<Self, Self::Err> {
540 match s {
541 "staccato" => Ok(Attribute::Staccato),
542 "accent" => Ok(Attribute::Accent),
543 "tenuto" => Ok(Attribute::Tenuto),
544 _ => Err(ErrorSource::Argument(format!("Invalid attribute: {s}"))),
545 }
546 }
547}
548
549#[derive(Debug, Clone, PartialEq)]
551pub struct Chord {
552 pub pitches: Vec<Pitch>,
553}
554
555impl Chord {
556 pub fn to_pitch_class_set(&self) -> PitchClassSet {
557 PitchClassSet::from_pitches(&self.pitches)
558 }
559}
560
561impl Transpose for Chord {
562 fn transpose(&self, semitones: i32) -> Self {
563 let pitches = self
564 .pitches
565 .iter()
566 .map(|p| p.transpose(semitones))
567 .collect();
568 Self { pitches }
569 }
570}
571
572impl Invert<PitchClass> for Chord {
573 fn invert(&self, axis: &PitchClass) -> Self {
574 let pitches = self.pitches.iter().map(|p| p.invert(axis)).collect();
575 Self { pitches }
576 }
577}
578
579impl Invert<Pitch> for Chord {
580 fn invert(&self, axis: &Pitch) -> Self {
581 let pitches = self.pitches.iter().map(|p| p.invert(axis)).collect();
582 Self { pitches }
583 }
584}
585
586#[derive(Debug, Clone, PartialEq)]
588pub enum EventContent {
589 Note(Pitch), Chord(Chord), Rest, Sequence(Vec<MusicalEvent>), }
594
595#[derive(Debug, Clone, PartialEq)]
601pub struct MusicalEvent {
602 pub duration: Duration, pub content: EventContent, pub dynamic: Option<Dynamic>, pub attributes: Vec<Attribute>, pub offset: Fraction, }
608
609impl MusicalEvent {
610 fn new(duration: Duration, content: EventContent) -> Self {
611 Self {
612 duration,
613 content,
614 dynamic: None,
615 attributes: Vec::new(),
616 offset: Fraction::new(0, 1),
617 }
618 }
619
620 pub fn note(pitch: Pitch, duration: Duration) -> Self {
621 Self::new(duration, EventContent::Note(pitch))
622 }
623
624 pub fn chord(chord: Chord, duration: Duration) -> Self {
625 Self::new(duration, EventContent::Chord(chord))
626 }
627
628 pub fn rest(duration: Duration) -> Self {
629 Self::new(duration, EventContent::Rest)
630 }
631
632 pub fn set_dynamic(&mut self, dynamic: Dynamic) {
633 self.dynamic = Some(dynamic);
634 }
635
636 pub fn with_dynamic(mut self, dynamic: Dynamic) -> Self {
637 self.set_dynamic(dynamic);
638 self
639 }
640
641 pub fn add_attribute(&mut self, attribute: Attribute) {
642 self.attributes.push(attribute);
643 }
644
645 pub fn with_attribute(mut self, attribute: Attribute) -> Self {
646 self.add_attribute(attribute);
647 self
648 }
649
650 pub fn set_attributes(&mut self, attributes: Vec<Attribute>) {
651 self.attributes = attributes;
652 }
653
654 pub fn with_attributes(mut self, attributes: Vec<Attribute>) -> Self {
655 self.set_attributes(attributes);
656 self
657 }
658
659 pub fn set_offset(&mut self, offset: Fraction) {
660 self.offset = offset;
661 }
662
663 pub fn with_offset(mut self, offset: Fraction) -> Self {
664 self.set_offset(offset);
665 self
666 }
667
668 pub fn atomic_event_count(&self) -> usize {
670 match &self.content {
671 EventContent::Sequence(events) => events.iter().map(|e| e.atomic_event_count()).sum(),
672 _ => 1,
673 }
674 }
675
676 pub fn total_duration(&self) -> Fraction {
678 match &self.content {
679 EventContent::Sequence(events) => {
680 if let Some(last_event) = events.last() {
681 last_event.offset + last_event.total_duration()
682 } else {
683 Fraction::new(0, 1)
684 }
685 }
686 _ => self.duration.fraction,
687 }
688 }
689}
690
691impl Transpose for MusicalEvent {
692 fn transpose(&self, semitones: i32) -> Self {
693 let content = match &self.content {
694 EventContent::Note(pitch) => EventContent::Note(pitch.transpose(semitones)),
695 EventContent::Chord(chord) => EventContent::Chord(chord.transpose(semitones)),
696 EventContent::Rest => EventContent::Rest,
697 EventContent::Sequence(events) => {
698 EventContent::Sequence(events.iter().map(|e| e.transpose(semitones)).collect())
699 }
700 };
701
702 Self {
703 content,
704 duration: self.duration,
705 dynamic: self.dynamic,
706 attributes: self.attributes.clone(),
707 offset: self.offset,
708 }
709 }
710}
711
712impl Invert<PitchClass> for MusicalEvent {
713 fn invert(&self, axis: &PitchClass) -> Self {
714 let content = match &self.content {
715 EventContent::Note(pitch) => EventContent::Note(pitch.invert(axis)),
716 EventContent::Chord(chord) => EventContent::Chord(chord.invert(axis)),
717 EventContent::Rest => EventContent::Rest,
718 EventContent::Sequence(events) => {
719 EventContent::Sequence(events.iter().map(|e| e.invert(axis)).collect())
720 }
721 };
722
723 Self {
724 content,
725 duration: self.duration,
726 dynamic: self.dynamic,
727 attributes: self.attributes.clone(),
728 offset: self.offset,
729 }
730 }
731}
732
733impl Invert<Pitch> for MusicalEvent {
734 fn invert(&self, axis: &Pitch) -> Self {
735 let content = match &self.content {
736 EventContent::Note(pitch) => EventContent::Note(pitch.invert(axis)),
737 EventContent::Chord(chord) => EventContent::Chord(chord.invert(axis)),
738 EventContent::Rest => EventContent::Rest,
739 EventContent::Sequence(events) => {
740 EventContent::Sequence(events.iter().map(|e| e.invert(axis)).collect())
741 }
742 };
743
744 Self {
745 content,
746 duration: self.duration,
747 dynamic: self.dynamic,
748 attributes: self.attributes.clone(),
749 offset: self.offset,
750 }
751 }
752}
753
754#[derive(Debug, Clone, PartialEq)]
756pub enum KeySignature {
757 Major(u8), Minor(u8), Custom(Vec<u8>), }
761
762impl std::fmt::Display for KeySignature {
763 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
764 match self {
765 KeySignature::Major(val) => {
766 let keys = [
767 "C", "G", "D", "A", "E", "B", "F#", "C#", "F", "Bb", "Eb", "Ab", "Db", "Gb",
768 "Cb",
769 ];
770 if (*val as usize) < keys.len() {
771 write!(f, "{} Major", keys[*val as usize])
772 } else {
773 write!(f, "Unknown Major Key({val})")
774 }
775 }
776 KeySignature::Minor(val) => {
777 let keys = [
778 "A", "E", "B", "F#", "C#", "G#", "D#", "A#", "D", "G", "C", "F", "Bb", "Eb",
779 "Ab",
780 ];
781 if (*val as usize) < keys.len() {
782 write!(f, "{} Minor", keys[*val as usize])
783 } else {
784 write!(f, "Unknown Minor Key({val})")
785 }
786 }
787 KeySignature::Custom(vals) => write!(f, "Custom Key Signature {vals:?}"),
788 }
789 }
790}
791
792#[derive(Debug, Clone, PartialEq)]
794pub enum MetadataKey {
795 Title,
796 Composer,
797 Copyright,
798 Tempo,
799 TimeSignature,
800 KeySignature,
801 Clef,
802 Instrument,
803 Channel,
804 Offset,
805}
806
807impl std::fmt::Display for MetadataKey {
808 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
809 match self {
810 MetadataKey::Title => write!(f, "title"),
811 MetadataKey::Composer => write!(f, "composer"),
812 MetadataKey::Copyright => write!(f, "copyright"),
813 MetadataKey::Tempo => write!(f, "tempo"),
814 MetadataKey::TimeSignature => write!(f, "time_signature"),
815 MetadataKey::KeySignature => write!(f, "key_signature"),
816 MetadataKey::Clef => write!(f, "clef"),
817 MetadataKey::Instrument => write!(f, "instrument"),
818 MetadataKey::Channel => write!(f, "channel"),
819 MetadataKey::Offset => write!(f, "offset"),
820 }
821 }
822}
823
824#[derive(Debug, Clone, PartialEq)]
826pub enum MetadataValue {
827 String(String),
828 Number(f64),
829 TimeSignature(u32, u32),
830 KeySignature(KeySignature),
831 Clef(Clef),
832}
833
834impl std::fmt::Display for MetadataValue {
835 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
836 match self {
837 MetadataValue::String(s) => write!(f, "\"{s}\""),
838 MetadataValue::Number(n) => write!(f, "{n}"),
839 MetadataValue::TimeSignature(num, den) => write!(f, "{num}/{den}"),
840 MetadataValue::KeySignature(ks) => write!(f, "{ks}"),
841 MetadataValue::Clef(c) => write!(f, "{c}"),
842 }
843 }
844}
845
846#[derive(Debug, Clone, PartialEq)]
848pub struct ContextChange {
849 pub key: MetadataKey, pub value: MetadataValue, pub time_offset: Fraction, }
853
854#[derive(Debug, Clone, Copy, PartialEq, Default)]
856pub enum Clef {
857 #[default]
858 Treble,
859 Bass,
860 Alto,
861 Tenor,
862}
863
864impl std::fmt::Display for Clef {
865 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
866 write!(f, "{self:?}")
867 }
868}
869
870impl FromStr for Clef {
871 type Err = ErrorSource;
872
873 fn from_str(s: &str) -> Result<Self, Self::Err> {
874 match s.to_lowercase().as_str() {
875 "treble" => Ok(Clef::Treble),
876 "bass" => Ok(Clef::Bass),
877 "alto" => Ok(Clef::Alto),
878 "tenor" => Ok(Clef::Tenor),
879 _ => Err(ErrorSource::Argument(format!("Invalid clef: {s}"))),
880 }
881 }
882}
883
884#[derive(Debug, Clone, PartialEq, Default)]
888pub struct Voice {
889 pub events: Vec<MusicalEvent>,
890}
891
892impl Voice {
893 pub fn new() -> Self {
894 Self::default()
895 }
896
897 pub fn add_event(&mut self, event: MusicalEvent) {
898 self.events.push(event);
899 }
900
901 pub fn with_event(mut self, event: MusicalEvent) -> Self {
902 self.add_event(event);
903 self
904 }
905
906 pub fn total_duration(&self) -> Fraction {
907 self.events
908 .iter()
909 .map(|e| e.offset + e.total_duration())
910 .max()
911 .unwrap_or_else(|| Fraction::new(0, 1))
912 }
913}
914
915#[derive(Debug, Clone, PartialEq)]
917pub struct Staff {
918 pub number: u32, pub voices: Vec<Voice>, pub context_changes: Vec<ContextChange>, }
922
923impl Staff {
924 pub fn new(number: u32) -> Self {
925 Self {
926 number,
927 voices: Vec::new(),
928 context_changes: Vec::new(),
929 }
930 }
931
932 pub fn add_voice(mut self, voice: Voice) -> Self {
933 self.voices.push(voice);
934 self
935 }
936
937 pub fn add_context_change(&mut self, change: ContextChange) {
938 self.context_changes.push(change);
939 }
940
941 pub fn with_context_change(mut self, change: ContextChange) -> Self {
942 self.add_context_change(change);
943 self
944 }
945
946 pub fn total_duration(&self) -> Fraction {
947 self.voices
948 .iter()
949 .map(|v| v.total_duration())
950 .max()
951 .unwrap_or_else(|| Fraction::new(0, 1))
952 }
953}
954
955#[derive(Debug, Clone, PartialEq)]
960pub struct Part {
961 pub name: Option<String>, pub instrument: Option<String>, pub channel: Option<u8>, pub offset: Fraction, pub events: Vec<MusicalEvent>, pub context_changes: Vec<ContextChange>, pub staves: Vec<Staff>, }
969
970impl Default for Part {
971 fn default() -> Self {
972 Self {
973 name: None,
974 instrument: None,
975 channel: None,
976 offset: Fraction::new(0, 1),
977 events: Vec::new(),
978 context_changes: Vec::new(),
979 staves: Vec::new(),
980 }
981 }
982}
983
984impl Part {
985 pub fn new(name: Option<String>) -> Self {
986 Self {
987 name,
988 instrument: None,
989 channel: None,
990 offset: Fraction::new(0, 1),
991 events: Vec::new(),
992 context_changes: Vec::new(),
993 staves: Vec::new(),
994 }
995 }
996
997 pub fn set_instrument(&mut self, instrument: String) {
998 self.instrument = Some(instrument);
999 }
1000
1001 pub fn with_instrument(mut self, instrument: String) -> Self {
1002 self.set_instrument(instrument);
1003 self
1004 }
1005
1006 pub fn set_channel(&mut self, channel: u8) {
1007 self.channel = Some(channel);
1008 }
1009
1010 pub fn with_channel(mut self, channel: u8) -> Self {
1011 self.set_channel(channel);
1012 self
1013 }
1014
1015 pub fn set_offset(&mut self, offset: Fraction) {
1016 self.offset = offset;
1017 }
1018
1019 pub fn with_offset(mut self, offset: Fraction) -> Self {
1020 self.set_offset(offset);
1021 self
1022 }
1023
1024 pub fn add_event(&mut self, event: MusicalEvent) {
1025 self.events.push(event);
1026 }
1027
1028 pub fn with_event(mut self, event: MusicalEvent) -> Self {
1029 self.add_event(event);
1030 self
1031 }
1032
1033 pub fn add_staff(&mut self, staff: Staff) {
1034 self.staves.push(staff);
1035 }
1036
1037 pub fn with_staff(mut self, staff: Staff) -> Self {
1038 self.add_staff(staff);
1039 self
1040 }
1041
1042 pub fn add_context_change(&mut self, change: ContextChange) {
1043 self.context_changes.push(change);
1044 }
1045
1046 pub fn with_context_change(mut self, change: ContextChange) -> Self {
1047 self.add_context_change(change);
1048 self
1049 }
1050
1051 pub fn all_events(&self) -> Vec<&MusicalEvent> {
1053 let mut events = Vec::new();
1054
1055 events.extend(self.events.iter());
1056
1057 for staff in &self.staves {
1058 for voice in &staff.voices {
1059 events.extend(voice.events.iter());
1060 }
1061 }
1062
1063 events
1064 }
1065
1066 pub fn total_event_count(&self) -> usize {
1068 let events_count: usize = self.events.iter().map(|e| e.atomic_event_count()).sum();
1069 let staves_count: usize = self
1070 .staves
1071 .iter()
1072 .map(|s| {
1073 s.voices
1074 .iter()
1075 .flat_map(|v| &v.events)
1076 .map(|e| e.atomic_event_count())
1077 .sum::<usize>()
1078 })
1079 .sum();
1080 events_count + staves_count
1081 }
1082
1083 pub fn total_duration(&self) -> Fraction {
1084 let main_duration = self
1085 .events
1086 .iter()
1087 .map(|e| e.offset + e.total_duration())
1088 .max()
1089 .unwrap_or_else(|| Fraction::new(0, 1));
1090
1091 let staff_duration = self
1092 .staves
1093 .iter()
1094 .map(|s| s.total_duration())
1095 .max()
1096 .unwrap_or_else(|| Fraction::new(0, 1));
1097
1098 self.offset + main_duration.max(staff_duration)
1099 }
1100
1101 pub fn merge(&self, other: &Part) -> Part {
1115 let mut new_part = self.clone();
1116 let base_offset = self.total_duration();
1117
1118 new_part.events.extend(other.events.iter().map(|e| {
1120 let mut new_event = e.clone();
1121 new_event.offset = e.offset + base_offset;
1122 new_event
1123 }));
1124
1125 new_part
1127 .context_changes
1128 .extend(other.context_changes.iter().map(|c| ContextChange {
1129 time_offset: c.time_offset + base_offset,
1130 ..c.clone()
1131 }));
1132
1133 for other_staff in &other.staves {
1135 if let Some(self_staff) = new_part
1136 .staves
1137 .iter_mut()
1138 .find(|s| s.number == other_staff.number)
1139 {
1140 let staff_offset = self_staff.total_duration();
1142 for (i, other_voice) in other_staff.voices.iter().enumerate() {
1143 let new_events: Vec<MusicalEvent> = other_voice
1144 .events
1145 .iter()
1146 .map(|e| {
1147 let mut new_event = e.clone();
1148 new_event.offset = e.offset + staff_offset;
1149 new_event
1150 })
1151 .collect();
1152
1153 if i < self_staff.voices.len() {
1154 self_staff.voices[i].events.extend(new_events);
1156 } else {
1157 let mut new_voice = Voice::new();
1159 new_voice.events = new_events;
1160 self_staff.voices.push(new_voice);
1161 }
1162 }
1163 } else {
1164 let mut new_staff = other_staff.clone();
1166 for voice in &mut new_staff.voices {
1167 for event in &mut voice.events {
1168 event.offset += base_offset;
1169 }
1170 }
1171 new_part.staves.push(new_staff);
1172 }
1173 }
1174
1175 new_part
1176 }
1177}
1178
1179#[derive(Debug, Clone, PartialEq, Default)]
1184pub struct Timeline {
1185 pub parts: Vec<Part>, pub context_changes: Vec<ContextChange>, }
1188
1189impl Timeline {
1190 pub fn add_part(&mut self, part: Part) {
1191 self.parts.push(part);
1192 }
1193
1194 pub fn with_part(mut self, part: Part) -> Self {
1195 self.parts.push(part);
1196 self
1197 }
1198
1199 pub fn add_parts(&mut self, parts: Vec<Part>) {
1200 self.parts.extend(parts);
1201 }
1202
1203 pub fn with_parts(mut self, parts: Vec<Part>) -> Self {
1204 self.parts.extend(parts);
1205 self
1206 }
1207
1208 pub fn add_context_change(&mut self, change: ContextChange) {
1209 self.context_changes.push(change);
1210 }
1211
1212 pub fn with_context_change(mut self, change: ContextChange) -> Self {
1213 self.add_context_change(change);
1214 self
1215 }
1216
1217 pub fn total_duration(&self) -> Fraction {
1218 self.parts
1219 .iter()
1220 .map(|p| p.total_duration())
1221 .max()
1222 .unwrap_or_else(|| Fraction::new(0, 1))
1223 }
1224}
1225
1226#[derive(Debug, Clone, PartialEq)]
1231pub struct Movement {
1232 pub name: Option<String>, pub timeline: Timeline, pub context_changes: Vec<ContextChange>, }
1236
1237impl Movement {
1238 pub fn new(name: Option<String>) -> Self {
1239 Self {
1240 name,
1241 timeline: Timeline::default(),
1242 context_changes: Vec::new(),
1243 }
1244 }
1245
1246 pub fn set_timeline(&mut self, timeline: Timeline) {
1247 self.timeline = timeline;
1248 }
1249
1250 pub fn with_timeline(mut self, timeline: Timeline) -> Self {
1251 self.set_timeline(timeline);
1252 self
1253 }
1254
1255 pub fn add_context_change(&mut self, change: ContextChange) {
1256 self.context_changes.push(change);
1257 }
1258
1259 pub fn with_context_change(mut self, change: ContextChange) -> Self {
1260 self.add_context_change(change);
1261 self
1262 }
1263
1264 pub fn total_duration(&self) -> Fraction {
1265 self.timeline.total_duration()
1266 }
1267}
1268
1269#[derive(Debug, Clone, Default, PartialEq)]
1275pub struct Score {
1276 pub name: Option<String>, pub movements: Vec<Movement>, pub timeline: Option<Timeline>, pub context_changes: Vec<ContextChange>, }
1281
1282impl Score {
1283 pub fn set_name(&mut self, name: String) {
1284 self.name = Some(name);
1285 }
1286 pub fn with_name(mut self, name: String) -> Self {
1287 self.set_name(name);
1288 self
1289 }
1290
1291 pub fn add_movement(&mut self, movement: Movement) {
1292 self.movements.push(movement);
1293 }
1294
1295 pub fn with_movement(mut self, movement: Movement) -> Self {
1296 self.add_movement(movement);
1297 self
1298 }
1299
1300 pub fn set_timeline(&mut self, timeline: Timeline) {
1301 self.timeline = Some(timeline);
1302 }
1303
1304 pub fn with_timeline(mut self, timeline: Timeline) -> Self {
1305 self.set_timeline(timeline);
1306 self
1307 }
1308
1309 pub fn add_context_change(&mut self, change: ContextChange) {
1310 self.context_changes.push(change);
1311 }
1312
1313 pub fn with_context_change(mut self, change: ContextChange) -> Self {
1314 self.add_context_change(change);
1315 self
1316 }
1317
1318 pub fn total_duration(&self) -> Fraction {
1319 if let Some(timeline) = &self.timeline {
1320 timeline.total_duration()
1321 } else {
1322 self.movements.iter().map(|m| m.total_duration()).sum()
1323 }
1324 }
1325
1326 pub fn is_multi_movement(&self) -> bool {
1328 !self.movements.is_empty()
1329 }
1330}
1331
1332pub fn pitch_interval(p1: &Pitch, p2: &Pitch, ordered: bool) -> i32 {
1337 let interval = p1.interval_to(p2);
1338 if ordered {
1339 interval
1340 } else {
1341 interval.abs()
1342 }
1343}
1344
1345pub fn pitch_class_interval(pc1: &PitchClass, pc2: &PitchClass, ordered: bool) -> u8 {
1350 if ordered {
1351 pc1.interval_to(pc2)
1352 } else {
1353 pc1.interval_class_to(pc2)
1354 }
1355}