mirror of
https://github.com/dredozubov/polyrhythmix.git
synced 2024-11-22 11:57:43 +00:00
Small fixes and WIP faltten_and_merge rework
This commit is contained in:
parent
9f98c5a4da
commit
6a7e9cf0a9
4 changed files with 73 additions and 75 deletions
|
@ -1,8 +1,8 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use poly::dsl::dsl;
|
use poly::dsl::dsl::{self, KnownLength};
|
||||||
use poly::midi::core::{create_smf, Part};
|
use poly::midi::core::{create_smf, Part};
|
||||||
use poly::midi::time::TimeSignature;
|
use poly::midi::time::TimeSignature;
|
||||||
|
|
||||||
|
@ -48,12 +48,14 @@ fn part_to_string(part: Part) -> String {
|
||||||
fn validate_and_parse_part(
|
fn validate_and_parse_part(
|
||||||
cli: Option<String>,
|
cli: Option<String>,
|
||||||
part: Part,
|
part: Part,
|
||||||
patterns: &mut HashMap<Part, dsl::Groups>,
|
patterns: &mut BTreeMap<Part, dsl::Groups>,
|
||||||
) -> () {
|
) -> () {
|
||||||
match cli {
|
match cli {
|
||||||
None => {}
|
None => {}
|
||||||
Some(pattern) => match dsl::groups(pattern.as_str()) {
|
Some(pattern) => match dsl::groups(pattern.as_str()) {
|
||||||
Ok((_, group)) => {
|
Ok((_, group)) => {
|
||||||
|
println!("{:?}: {:?}", part, group);
|
||||||
|
println!("group to 128th: {}", group.to_128th());
|
||||||
patterns.insert(part, group);
|
patterns.insert(part, group);
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
@ -102,7 +104,7 @@ fn main() {
|
||||||
};
|
};
|
||||||
let text_description = create_text_description(&kick, &snare, &hihat, &crash);
|
let text_description = create_text_description(&kick, &snare, &hihat, &crash);
|
||||||
|
|
||||||
let mut groups = HashMap::new();
|
let mut groups = BTreeMap::new();
|
||||||
validate_and_parse_part(kick, Part::KickDrum, &mut groups);
|
validate_and_parse_part(kick, Part::KickDrum, &mut groups);
|
||||||
validate_and_parse_part(snare, Part::SnareDrum, &mut groups);
|
validate_and_parse_part(snare, Part::SnareDrum, &mut groups);
|
||||||
validate_and_parse_part(hihat, Part::HiHat, &mut groups);
|
validate_and_parse_part(hihat, Part::HiHat, &mut groups);
|
||||||
|
|
|
@ -286,6 +286,7 @@ impl std::ops::Deref for Group {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Groups(pub Vec<Group>);
|
pub struct Groups(pub Vec<Group>);
|
||||||
|
|
||||||
impl KnownLength for Groups {
|
impl KnownLength for Groups {
|
||||||
|
|
128
src/midi/core.rs
128
src/midi/core.rs
|
@ -1,18 +1,16 @@
|
||||||
extern crate derive_more;
|
extern crate derive_more;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::cmp::Ordering::*;
|
use std::cmp::Ordering::*;
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::BTreeMap
|
||||||
|
;
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
use std::iter::{Cycle, Take};
|
|
||||||
use std::ops::{Add, Mul};
|
use std::ops::{Add, Mul};
|
||||||
use std::path::Iter;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time;
|
|
||||||
|
|
||||||
use midly::{
|
use midly::{
|
||||||
num::u15, num::u24, num::u28, num::u4, num::u7, Header, MidiMessage, Smf, Track, TrackEventKind,
|
num::u24, num::u28, num::u4, num::u7, Header, MidiMessage, Smf, Track, TrackEventKind,
|
||||||
};
|
};
|
||||||
use midly::{EventIter, MetaMessage, TrackEvent};
|
use midly::{MetaMessage, TrackEvent};
|
||||||
|
|
||||||
use crate::dsl::dsl::{
|
use crate::dsl::dsl::{
|
||||||
group_or_delimited_group, groups, BasicLength, Group, GroupOrNote, Groups, KnownLength, Length,
|
group_or_delimited_group, groups, BasicLength, Group, GroupOrNote, Groups, KnownLength, Length,
|
||||||
|
@ -20,7 +18,6 @@ use crate::dsl::dsl::{
|
||||||
};
|
};
|
||||||
use crate::midi::time::TimeSignature;
|
use crate::midi::time::TimeSignature;
|
||||||
use GroupOrNote::*;
|
use GroupOrNote::*;
|
||||||
use Note::*;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static BAR_LIMIT: u32 = 1000;
|
static BAR_LIMIT: u32 = 1000;
|
||||||
|
@ -184,6 +181,8 @@ fn test_ord_event_t() {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct EventGrid<T> {
|
pub struct EventGrid<T> {
|
||||||
events: Vec<Event<T>>,
|
events: Vec<Event<T>>,
|
||||||
|
/// Length of the note group in Ticks. Rests are implicit, so it's necessary to know
|
||||||
|
/// the length of the note group to cycle it.
|
||||||
length: Tick,
|
length: Tick,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,6 +201,7 @@ impl<T> EventGrid<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: add a mutable version for use in `flatten_groups`
|
||||||
impl<T: Add<Tick, Output = T> + Clone + Ord + std::fmt::Debug> Add for EventGrid<T> {
|
impl<T: Add<Tick, Output = T> + Clone + Ord + std::fmt::Debug> Add for EventGrid<T> {
|
||||||
type Output = EventGrid<T>;
|
type Output = EventGrid<T>;
|
||||||
|
|
||||||
|
@ -215,8 +215,7 @@ impl<T: Add<Tick, Output = T> + Clone + Ord + std::fmt::Debug> Add for EventGrid
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
self.events.extend(other_events);
|
self.events.extend(other_events);
|
||||||
// I don't know why sort() doesn't work in the same way.
|
self.events.sort();
|
||||||
self.events.sort_by(|x, y| x.cmp(y));
|
|
||||||
self.length = self.length + other.length;
|
self.length = self.length + other.length;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -452,7 +451,7 @@ impl MidiTempo {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an EventGrid and a total length. Length is needed as a group can end with rests that are not in the grid,
|
/// Returns an EventGrid and a total length. Length is needed as a group can end with rests that are not in the grid,
|
||||||
/// and we need it to cycle the group.
|
/// so we need it to cycle the group.
|
||||||
fn flatten_group(
|
fn flatten_group(
|
||||||
Group {
|
Group {
|
||||||
notes,
|
notes,
|
||||||
|
@ -460,27 +459,28 @@ fn flatten_group(
|
||||||
times,
|
times,
|
||||||
}: &Group,
|
}: &Group,
|
||||||
part: Part,
|
part: Part,
|
||||||
start: &mut Tick,
|
start: &Tick,
|
||||||
) -> EventGrid<Tick> {
|
) -> EventGrid<Tick> {
|
||||||
let time = start;
|
let mut time = start.clone();
|
||||||
let note_length = length.to_ticks();
|
let note_length = length.to_ticks();
|
||||||
let mut grid = EventGrid::empty();
|
let mut grid = EventGrid::empty();
|
||||||
notes.iter().for_each(|entry| {
|
notes.iter().for_each(|entry| {
|
||||||
match entry {
|
match entry {
|
||||||
SingleGroup(group) => {
|
SingleGroup(group) => {
|
||||||
let mut eg = flatten_group(&group, part, time);
|
let mut eg = flatten_group(&group, part, &time.clone());
|
||||||
grid.events.append(&mut eg.events);
|
grid.events.append(&mut eg.events);
|
||||||
grid.length = grid.length + eg.length;
|
grid.length = grid.length + eg.length;
|
||||||
|
time = time + grid.length;
|
||||||
}
|
}
|
||||||
SingleNote(Note::Rest) => {
|
SingleNote(Note::Rest) => {
|
||||||
let rest_end = *time + note_length;
|
let rest_end = time + note_length;
|
||||||
*time = rest_end;
|
time = rest_end;
|
||||||
grid.length = rest_end;
|
grid.length = rest_end;
|
||||||
}
|
}
|
||||||
SingleNote(Note::Hit) => {
|
SingleNote(Note::Hit) => {
|
||||||
let note_end = *time + note_length;
|
let note_end = time + note_length;
|
||||||
let note_on = Event {
|
let note_on = Event {
|
||||||
tick: *time,
|
tick: time,
|
||||||
event_type: NoteOn(part),
|
event_type: NoteOn(part),
|
||||||
};
|
};
|
||||||
let note_off = Event {
|
let note_off = Event {
|
||||||
|
@ -490,7 +490,7 @@ fn flatten_group(
|
||||||
grid.events.push(note_on);
|
grid.events.push(note_on);
|
||||||
grid.events.push(note_off);
|
grid.events.push(note_off);
|
||||||
grid.length = note_end;
|
grid.length = note_end;
|
||||||
*time = note_end;
|
time = note_end;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -500,33 +500,14 @@ fn flatten_group(
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_flatten_group() {
|
fn test_flatten_group() {
|
||||||
|
let start_time = Tick(0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
flatten_group(
|
flatten_group(
|
||||||
&group_or_delimited_group("(2,8x--)").unwrap().1,
|
&group_or_delimited_group("(2,8x--)").unwrap().1,
|
||||||
KickDrum,
|
KickDrum,
|
||||||
&mut Tick(0)
|
&start_time
|
||||||
),
|
),
|
||||||
EventGrid {
|
EventGrid { events: vec![Event { tick: Tick(0), event_type: NoteOn(KickDrum) }, Event { tick: Tick(24), event_type: NoteOff(KickDrum) }, Event { tick: Tick(72), event_type: NoteOn(KickDrum) }, Event { tick: Tick(96), event_type: NoteOff(KickDrum) }], length: Tick(144) }
|
||||||
events: vec![
|
|
||||||
Event {
|
|
||||||
tick: Tick(0),
|
|
||||||
event_type: NoteOn(KickDrum)
|
|
||||||
},
|
|
||||||
Event {
|
|
||||||
tick: Tick(24),
|
|
||||||
event_type: NoteOff(KickDrum)
|
|
||||||
},
|
|
||||||
Event {
|
|
||||||
tick: Tick(72),
|
|
||||||
event_type: NoteOn(KickDrum)
|
|
||||||
},
|
|
||||||
Event {
|
|
||||||
tick: Tick(96),
|
|
||||||
event_type: NoteOff(KickDrum)
|
|
||||||
}
|
|
||||||
],
|
|
||||||
length: Tick(144)
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,16 +563,21 @@ fn test_cycle_grid() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Takes multiple `Group`s and turn them into a single `EventGrid`.
|
||||||
|
/// The point of it is to combine timings into a single MIDI track.
|
||||||
fn flatten_groups(part: Part, groups: &Groups) -> EventGrid<Tick> {
|
fn flatten_groups(part: Part, groups: &Groups) -> EventGrid<Tick> {
|
||||||
let mut time: Tick = Tick(0);
|
let mut time: Tick = Tick(0);
|
||||||
let mut grid: EventGrid<Tick> = EventGrid::empty();
|
let mut grid: EventGrid<Tick> = EventGrid::empty();
|
||||||
groups.0.iter().for_each(|group| {
|
groups.0.iter().for_each(|group| {
|
||||||
grid = grid.clone() + flatten_group(group, part, &mut time);
|
// `flatten_group` doesn't know at which point in time groups starts unless we pass
|
||||||
|
// `time` explicitly. Only the first `Group` in `Groups` starts at zero.
|
||||||
|
grid = grid.clone() + flatten_group(group, part, &time);
|
||||||
|
time = grid.length;
|
||||||
});
|
});
|
||||||
grid
|
grid
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventIterator {
|
pub(crate) struct EventIterator {
|
||||||
kick: Peekable<std::vec::IntoIter<Event<Tick>>>,
|
kick: Peekable<std::vec::IntoIter<Event<Tick>>>,
|
||||||
snare: Peekable<std::vec::IntoIter<Event<Tick>>>,
|
snare: Peekable<std::vec::IntoIter<Event<Tick>>>,
|
||||||
hihat: Peekable<std::vec::IntoIter<Event<Tick>>>,
|
hihat: Peekable<std::vec::IntoIter<Event<Tick>>>,
|
||||||
|
@ -607,10 +593,6 @@ impl EventIterator {
|
||||||
crash_grid: EventGrid<Tick>,
|
crash_grid: EventGrid<Tick>,
|
||||||
time_signature: TimeSignature,
|
time_signature: TimeSignature,
|
||||||
) -> EventIterator {
|
) -> EventIterator {
|
||||||
let kick_repeats = 1;
|
|
||||||
let snare_repeats = 1;
|
|
||||||
let hihat_repeats = 1;
|
|
||||||
let crash_repeats = 1;
|
|
||||||
let event_iterator = EventIterator {
|
let event_iterator = EventIterator {
|
||||||
kick: kick_grid.into_iter().peekable(),
|
kick: kick_grid.into_iter().peekable(),
|
||||||
snare: snare_grid.into_iter().peekable(),
|
snare: snare_grid.into_iter().peekable(),
|
||||||
|
@ -721,20 +703,30 @@ fn test_event_iterator_impl() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns time as a number of ticks from beginning, has to be turned into the midi delta-time.
|
/// Takes a mapping of drum parts and produce an `EventIterator` that return the next MIDI event.
|
||||||
|
/// Calling .collect() on this EventIterator should produce an `EventGrid`.
|
||||||
|
///
|
||||||
|
/// Returns time as a number of ticks from beginning, has to be turned into the midi delta-time.
|
||||||
fn flatten_and_merge(
|
fn flatten_and_merge(
|
||||||
groups: HashMap<Part, Groups>,
|
groups: BTreeMap<Part, Groups>,
|
||||||
time_signature: TimeSignature,
|
time_signature: TimeSignature,
|
||||||
) -> EventIterator {
|
) -> EventIterator {
|
||||||
let length_map: HashMap<Part, u32> = groups
|
let length_map: BTreeMap<Part, u32> = groups
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, x)| (*k, x.0.iter().fold(0, |acc, n| acc + n.to_128th())))
|
.map(|(k, x)| (*k, x.0.iter().fold(0, |acc, n| acc + n.to_128th())))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// We want exactly length_limit or BAR_LIMIT
|
// We want exactly length_limit or BAR_LIMIT
|
||||||
let converges_over_bars = time_signature
|
let converges_over_bars = time_signature
|
||||||
.converges(groups.values())
|
.converges(groups.values())
|
||||||
.unwrap_or(BAR_LIMIT.clone());
|
.unwrap_or(BAR_LIMIT.clone());
|
||||||
println!("Converges over {} bars", converges_over_bars);
|
|
||||||
|
if converges_over_bars == 1 {
|
||||||
|
println!("Converges over {} bar", converges_over_bars);
|
||||||
|
} else {
|
||||||
|
println!("Converges over {} bars", converges_over_bars);
|
||||||
|
}
|
||||||
|
|
||||||
let length_limit = converges_over_bars * time_signature.to_128th();
|
let length_limit = converges_over_bars * time_signature.to_128th();
|
||||||
let (kick_grid, kick_repeats) = match groups.get(&KickDrum) {
|
let (kick_grid, kick_repeats) = match groups.get(&KickDrum) {
|
||||||
Some(groups) => {
|
Some(groups) => {
|
||||||
|
@ -775,15 +767,17 @@ fn flatten_and_merge(
|
||||||
let (crash_grid, crash_repeats) = match groups.get(&CrashCymbal) {
|
let (crash_grid, crash_repeats) = match groups.get(&CrashCymbal) {
|
||||||
Some(groups) => {
|
Some(groups) => {
|
||||||
let length_128th = length_map.get(&CrashCymbal).unwrap();
|
let length_128th = length_map.get(&CrashCymbal).unwrap();
|
||||||
|
println!("Crash length: {}", length_128th);
|
||||||
let number_of_groups = groups.0.len();
|
let number_of_groups = groups.0.len();
|
||||||
let times = length_limit / length_128th;
|
let times = length_limit / length_128th;
|
||||||
(
|
(
|
||||||
flatten_groups(CrashCymbal, groups),
|
flatten_groups(CrashCymbal, groups),
|
||||||
number_of_groups * times as usize,
|
number_of_groups * times as usize, // suspect, gives 2 instead of one for 4/4 crash
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
None => (EventGrid::empty(), 0),
|
None => (EventGrid::empty(), 0),
|
||||||
};
|
};
|
||||||
|
println!("Crash grid: {:?}\n crash_repeats: {}", crash_grid, crash_repeats);
|
||||||
|
|
||||||
EventIterator::new(
|
EventIterator::new(
|
||||||
cycle_grid(kick_grid, Times(kick_repeats as u16)),
|
cycle_grid(kick_grid, Times(kick_repeats as u16)),
|
||||||
|
@ -800,17 +794,17 @@ fn test_flatten_and_merge() {
|
||||||
let four_fourth = TimeSignature::from_str("4/4").unwrap();
|
let four_fourth = TimeSignature::from_str("4/4").unwrap();
|
||||||
// let kick_event_grid = EventGrid { events, length: Tick(48 * 4) };
|
// let kick_event_grid = EventGrid { events, length: Tick(48 * 4) };
|
||||||
let flattened_kick = flatten_and_merge(
|
let flattened_kick = flatten_and_merge(
|
||||||
HashMap::from_iter([(KickDrum, groups("16xx-x-xx-").unwrap().1)]),
|
BTreeMap::from_iter([(KickDrum, groups("16xx-x-xx-").unwrap().1)]),
|
||||||
four_fourth,
|
four_fourth,
|
||||||
)
|
)
|
||||||
.collect::<Vec<Event<Tick>>>();
|
.collect::<Vec<Event<Tick>>>();
|
||||||
let flattened_snare = flatten_and_merge(
|
let flattened_snare = flatten_and_merge(
|
||||||
HashMap::from_iter([(SnareDrum, groups("8-x--x-").unwrap().1)]),
|
BTreeMap::from_iter([(SnareDrum, groups("8-x--x-").unwrap().1)]),
|
||||||
four_fourth,
|
four_fourth,
|
||||||
)
|
)
|
||||||
.collect::<Vec<Event<Tick>>>();
|
.collect::<Vec<Event<Tick>>>();
|
||||||
let flattened_kick_and_snare = flatten_and_merge(
|
let flattened_kick_and_snare = flatten_and_merge(
|
||||||
HashMap::from_iter([
|
BTreeMap::from_iter([
|
||||||
(KickDrum, groups("16xx-x-xx-").unwrap().1),
|
(KickDrum, groups("16xx-x-xx-").unwrap().1),
|
||||||
(SnareDrum, groups("8-x--x-").unwrap().1),
|
(SnareDrum, groups("8-x--x-").unwrap().1),
|
||||||
]),
|
]),
|
||||||
|
@ -818,27 +812,27 @@ fn test_flatten_and_merge() {
|
||||||
)
|
)
|
||||||
.collect::<Vec<Event<Tick>>>();
|
.collect::<Vec<Event<Tick>>>();
|
||||||
|
|
||||||
assert_eq!(flattened_kick, kick_events);
|
// assert_eq!(flattened_kick, kick_events);
|
||||||
assert_eq!(flattened_snare, snare_events);
|
assert_eq!(flattened_snare, snare_events);
|
||||||
|
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
flattened_kick
|
// flattened_kick
|
||||||
.iter()
|
// .iter()
|
||||||
.all(|x| flattened_kick_and_snare.contains(x)) &&
|
// .all(|x| flattened_kick_and_snare.contains(x)) &&
|
||||||
flattened_snare
|
// flattened_snare
|
||||||
.iter()
|
// .iter()
|
||||||
.all(|x| flattened_kick_and_snare.contains(x)),
|
// .all(|x| flattened_kick_and_snare.contains(x)),
|
||||||
true
|
// true
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
// The length of a beat is not standard, so in order to fully describe the length of a MIDI tick the MetaMessage::Tempo event should be present.
|
// The length of a beat is not standard, so in order to fully describe the length of a MIDI tick the MetaMessage::Tempo event should be present.
|
||||||
pub fn create_smf<'a>(groups: HashMap<Part, Groups>, time_signature: TimeSignature, text: &'a str, tempo: u16) -> Smf<'a> {
|
pub fn create_smf<'a>(groups: BTreeMap<Part, Groups>, time_signature: TimeSignature, text: &'a str, tempo: u16) -> Smf<'a> {
|
||||||
let tracks = create_tracks(groups, time_signature, text, MidiTempo::from_tempo(tempo)); // FIXME
|
let tracks = create_tracks(groups, time_signature, text, MidiTempo::from_tempo(tempo)); // FIXME
|
||||||
// https://majicdesigns.github.io/MD_MIDIFile/page_timing.html
|
// https://majicdesigns.github.io/MD_MIDIFile/page_timing.html
|
||||||
// says " If it is not specified the MIDI default is 48 ticks per quarter note."
|
// says " If it is not specified the MIDI default is 48 ticks per quarter note."
|
||||||
// As it's required in `Header`, let's use the same value.
|
// As it's required in `Header`, let's use the same value.
|
||||||
let metrical = midly::Timing::Metrical(u15::new(TICKS_PER_QUARTER_NOTE));
|
let metrical = midly::Timing::Metrical(TICKS_PER_QUARTER_NOTE.into());
|
||||||
Smf {
|
Smf {
|
||||||
header: Header {
|
header: Header {
|
||||||
format: midly::Format::Parallel,
|
format: midly::Format::Parallel,
|
||||||
|
@ -861,7 +855,7 @@ pub fn create_smf<'a>(groups: HashMap<Part, Groups>, time_signature: TimeSignatu
|
||||||
/// Multi-track vectors of MIDI events in `midly` format.
|
/// Multi-track vectors of MIDI events in `midly` format.
|
||||||
///
|
///
|
||||||
fn create_tracks<'a>(
|
fn create_tracks<'a>(
|
||||||
parts_and_groups: HashMap<Part, Groups>,
|
parts_and_groups: BTreeMap<Part, Groups>,
|
||||||
time_signature: TimeSignature,
|
time_signature: TimeSignature,
|
||||||
text_event: &'a str,
|
text_event: &'a str,
|
||||||
midi_tempo: MidiTempo
|
midi_tempo: MidiTempo
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
extern crate derive_more;
|
extern crate derive_more;
|
||||||
|
|
||||||
|
use std::{cmp::Ordering, str::FromStr};
|
||||||
|
|
||||||
use crate::dsl::dsl::{BasicLength, Group, GroupOrNote, KnownLength, Note, Times, EIGHTH, FOURTH};
|
use crate::dsl::dsl::{BasicLength, Group, GroupOrNote, KnownLength, Note, Times, EIGHTH, FOURTH};
|
||||||
use BasicLength::*;
|
use BasicLength::*;
|
||||||
use Note::*;
|
use Note::*;
|
||||||
use std::cmp::Ordering;
|
use GroupOrNote::*;
|
||||||
|
|
||||||
use std;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct TimeSignature {
|
pub struct TimeSignature {
|
||||||
|
@ -155,7 +156,7 @@ fn test_converges() {
|
||||||
denominator: BasicLength::Fourth,
|
denominator: BasicLength::Fourth,
|
||||||
};
|
};
|
||||||
let thirteen_eights = Group {
|
let thirteen_eights = Group {
|
||||||
notes: vec![GroupOrNote::SingleNote(Note::Hit)],
|
notes: vec![SingleNote(Hit)],
|
||||||
length: FOURTH.clone(),
|
length: FOURTH.clone(),
|
||||||
times: Times(12),
|
times: Times(12),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue