1#[macro_use]
2extern crate serde_derive;
3
4mod audio;
5mod io;
6pub mod performance;
7pub mod theory;
8
9use music_timer::{music_time, time_signature};
10use performance::performance_engine;
11use std::{io::Write, path::Path};
12use theory::{chords, composition, notes};
13
14#[derive(Debug, PartialEq)]
16pub enum FailResult {
17 Deserialize,
18 ExportMIDI,
19 ExportTemplate,
20 NoPatterns,
21 NoFoundPattern(String),
22 EmptyPatterns,
23 TimeReverse(music_time::MusicTime, usize, String),
24 UnreachableTime(music_time::MusicTime, usize, String),
25 TimeSignature(time_signature::TimeSignature),
26 LoadSampler,
27}
28
29#[derive(Debug, PartialEq)]
31pub enum SuccessResult {
32 Export(Vec<String>),
33 ExportTemplate,
34 Playback,
35}
36
37pub fn get_chord_keywords() -> Vec<&'static str> {
42 theory::chords::chord_to_string_array()
43}
44
45pub fn export_to_midi_file(
52 composition: &composition::Composition,
53 export_path: &str,
54) -> Result<SuccessResult, FailResult> {
55 let parent_directory = Path::new(export_path)
56 .parent()
57 .unwrap_or(Path::new("./"))
58 .to_str()
59 .unwrap_or("./");
60
61 io::exporter::export_composition(&composition, parent_directory)
62}
63
64pub fn export_file_to_midi(composition_path: &str) -> Result<SuccessResult, FailResult> {
71 let composition_parameters = io::deseralizer::deserialize_file(composition_path)?;
72 let composition = parameters_to_composition(&composition_parameters)?;
73
74 export_to_midi_file(&composition, composition_path)
75}
76
77pub fn build_event(
88 bar: u16,
89 beat: u8,
90 beat_interval: u8,
91 chord_intervals: Vec<i8>,
92 transpose: i8,
93) -> composition::PatternEvent {
94 (
95 music_time::MusicTime::new(bar, beat, beat_interval),
96 chords::IntervalChord::new(chord_intervals, transpose)
97 .transpose_octave(3)
98 .to_midi(),
99 )
100}
101
102pub fn play_from_index<State: performance_engine::PerformanceState>(
116 composition: &composition::Composition,
117 performance_state: &mut State,
118 is_metronome_enabled: bool,
119 sample_paths_metronome: &Vec<String>,
120 sample_paths_piano: &Vec<String>,
121 playback_start: &music_timer::music_time::MusicTime,
122 pattern_start_index: usize,
123) -> Result<SuccessResult, FailResult> {
124 let mut performance_engine = performance_engine::PerformanceEngine::new(
125 &composition,
126 performance_state,
127 sample_paths_metronome,
128 sample_paths_piano,
129 )?;
130
131 performance_engine.set_metronome_enabled(is_metronome_enabled);
132 performance_engine.run_from(playback_start, pattern_start_index);
133 Ok(SuccessResult::Playback)
134}
135
136pub fn play_from<State: performance_engine::PerformanceState>(
260 composition: &composition::Composition,
261 performance_state: &mut State,
262 is_metronome_enabled: bool,
263 sample_paths_metronome: &Vec<String>,
264 sample_paths_piano: &Vec<String>,
265 playback_start: &music_timer::music_time::MusicTime,
266 pattern_start_name: &str,
267) -> Result<SuccessResult, FailResult> {
268 let patterns_playback_index = composition
269 .get_patterns()
270 .iter()
271 .position(|pattern| pattern.get_name() == pattern_start_name);
272
273 match patterns_playback_index {
274 Some(pattern_index) => play_from_index(
275 composition,
276 performance_state,
277 is_metronome_enabled,
278 sample_paths_metronome,
279 sample_paths_piano,
280 playback_start,
281 pattern_index,
282 ),
283 None => Err(FailResult::NoFoundPattern(pattern_start_name.to_owned())),
284 }
285}
286
287pub fn play<State: performance_engine::PerformanceState>(
298 composition: &composition::Composition,
299 performance_state: &mut State,
300 is_metronome_enabled: bool,
301 sample_paths_metronome: &Vec<String>,
302 sample_paths_piano: &Vec<String>,
303) -> Result<SuccessResult, FailResult> {
304 play_from_index(
305 composition,
306 performance_state,
307 is_metronome_enabled,
308 sample_paths_metronome,
309 sample_paths_piano,
310 &music_time::MusicTime::default(),
311 0,
312 )
313}
314
315pub fn play_file<State: performance_engine::PerformanceState>(
326 composition_path: &str,
327 performance_state: &mut State,
328 is_metronome_enabled: bool,
329 sample_paths_metronome: &Vec<String>,
330 sample_paths_piano: &Vec<String>,
331) -> Result<SuccessResult, FailResult> {
332 let composition_parameters = io::deseralizer::deserialize_file(composition_path)?;
333 let composition = parameters_to_composition(&composition_parameters)?;
334 play(
335 &composition,
336 performance_state,
337 is_metronome_enabled,
338 sample_paths_metronome,
339 sample_paths_piano,
340 )
341}
342
343pub fn play_yaml<State: performance_engine::PerformanceState>(
354 composition_yaml: &str,
355 performance_state: &mut State,
356 is_metronome_enabled: bool,
357 sample_paths_metronome: &Vec<String>,
358 sample_paths_piano: &Vec<String>,
359) -> Result<SuccessResult, FailResult> {
360 let composition_parameters = io::deseralizer::deserialize_string(composition_yaml)?;
361 let composition = parameters_to_composition(&composition_parameters)?;
362 play(
363 &composition,
364 performance_state,
365 is_metronome_enabled,
366 sample_paths_metronome,
367 sample_paths_piano,
368 )
369}
370
371pub fn play_file_from<State: performance_engine::PerformanceState>(
385 composition_path: &str,
386 performance_state: &mut State,
387 is_metronome_enabled: bool,
388 sample_paths_metronome: &Vec<String>,
389 sample_paths_piano: &Vec<String>,
390 playback_start: &music_timer::music_time::MusicTime,
391 pattern_start_name: &str,
392) -> Result<SuccessResult, FailResult> {
393 let composition_parameters = io::deseralizer::deserialize_file(composition_path)?;
394 let composition = parameters_to_composition(&composition_parameters)?;
395 play_from(
396 &composition,
397 performance_state,
398 is_metronome_enabled,
399 sample_paths_metronome,
400 sample_paths_piano,
401 playback_start,
402 pattern_start_name,
403 )
404}
405
406pub fn play_yaml_from<State: performance_engine::PerformanceState>(
420 composition_yaml: &str,
421 performance_state: &mut State,
422 is_metronome_enabled: bool,
423 sample_paths_metronome: &Vec<String>,
424 sample_paths_piano: &Vec<String>,
425 playback_start: &music_timer::music_time::MusicTime,
426 pattern_start_name: &str,
427) -> Result<SuccessResult, FailResult> {
428 let composition_parameters = io::deseralizer::deserialize_string(composition_yaml)?;
429 let composition = parameters_to_composition(&composition_parameters)?;
430 play_from(
431 &composition,
432 performance_state,
433 is_metronome_enabled,
434 sample_paths_metronome,
435 sample_paths_piano,
436 playback_start,
437 pattern_start_name,
438 )
439}
440
441pub fn export_template(path: &str) -> Result<SuccessResult, FailResult> {
446 let mut file = match std::fs::File::create(path) {
447 Ok(file) => Ok(file),
448 _ => Err(FailResult::ExportTemplate),
449 }?;
450
451 let write_out = file.write_all(
452 br#"
453# Name of the composition
454name: default_composition
455
456# The default master parameters of the composition.
457# New master pattern can be assigned to a pattern that overrides
458# the default master values.
459master:
460 # The musical key to transpose the chords.
461 # Supported values: C, C#, D, D#, E, F, F#, G, G#, A, A#, B
462 key: F#
463
464 # The beats per minute of the composition.
465 time: 120
466
467 # The time signature of the composition.
468 # Beat numerator supported values: must be > 0.
469 # Beat denominator supported values: 2, 4, 8, 16, 32, 64
470 # e.g 3/8 is supported, 0/7 is not supported.
471 signature: [4, 4]
472
473# Composition defined chords.
474chords:
475 # [chord_name, [chord intervals]].
476 - [custom1, [0, 3, 8]]
477 - [custom2, [0, 5]]
478
479# The composition's chord patterns/progressions.
480patterns:
481 - name: part_a
482 # Each pattern event = [bar, beat, beat interval, chord name, chord transpose].
483 pattern:
484 - [1, 1, 1, MAJOR_SEVENTH, 0]
485 - [1, 3, 1, custom1, 0]
486 - [2, 1, 1, MAJOR_NINTH, 0]
487 - [2, 3, 1, custom1, 0]
488 - [3, 1, 1, MAJOR_SEVENTH, 3]
489 - [3, 2, 1, custom1, 0]
490 - [4, 1, 1, MAJOR_NINTH, -3]
491 - [4, 2, 1, ?, 0] # ? = Select a random user defined chord.
492
493 - name: part_b
494 master:
495 signature: [3, 4]
496 key: C#
497 time: 69
498 # Each pattern event = [bar, beat, beat interval, chord name, chord transpose].
499 pattern:
500 - [1, 1, 1, MAJOR_SEVENTH, 0]
501 - [1, 2, 1, custom1, 0]
502 - [2, 1, 5, MAJOR_NINTH, 0]
503 - [2, 2, 1, custom1, 0]
504 - [3, 1, 5, MAJOR_SEVENTH, 3]
505 - [3, 2, 1, custom1, 0]
506 - [4, 1, 1, MAJOR_NINTH, -3]
507 - [4, 2, 1, ??, 0] #?? = Select a random chord from user defined and internal defined chord.
508 "#,
509 );
510
511 match write_out {
512 Ok(()) => Ok(SuccessResult::ExportTemplate),
513 _ => Err(FailResult::ExportTemplate),
514 }
515}
516
517fn parameters_to_composition(
522 params: &io::deseralizer::CompositionParameters,
523) -> Result<composition::Composition, crate::FailResult> {
524 let default_master: io::deseralizer::MasterParameters = match params.get_master() {
525 Some(master) => master.clone(),
526 None => io::deseralizer::MasterParameters::default(),
527 };
528
529 let mut composition_result = Ok(composition::Composition::new(¶ms.get_name()));
530 let mut count = 0;
531
532 match params.get_patterns() {
533 None => Err(crate::FailResult::NoPatterns),
534 Some(patterns) => {
535 if patterns.is_empty() {
536 Err(crate::FailResult::EmptyPatterns)
537 } else {
538 for pattern in patterns {
539 if composition_result.is_err() {
540 break;
541 }
542
543 match pattern.get_pattern() {
544 Some(pattern_pattern) => {
545 let pattern_master = match pattern.get_master() {
546 Some(pattern_master) => {
547 io::deseralizer::MasterParameters::from_overrides(&default_master, pattern_master)
548 }
549 _ => default_master.clone(),
550 };
551
552 let name = match pattern.get_name() {
553 Some(name) => name.to_string(),
554 _ => format!("unnamed_pattern_{}", count),
555 };
556 let composition_pattern = {
557 let mut pattern = {
558 let bpm = pattern_master.get_time_or_default();
559 let (numerator, denominator) = pattern_master.get_signature_or_default();
560 let time_signature = time_signature::TimeSignature::new(numerator, denominator);
561
562 if !time_signature.is_valid() {
563 composition_result = Err(FailResult::TimeSignature(time_signature));
564 break;
565 }
566
567 composition::Pattern::new(&name, bpm, time_signature)
568 };
569
570 let additional_chords = match params.get_custom_chords() {
571 Some(custom_chords) => custom_chords.clone(),
572 None => Vec::new(),
573 };
574
575 for (bar, beat, beat_interval, chord_string, transpose) in pattern_pattern {
576 let chord_notes = {
577 let mut chord_intervals = chords::IntervalChord::from_string_with_custom(
578 chord_string,
579 &additional_chords,
580 );
581
582 let key_offset = {
583 let key_string = pattern_master.get_key_or_default();
584 let key = notes::string_to_key(&key_string);
585 notes::key_to_index(key) as i8
586 };
587 chord_intervals
588 .transpose(key_offset)
589 .transpose(*transpose)
590 .transpose_octave(3)
591 .to_midi()
592 };
593
594 let time = music_time::MusicTime::new(*bar, *beat, *beat_interval);
595
596 const INTERVAL_RESOLUTION: u8 = 16;
597 let unreachable_beat_interval = time.get_beat_interval()
598 > INTERVAL_RESOLUTION / 2
599 || time.get_beat_interval() == 0;
600 let unreachable_beat = time.get_beat()
601 > pattern.get_time_signature().get_numerator()
602 || time.get_beat() == 0;
603 let unreachable_bar = time.get_bar() == 0;
604
605 if unreachable_bar || unreachable_beat_interval || unreachable_beat {
606 composition_result = Err(FailResult::UnreachableTime(
607 time,
608 pattern.len(),
609 chord_string.to_string(),
610 ));
611 } else if pattern.len() != 0 {
612 let previous_time = pattern.get(pattern.len() - 1).0;
613 let time_does_not_advance = time == previous_time;
614
615 if time_does_not_advance {
616 composition_result = Err(FailResult::UnreachableTime(
617 time,
618 pattern.len(),
619 chord_string.to_string(),
620 ));
621 } else {
622 let reverse_time_flow = time < previous_time;
623 if reverse_time_flow {
624 composition_result = Err(FailResult::TimeReverse(
625 time,
626 pattern.len(),
627 chord_string.to_string(),
628 ));
629 }
630 }
631 }
632
633 pattern.push_event(time, chord_notes);
634 }
635 pattern
636 };
637
638 match &mut composition_result {
639 Ok(composition) => composition.push_pattern(composition_pattern),
640 _ => break,
641 }
642
643 count += 1;
644 }
645 None => composition_result = Err(FailResult::NoPatterns),
646 }
647 }
648
649 composition_result
650 }
651 }
652 }
653}
654
655#[test]
656fn test_new_composition() {
657 let params = io::deseralizer::deserialize_string(
658 r#"
659 name: bc_000_a
660
661 # Can be overridden by patterns
662 master:
663 key: D
664 time: 128
665 signature: [3, 4]
666
667 chords:
668 - [custom1, [0, 3, 8]]
669
670 patterns:
671 - name: part_a
672 pattern:
673 - [1,1,1, MAJOR_SEVENTH, 0]
674 - [1,3,1, custom1, 0]
675 - [2,1,1, MAJOR_NINTH, 0]
676 - [2,3,1, custom1, 0]
677 - [3,1,1, MAJOR_SEVENTH, 3]
678 - [3,2,1, custom1, 0]
679 - [4,1,1, MAJOR_NINTH, -3]
680 - [4,2,1, custom1, -3]
681
682 - name: part_b
683 master:
684 signature: [4, 8]
685 key: C#
686 time: 69
687 pattern:
688 - [1,1,1, MAJOR_SEVENTH, 0]
689 - [1,2,1, custom1, 0]
690 - [2,1,1, MAJOR_NINTH, 0]
691 - [2,2,1, custom1, 0]
692 - [3,1,1, MAJOR_SEVENTH, 3]
693 - [3,2,1, custom1, 0]
694 - [4,1,1, MAJOR_NINTH, -3]
695 - [4,2,1, custom1, 0]
696
697 "#,
698 );
699
700 assert_ne!(params, Err(crate::FailResult::Deserialize));
701
702 let compo = parameters_to_composition(¶ms.unwrap()).unwrap();
703
704 assert_eq!(compo.len(), 2);
705 assert_eq!(compo.get(0).len(), 8);
706 assert_eq!(compo.get(1).len(), 8);
707
708 assert_eq!(compo.get(0).get_bpm(), 128);
709 assert_eq!(
710 compo.get(0).get_time_signature(),
711 time_signature::TimeSignature::new(3, 4)
712 );
713
714 assert_eq!(compo.get(1).get_bpm(), 69);
715 assert_eq!(
716 compo.get(1).get_time_signature(),
717 time_signature::TimeSignature::new(4, 8)
718 );
719
720 let (time, notes) = compo.get(0).get(0);
721 assert_eq!(time, &music_time::MusicTime::new(1, 1, 1));
722 assert_eq!(notes, &vec![62, 66, 69, 73]);
723
724 let (time, notes) = compo.get(0).get(1);
725 assert_eq!(time, &music_time::MusicTime::new(1, 3, 1));
726 assert_eq!(notes, &vec![62, 65, 70]);
727
728 let (time, notes) = compo.get(0).get(2);
729 assert_eq!(time, &music_time::MusicTime::new(2, 1, 1));
730 assert_eq!(notes, &vec![62, 66, 69, 73, 64]);
731
732 let (time, notes) = compo.get(0).get(7);
733 assert_eq!(time, &music_time::MusicTime::new(4, 2, 1));
734 assert_eq!(notes, &vec![59, 62, 67]);
735
736 let (time, notes) = compo.get(1).get(0);
737 assert_eq!(time, &music_time::MusicTime::new(1, 1, 1));
738 assert_eq!(notes, &vec![61, 65, 68, 72]);
739
740 let (time, notes) = compo.get(1).get(1);
741 assert_eq!(time, &music_time::MusicTime::new(1, 2, 1));
742 assert_eq!(notes, &vec![61, 64, 69]);
743
744 let (time, notes) = compo.get(1).get(2);
745 assert_eq!(time, &music_time::MusicTime::new(2, 1, 1));
746 assert_eq!(notes, &vec![61, 65, 68, 72, 63]);
747
748 let (time, notes) = compo.get(1).get(7);
749 assert_eq!(time, &music_time::MusicTime::new(4, 2, 1));
750 assert_eq!(notes, &vec![61, 64, 69]);
751}
752
753#[test]
754fn test_flow_reverse() {
755 let params = io::deseralizer::deserialize_string(
756 r#"
757 patterns:
758 - name: part_a
759 pattern:
760 - [1,3,1, MAJOR_SEVENTH, 0]
761 - [1,2,1, custom1, 0]
762 "#,
763 );
764 assert_ne!(params, Err(crate::FailResult::Deserialize));
765
766 let compo = parameters_to_composition(¶ms.unwrap());
767
768 match compo {
769 Err(FailResult::TimeReverse(music_time, index, chord)) => {
770 assert_eq!(music_time, music_time::MusicTime::new(1, 2, 1));
771 assert_eq!(index, 1);
772 assert_eq!(chord, "custom1".to_string());
773 }
774 _ => assert_eq!(false, true),
775 }
776}
777
778#[test]
779fn test_unreachable_time() {
780 {
781 let params = io::deseralizer::deserialize_string(
782 r#"
783 patterns:
784 - name: part_a
785 pattern:
786 - [1,3,1, MAJOR_SEVENTH, 0]
787 - [1,2,9, custom1, 0]
788 "#,
789 );
790 assert_ne!(params, Err(crate::FailResult::Deserialize));
791
792 let compo = parameters_to_composition(¶ms.unwrap());
793
794 match compo {
795 Err(FailResult::UnreachableTime(music_time, index, chord)) => {
796 assert_eq!(music_time, music_time::MusicTime::new(1, 2, 9));
797 assert_eq!(index, 1);
798 assert_eq!(chord, "custom1".to_string());
799 }
800 _ => assert_eq!(false, true),
801 }
802 }
803 {
804 let params = io::deseralizer::deserialize_string(
805 r#"
806 patterns:
807 - name: part_a
808 pattern:
809 - [1,3,1, MAJOR_SEVENTH, 0]
810 - [1,9,1, custom1, 0]
811 "#,
812 );
813 assert_ne!(params, Err(crate::FailResult::Deserialize));
814
815 let compo = parameters_to_composition(¶ms.unwrap());
816
817 match compo {
818 Err(FailResult::UnreachableTime(music_time, index, chord)) => {
819 assert_eq!(music_time, music_time::MusicTime::new(1, 9, 1));
820 assert_eq!(index, 1);
821 assert_eq!(chord, "custom1".to_string());
822 }
823 _ => assert_eq!(false, true),
824 }
825 }
826 {
827 let params = io::deseralizer::deserialize_string(
828 r#"
829 patterns:
830 - name: part_a
831 pattern:
832 - [1,3,1, MAJOR_SEVENTH, 0]
833 - [1,9,0, custom1, 0]
834 "#,
835 );
836 assert_ne!(params, Err(crate::FailResult::Deserialize));
837
838 let compo = parameters_to_composition(¶ms.unwrap());
839
840 match compo {
841 Err(FailResult::UnreachableTime(music_time, index, chord)) => {
842 assert_eq!(music_time, music_time::MusicTime::new(1, 9, 0));
843 assert_eq!(index, 1);
844 assert_eq!(chord, "custom1".to_string());
845 }
846 _ => assert_eq!(false, true),
847 }
848 }
849 {
850 let params = io::deseralizer::deserialize_string(
851 r#"
852 patterns:
853 - name: part_a
854 pattern:
855 - [1,0,1, custom1, 0]
856 "#,
857 );
858 assert_ne!(params, Err(crate::FailResult::Deserialize));
859
860 let compo = parameters_to_composition(¶ms.unwrap());
861
862 match compo {
863 Err(FailResult::UnreachableTime(music_time, index, chord)) => {
864 assert_eq!(music_time, music_time::MusicTime::new(1, 0, 1));
865 assert_eq!(index, 0);
866 assert_eq!(chord, "custom1".to_string());
867 }
868 _ => assert_eq!(false, true),
869 }
870 }
871 {
872 let params = io::deseralizer::deserialize_string(
873 r#"
874 patterns:
875 - name: part_a
876 pattern:
877 - [0,1,1, custom1, 0]
878 "#,
879 );
880 assert_ne!(params, Err(crate::FailResult::Deserialize));
881
882 let compo = parameters_to_composition(¶ms.unwrap());
883
884 match compo {
885 Err(FailResult::UnreachableTime(music_time, index, chord)) => {
886 assert_eq!(music_time, music_time::MusicTime::new(0, 1, 1));
887 assert_eq!(index, 0);
888 assert_eq!(chord, "custom1".to_string());
889 }
890 _ => assert!(false, true),
891 }
892 }
893 {
894 let params = io::deseralizer::deserialize_string(
895 r#"
896 patterns:
897 - name: part_a
898 pattern:
899 - [1,1,1, MINOR, 0]
900 - [1,1,1, custom1, 0]
901 "#,
902 );
903 assert_ne!(params, Err(crate::FailResult::Deserialize));
904
905 let compo = parameters_to_composition(¶ms.unwrap());
906
907 match compo {
908 Err(FailResult::UnreachableTime(music_time, index, chord)) => {
909 assert_eq!(music_time, music_time::MusicTime::new(1, 1, 1));
910 assert_eq!(index, 1);
911 assert_eq!(chord, "custom1".to_string());
912 }
913 _ => assert!(false, true),
914 }
915 }
916}