Convert EventGrid<Tick> to EventGrid<Delta>

This commit is contained in:
Denis Redozubov 2023-05-09 20:29:10 +04:00
parent 1d74b52cff
commit b48546af61
3 changed files with 81 additions and 43 deletions

View file

@ -10,5 +10,6 @@
"./Cargo.toml", "./Cargo.toml",
"./Cargo.toml", "./Cargo.toml",
"./Cargo.toml" "./Cargo.toml"
] ],
"docwriter.style": "RustDoc"
} }

View file

@ -51,9 +51,8 @@ fn validate_and_parse_part(cli: Option<String>, part: Part, patterns: &mut HashM
Some(pattern) => { Some(pattern) => {
match dsl::groups(pattern.as_str()) { match dsl::groups(pattern.as_str()) {
Ok((_, group)) => { patterns.insert(part, group); }, Ok((_, group)) => { patterns.insert(part, group); },
Err(e) => { Err(_) => {
println!("{} pattern is malformed.", part_to_string(part)); panic!("{} pattern is malformed.", part_to_string(part))
exit(1)
} }
} }
} }

View file

@ -5,7 +5,7 @@ use std::ops::Add;
use midly::{live::LiveEvent, num::u15, Header, MidiMessage, Smf, Track}; use midly::{live::LiveEvent, num::u15, Header, MidiMessage, Smf, Track};
use crate::dsl::dsl::{ use crate::dsl::dsl::{
group_or_delimited_group, groups, BasicLength, Group, Length, ModdedLength, Note, Times, group_or_delimited_group, groups, BasicLength, Length, Group, ModdedLength, Note, Times,
}; };
// Typically used as number of ticks since the beginning of the track. // Typically used as number of ticks since the beginning of the track.
@ -18,6 +18,7 @@ use crate::dsl::dsl::{
PartialOrd, PartialOrd,
Ord, Ord,
derive_more::Add, derive_more::Add,
derive_more::Sub,
derive_more::Mul, derive_more::Mul,
derive_more::Display, derive_more::Display,
)] )]
@ -142,11 +143,27 @@ impl<T> EventGrid<T> {
} }
} }
impl EventGrid<Tick> {
/// Converts a sorted `EventGrid<Tick>`
fn to_delta(&self) -> EventGrid<Delta> {
let mut time = Tick(0);
let mut delta_grid = EventGrid::new();
for e in &self.events {
let delta = e.tick - time;
time = time + delta;
delta_grid.events.push(Event { tick: Delta(delta.0), event_type: e.event_type })
}
delta_grid
}
}
#[allow(dead_code)] #[allow(dead_code)]
static TICKS_PER_QUARTER_NOTE: u16 = 48; static TICKS_PER_QUARTER_NOTE: u16 = 48;
fn basic_length_to_ticks(basic_length: BasicLength) -> Tick { impl BasicLength {
match basic_length { /// `BasicLength` to MIDI Ticks
fn to_ticks(&self) -> Tick {
match self {
BasicLength::Whole => Tick((TICKS_PER_QUARTER_NOTE * 4) as u128), BasicLength::Whole => Tick((TICKS_PER_QUARTER_NOTE * 4) as u128),
BasicLength::Half => Tick((TICKS_PER_QUARTER_NOTE * 2) as u128), BasicLength::Half => Tick((TICKS_PER_QUARTER_NOTE * 2) as u128),
BasicLength::Fourth => Tick(TICKS_PER_QUARTER_NOTE as u128), BasicLength::Fourth => Tick(TICKS_PER_QUARTER_NOTE as u128),
@ -156,34 +173,55 @@ fn basic_length_to_ticks(basic_length: BasicLength) -> Tick {
BasicLength::SixtyFourth => Tick((TICKS_PER_QUARTER_NOTE / 16) as u128), BasicLength::SixtyFourth => Tick((TICKS_PER_QUARTER_NOTE / 16) as u128),
} }
} }
}
fn modded_length_to_ticks(modded_length: ModdedLength) -> Tick { impl ModdedLength {
match modded_length { /// `ModdedLength` to MIDI Ticks
ModdedLength::Plain(blen) => basic_length_to_ticks(blen), fn to_ticks(&self) -> Tick {
match self {
ModdedLength::Plain(blen) => blen.to_ticks(),
ModdedLength::Dotted(blen) => { ModdedLength::Dotted(blen) => {
let Tick(whole) = basic_length_to_ticks(blen); let Tick(whole) = blen.to_ticks();
let half = whole / 2; let half = whole / 2;
Tick(whole + half) Tick(whole + half)
} }
} }
} }
}
fn length_to_ticks(length: Length) -> Tick { impl Length {
match length { /// Note length to MIDI ticks
Length::Simple(mlen) => modded_length_to_ticks(mlen), /// The function converts a musical note length to ticks, accounting for simple notes, tied notes, and
/// triplets.
///
/// Arguments:
///
/// * `length`: `length` is a variable of type `Length`, which is an enum that represents different
/// types of musical note lengths. The function `length_to_ticks` takes a `Length` as input and returns
/// a `Tick`, which is a struct representing the number of ticks (a unit of time in music
///
/// Returns:
///
/// The function `length_to_ticks` takes a `Length` enum as input and returns a `Tick` value. The `Tick`
/// value represents the duration of the note in ticks, which is a unit of time used in music notation
/// software.
fn to_ticks(&self) -> Tick {
match self {
Length::Simple(mlen) => mlen.to_ticks(),
Length::Tied(first, second) => { Length::Tied(first, second) => {
modded_length_to_ticks(first) + modded_length_to_ticks(second) first.to_ticks() + second.to_ticks()
} }
Length::Triplet(mlen) => { Length::Triplet(mlen) => {
let Tick(straight) = modded_length_to_ticks(mlen); let Tick(straight) = mlen.to_ticks();
let triplet = straight * 2 / 3; let triplet = straight * 2 / 3;
Tick(triplet) Tick(triplet)
} }
} }
} }
}
// 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. /// and we need it to cycle the group.
fn flatten_group( fn flatten_group(
Group { Group {
notes, notes,
@ -194,7 +232,7 @@ fn flatten_group(
start: &mut Tick, start: &mut Tick,
) -> EventGrid<Tick> { ) -> EventGrid<Tick> {
let time = start; let time = start;
let note_length = length_to_ticks(*length); let note_length = length.to_ticks();
let mut grid = EventGrid::new(); let mut grid = EventGrid::new();
notes.iter().for_each(|entry| { notes.iter().for_each(|entry| {
match entry { match entry {
@ -230,7 +268,6 @@ fn flatten_group(
#[test] #[test]
fn test_flatten_group() { fn test_flatten_group() {
let empty: EventGrid<Tick> = EventGrid::new();
assert_eq!( assert_eq!(
flatten_group( flatten_group(
&group_or_delimited_group("(2,8x--)").unwrap().1, &group_or_delimited_group("(2,8x--)").unwrap().1,
@ -263,7 +300,7 @@ fn test_flatten_group() {
fn cycle_grid(event_grid: EventGrid<Tick>, times: Times) -> EventGrid<Tick> { fn cycle_grid(event_grid: EventGrid<Tick>, times: Times) -> EventGrid<Tick> {
let mut grid = EventGrid::new(); let mut grid = EventGrid::new();
for i in 1..(times.0 + 1) { for _ in 1..(times.0 + 1) {
grid = grid + event_grid.clone(); grid = grid + event_grid.clone();
} }
grid grid
@ -356,7 +393,7 @@ fn flatten_and_merge(groups: HashMap<Part, Vec<Group>>) -> EventGrid<Tick> {
} }
// 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.
fn create_smf<'a>(groups: HashMap<Part, Vec<Group>>) -> Smf<'a> { pub fn create_smf<'a>(groups: HashMap<Part, Vec<Group>>) -> Smf<'a> {
let tracks = vec![]; // create_tracks(groups); // FIXME let tracks = vec![]; // create_tracks(groups); // 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."
@ -371,6 +408,7 @@ fn create_smf<'a>(groups: HashMap<Part, Vec<Group>>) -> Smf<'a> {
} }
} }
// fn create_tracks(groups: HashMap<Part, Vec<Group>>) -> Vec<Vec<midly::TrackEvent>> { /// Translates drum parts to a single MIDI track.
// todo!() fn create_tracks<'a>(parts_and_groups: HashMap<Part, Vec<Group>>) -> Vec<Vec<midly::TrackEvent<'a>>> {
// } let event_grid = flatten_and_merge(parts_and_groups).to_delta();
}