chord_composer/
lib.rs

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/// Possible failures.
15#[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/// Possible successes.
30#[derive(Debug, PartialEq)]
31pub enum SuccessResult {
32  Export(Vec<String>),
33  ExportTemplate,
34  Playback,
35}
36
37/// Returns all the internally supported chord keywords.
38/// * Interpreted when parsing composition YAML.
39/// * Case sensitive.
40/// * Considered for future removal.
41pub fn get_chord_keywords() -> Vec<&'static str> {
42  theory::chords::chord_to_string_array()
43}
44
45/// Export a composition to a midi files. Each pattern will be
46/// exported as a different midi file.
47///
48/// # Arguments
49/// * `composition` - The composition to export from.
50/// * `export_path` - The path to export the midi patterns.
51pub 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
64/// Load a composition then export it to midi files. Each pattern will be
65/// exported as a different midi file.
66///
67/// # Arguments
68/// * `composition_path` - The file path to the composition YAML file and the export path
69/// of the midi files.
70pub 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
77/// Helper to build music events. Chord intervals will be transposed and
78/// converted to midi key values in the returned `PatternEvent`.
79///
80/// # Arguments
81/// * `bar` - The bar for the music event.
82/// * `beat` - The beat of the msuic event.
83/// * `beat_interval` - The interval value between beats. There are 8
84/// * `beat_intervals` in a `beat`, this is the same as a 16th.
85/// * `chord_intervals` - The note intervals in the chord.
86/// * `transpose` - The transpose of all the chord intervals.
87pub 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
102/// Play a composition starting from the composition's pattern index
103/// and time.
104///
105/// # Arguments
106/// * `composition` - The composition to play.
107/// * `performance_state` - The state performance with callbacks to be triggered.
108/// * `is_metronome_enabled` - Set if the metronome is to be played during playback.
109/// This is only relevant when `sample_paths_metronome` contains audio file paths
110/// and the compiler feature `with-sound` is used.
111/// * `sample_paths_metronome` - Paths to the 2 metronome audio files, tick and tock.
112/// * `sample_paths_piano` - Paths to the playback instrument audio files.
113/// * `playback_start` - The `MusicTime` to begin playback at.
114/// * `pattern_start_index` - The index of the `MusicPattern` to begin from.
115pub 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
136/// Play a composition starting from a composition's pattern name
137/// and time.
138///
139/// # Arguments
140/// * `composition` - The composition to play.
141/// * `performance_state` - The state performance with callbacks to be triggered.
142/// * `is_metronome_enabled` - Set if the metronome is to be played during playback.
143/// This is only relevant when `sample_paths_metronome` contains audio file paths
144/// and the compiler feature `with-sound` is used.
145/// * `sample_paths_metronome` - Paths to the 2 metronome audio files, tick and tock.
146/// * `sample_paths_piano` - Paths to the playback instrument audio files.
147/// * `playback_start` - The `MusicTime` to begin playback at.
148/// * `pattern_start_name` - The name of the `MusicPattern` to begin from.
149///
150/// # Example
151/// ```
152/// use chord_composer::{
153///   performance::performance_engine::PerformanceState,
154///   theory::composition::{Composition, Pattern, PatternEvent},
155///   FailResult, SuccessResult,
156/// };
157/// use music_timer::{music_time::MusicTime, time_signature::TimeSignature};
158/// 
159/// struct MyState {
160///   events: u16,
161///   current_time: MusicTime,
162/// }
163/// impl PerformanceState for MyState {
164///   fn on_ready(&mut self, _composition: &Composition) {
165///     println!("on_ready");
166///   }
167///   fn on_beat_interval_change(&mut self, current_time: &MusicTime) {
168///     self.current_time = current_time.clone();
169///     println!("on_beat_interval_change: {:?}", current_time);
170///   }
171///   fn on_beat_change(&mut self, current_time: &MusicTime) {
172///     println!("on_beat_change: {:?}", current_time);
173///   }
174///   fn on_bar_change(&mut self, current_time: &MusicTime) {
175///     println!("on_bar_change: {:?}", current_time);
176///   }
177///   fn on_event(&mut self, event: &PatternEvent) {
178///     self.events += 1;
179///
180///     if self.events == 1 {
181///       assert_eq!(event, &chord_composer::build_event(2, 1, 1, vec![1], -1));
182///     } else if self.events == 2 {
183///       assert_eq!(event, &chord_composer::build_event(3, 5, 1, vec![2], -2));
184///     }
185///     println!("on_event");
186///   }
187///   fn on_pattern_playback_begin(&mut self, _pattern: &Pattern) {
188///     println!("on_pattern_playback_begin");
189///   }
190///   fn on_pattern_playback_end(&mut self, _pattern: &Pattern) {
191///     println!("on_pattern_playback_end");
192///   }
193///   fn on_completed(&mut self, composition: &Composition) {
194///     assert_eq!(composition.get_name(), "test compo");
195///     println!("on_completed");
196///   }
197/// }
198///
199/// let composition = Composition::new_with_patterns(
200///   "test compo",
201///   vec![
202///     Pattern::new_with_events(
203///       "a",
204///       100,
205///       TimeSignature::default(),
206///       vec![
207///         chord_composer::build_event(1, 1, 1, vec![0, 3, 7], 0),
208///         chord_composer::build_event(2, 1, 1, vec![0, 3, 7], 1),
209///         chord_composer::build_event(3, 5, 1, vec![0, 3, 7], 2),
210///       ],
211///     ),
212///     Pattern::new_with_events(
213///       "b",
214///       150,
215///       TimeSignature::new(7, 4),
216///       vec![
217///         chord_composer::build_event(3, 5, 1, vec![2], -2),
218///         chord_composer::build_event(2, 1, 1, vec![1], -1),
219///         chord_composer::build_event(1, 1, 1, vec![0], 0),
220///       ],
221///     ),
222///   ],
223/// );
224///
225/// let mut my_state = MyState {
226///   events: 0,
227///   current_time: MusicTime::default(),
228/// };
229///
230/// assert_eq!(
231///   chord_composer::play_from(
232///     &composition,
233///     &mut my_state,
234///     false,
235///     &Vec::new(),
236///     &Vec::new(),
237///     &MusicTime::new(2, 1, 1),
238///     "b"
239///   ),
240///   Ok(SuccessResult::Playback),
241/// );
242///
243/// assert_eq!(my_state.events, 2);
244/// assert_eq!(my_state.current_time, MusicTime::new(3, 7, 8));
245///
246/// assert_eq!(
247///   chord_composer::play_from(
248///     &composition,
249///     &mut my_state,
250///     false,
251///     &Vec::new(),
252///     &Vec::new(),
253///     &MusicTime::new(2, 1, 1),
254///     "c"
255///   ),
256///   Err(FailResult::NoFoundPattern("c".to_owned())),
257/// );
258/// ```
259pub 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
287/// Play a composition and all it's patterns from the start.
288///
289/// # Arguments
290/// * `composition` - The composition to play.
291/// * `performance_state` - The state performance with callbacks to be triggered.
292/// * `is_metronome_enabled` - Set if the metronome is to be played during playback.
293/// This is only relevant when `sample_paths_metronome` contains audio file paths
294/// and the compiler feature `with-sound` is used.
295/// * `sample_paths_metronome` - Paths to the 2 metronome audio files, tick and tock.
296/// * `sample_paths_piano` - Paths to the playback instrument audio files.
297pub 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
315/// Load a YAML file of a composition then play all it's patterns from the start.
316///
317/// # Arguments
318/// * `composition_path` - Path to the composition YAML file.
319/// * `performance_state` - The state performance with callbacks to be triggered.
320/// * `is_metronome_enabled` - Set if the metronome is to be played during playback.
321/// This is only relevant when `sample_paths_metronome` contains audio file paths
322/// and the compiler feature `with-sound` is used.
323/// * `sample_paths_metronome` - Paths to the 2 metronome audio files, tick and tock.
324/// * `sample_paths_piano` - Paths to the playback instrument audio files.
325pub 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
343/// Parse YAML of a composition then play all it's patterns from the start.
344///
345/// # Arguments
346/// * `composition_yaml` - Composition YAML.
347/// * `performance_state` - The state performance with callbacks to be triggered.
348/// * `is_metronome_enabled` - Set if the metronome is to be played during playback.
349/// This is only relevant when `sample_paths_metronome` contains audio file paths
350/// and the compiler feature `with-sound` is used.
351/// * `sample_paths_metronome` - Paths to the 2 metronome audio files, tick and tock.
352/// * `sample_paths_piano` - Paths to the playback instrument audio files.
353pub 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
371/// Load a YAML file of a composition then play it's patterns starting from a composition's pattern name
372/// and time.
373///
374/// # Arguments
375/// * `composition_path` - Path to the composition YAML file.
376/// * `performance_state` - The state performance with callbacks to be triggered.
377/// * `is_metronome_enabled` - Set if the metronome is to be played during playback.
378/// This is only relevant when `sample_paths_metronome` contains audio file paths
379/// and the compiler feature `with-sound` is used.
380/// * `sample_paths_metronome` - Paths to the 2 metronome audio files, tick and tock.
381/// * `sample_paths_piano` - Paths to the playback instrument audio files.
382/// * `playback_start` - The `MusicTime` to begin playback at.
383/// * `pattern_start_name` - The name of the `MusicPattern` to begin from.
384pub 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
406/// Parse YAML of a composition then play all it's patterns starting from a composition's pattern name
407/// and time.
408///
409/// # Arguments
410/// * `composition_yaml` - Path to the composition YAML file.
411/// * `performance_state` - The state performance with callbacks to be triggered.
412/// * `is_metronome_enabled` - Set if the metronome is to be played during playback.
413/// This is only relevant when `sample_paths_metronome` contains audio file paths
414/// and the compiler feature `with-sound` is used.
415/// * `sample_paths_metronome` - Paths to the 2 metronome audio files, tick and tock.
416/// * `sample_paths_piano` - Paths to the playback instrument audio files.
417/// * `playback_start` - The `MusicTime` to begin playback at.
418/// * `pattern_start_name` - The name of the `MusicPattern` to begin from.
419pub 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
441/// Export a template of a composition YAML file to a path.
442///
443/// # Arguments
444/// * `path` - The path to export the template to.
445pub 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
517/// Convert YAML deserialized composition parameters to a `Composition` data type.
518///
519/// # Arguments
520/// * `params` - The `CompositionParameters` to convert into a `Composition`.
521fn 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(&params.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(&params.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(&params.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(&params.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(&params.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(&params.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(&params.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(&params.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(&params.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}