mirror of
https://github.com/dredozubov/polyrhythmix.git
synced 2024-11-22 11:57:43 +00:00
Convert EventGrid<Tick> to EventGrid<Delta>
This commit is contained in:
parent
1d74b52cff
commit
b48546af61
3 changed files with 81 additions and 43 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -10,5 +10,6 @@
|
||||||
"./Cargo.toml",
|
"./Cargo.toml",
|
||||||
"./Cargo.toml",
|
"./Cargo.toml",
|
||||||
"./Cargo.toml"
|
"./Cargo.toml"
|
||||||
]
|
],
|
||||||
|
"docwriter.style": "RustDoc"
|
||||||
}
|
}
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
116
src/midi/core.rs
116
src/midi/core.rs
|
@ -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,48 +143,85 @@ 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
|
||||||
BasicLength::Whole => Tick((TICKS_PER_QUARTER_NOTE * 4) as u128),
|
fn to_ticks(&self) -> Tick {
|
||||||
BasicLength::Half => Tick((TICKS_PER_QUARTER_NOTE * 2) as u128),
|
match self {
|
||||||
BasicLength::Fourth => Tick(TICKS_PER_QUARTER_NOTE as u128),
|
BasicLength::Whole => Tick((TICKS_PER_QUARTER_NOTE * 4) as u128),
|
||||||
BasicLength::Eighth => Tick((TICKS_PER_QUARTER_NOTE / 2) as u128),
|
BasicLength::Half => Tick((TICKS_PER_QUARTER_NOTE * 2) as u128),
|
||||||
BasicLength::Sixteenth => Tick((TICKS_PER_QUARTER_NOTE / 4) as u128),
|
BasicLength::Fourth => Tick(TICKS_PER_QUARTER_NOTE as u128),
|
||||||
BasicLength::ThirtySecond => Tick((TICKS_PER_QUARTER_NOTE / 8) as u128),
|
BasicLength::Eighth => Tick((TICKS_PER_QUARTER_NOTE / 2) as u128),
|
||||||
BasicLength::SixtyFourth => Tick((TICKS_PER_QUARTER_NOTE / 16) as u128),
|
BasicLength::Sixteenth => Tick((TICKS_PER_QUARTER_NOTE / 4) as u128),
|
||||||
}
|
BasicLength::ThirtySecond => Tick((TICKS_PER_QUARTER_NOTE / 8) as u128),
|
||||||
}
|
BasicLength::SixtyFourth => Tick((TICKS_PER_QUARTER_NOTE / 16) as u128),
|
||||||
|
|
||||||
fn modded_length_to_ticks(modded_length: ModdedLength) -> Tick {
|
|
||||||
match modded_length {
|
|
||||||
ModdedLength::Plain(blen) => basic_length_to_ticks(blen),
|
|
||||||
ModdedLength::Dotted(blen) => {
|
|
||||||
let Tick(whole) = basic_length_to_ticks(blen);
|
|
||||||
let half = whole / 2;
|
|
||||||
Tick(whole + half)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn length_to_ticks(length: Length) -> Tick {
|
impl ModdedLength {
|
||||||
match length {
|
/// `ModdedLength` to MIDI Ticks
|
||||||
Length::Simple(mlen) => modded_length_to_ticks(mlen),
|
fn to_ticks(&self) -> Tick {
|
||||||
Length::Tied(first, second) => {
|
match self {
|
||||||
modded_length_to_ticks(first) + modded_length_to_ticks(second)
|
ModdedLength::Plain(blen) => blen.to_ticks(),
|
||||||
}
|
ModdedLength::Dotted(blen) => {
|
||||||
Length::Triplet(mlen) => {
|
let Tick(whole) = blen.to_ticks();
|
||||||
let Tick(straight) = modded_length_to_ticks(mlen);
|
let half = whole / 2;
|
||||||
let triplet = straight * 2 / 3;
|
Tick(whole + half)
|
||||||
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,
|
impl Length {
|
||||||
// and we need it to cycle the group.
|
/// Note length to MIDI ticks
|
||||||
|
/// 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) => {
|
||||||
|
first.to_ticks() + second.to_ticks()
|
||||||
|
}
|
||||||
|
Length::Triplet(mlen) => {
|
||||||
|
let Tick(straight) = mlen.to_ticks();
|
||||||
|
let triplet = straight * 2 / 3;
|
||||||
|
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,
|
||||||
|
/// 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();
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue