mirror of
https://github.com/dredozubov/polyrhythmix.git
synced 2024-12-22 14:25:29 +00:00
WIP flattening to midi events
This commit is contained in:
parent
c642c72061
commit
8f84491801
9 changed files with 330 additions and 4 deletions
203
Cargo.lock
generated
203
Cargo.lock
generated
|
@ -2,12 +2,125 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "midly"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "207d755f4cb882d20c4da58d707ca9130a0c9bc5061f657a4f299b8e36362b7a"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
|
@ -24,9 +137,99 @@ dependencies = [
|
|||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "poly"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"midly",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
|
|
@ -11,3 +11,5 @@ path = "src/bin/main.rs"
|
|||
|
||||
[dependencies]
|
||||
nom = "*"
|
||||
midly = "0.5.3"
|
||||
derive_more = "*"
|
|
@ -1,4 +1,5 @@
|
|||
use poly::dsl;
|
||||
use poly::midi;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
|
|
|
@ -6,7 +6,7 @@ use nom::multi::many1;
|
|||
use nom::sequence::{separated_pair, tuple, delimited};
|
||||
use nom::{Err, IResult};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
|
||||
use nom::combinator::{map, map_res};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -40,7 +40,7 @@ pub enum Note {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Times(u16);
|
||||
pub struct Times(pub u16);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum GroupOrNote {
|
||||
|
@ -82,6 +82,7 @@ static ONCE : Times = Times(1);
|
|||
static TWICE: Times = Times(2);
|
||||
static THRICE : Times = Times(3);
|
||||
|
||||
|
||||
fn hit(input: &str) -> IResult<&str, Note> {
|
||||
map(char('x'), |_| { Note::Hit })(input)
|
||||
}
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
pub mod dsl;
|
||||
pub mod dsl;
|
||||
pub mod process;
|
6
src/dsl/process.rs
Normal file
6
src/dsl/process.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use super::dsl::{BasicLength};
|
||||
|
||||
pub struct TimeSignature {
|
||||
numerator: u8,
|
||||
denominator: BasicLength
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
pub mod dsl;
|
||||
pub mod dsl;
|
||||
pub mod midi;
|
110
src/midi/core.rs
Normal file
110
src/midi/core.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
extern crate derive_more;
|
||||
use derive_more::{Mul, Add};
|
||||
use midly::{Smf, Header, live::LiveEvent, MidiMessage, num::u15};
|
||||
use std::convert::{TryFrom};
|
||||
|
||||
use crate::dsl::dsl::{Group, Length, ModdedLength, BasicLength};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct FlatNote {
|
||||
// measured in ticks (128th notes), so it's easy to align to a midi grid
|
||||
length: u8
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Add, Mul)]
|
||||
#[repr(transparent)]
|
||||
pub struct Tick(pub u128);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum EventType {
|
||||
NoteOn,
|
||||
NoteOff,
|
||||
Tempo,
|
||||
Signature
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Event {
|
||||
tick: Tick,
|
||||
event_type: EventType
|
||||
}
|
||||
|
||||
// Events are supposed to be sorted.
|
||||
pub type EventGrid = Vec<Event>;
|
||||
|
||||
static TICKS_PER_QUARTER_NOTE : u16 = 48;
|
||||
|
||||
fn basic_length_to_ticks(basic_length: BasicLength) -> Tick {
|
||||
match basic_length {
|
||||
BasicLength::Whole => Tick((TICKS_PER_QUARTER_NOTE * 4) as u128),
|
||||
BasicLength::Half => Tick((TICKS_PER_QUARTER_NOTE * 2) as u128),
|
||||
BasicLength::Fourth => Tick(TICKS_PER_QUARTER_NOTE as u128),
|
||||
BasicLength::Eighth => Tick((TICKS_PER_QUARTER_NOTE / 2) 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 {
|
||||
match length {
|
||||
Length::Simple(mlen) => modded_length_to_ticks(mlen),
|
||||
Length::Tied(first, second) => modded_length_to_ticks(first) + modded_length_to_ticks(second),
|
||||
Length::Triplet(mlen) => {
|
||||
let Tick(straight) = modded_length_to_ticks(mlen);
|
||||
let triplet = straight * 2 / 3;
|
||||
Tick(triplet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flatten_group(Group { notes, length, times }: Group, start: Tick) -> EventGrid {
|
||||
let mut time = start;
|
||||
let note_length = length_to_ticks(length);
|
||||
let mut grid = Vec::new();
|
||||
let ticks = length_to_ticks(length);
|
||||
for entry in notes.iter() { |entry|
|
||||
match entry {
|
||||
crate::dsl::dsl::GroupOrNote::SingleGroup(_) => todo!(),
|
||||
crate::dsl::dsl::GroupOrNote::SingleNote(Rest) => { time = time + note_length },
|
||||
crate::dsl::dsl::GroupOrNote::SingleNote(Hit) => {
|
||||
let note_end = time + note_length;
|
||||
let note_on = Event { tick: time, event_type: EventType::NoteOn };
|
||||
let note_off = Event { tick: note_end, event_type: EventType::NoteOff };
|
||||
grid.push(note_on);
|
||||
grid.push(note_off);
|
||||
time = note_end;
|
||||
},
|
||||
};
|
||||
}
|
||||
grid.repeat(times.0 as usize)
|
||||
}
|
||||
|
||||
fn flatten_groups(groups: Vec<Group>) -> EventGrid {
|
||||
let mut out : EventGrid = Vec::new();
|
||||
groups.iter().flat_map(|Group { notes, length, times }| {
|
||||
|
||||
}).collect()
|
||||
}
|
||||
|
||||
|
||||
// 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>() -> Smf<'a> {
|
||||
let tracks = vec![]; // FIXME
|
||||
// https://majicdesigns.github.io/MD_MIDIFile/page_timing.html
|
||||
// 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.
|
||||
let metrical = midly::Timing::Metrical(u15::new(TICKS_PER_QUARTER_NOTE.clone()));
|
||||
Smf { header: Header { format: midly::Format::Parallel, timing: metrical }, tracks: tracks }
|
||||
}
|
1
src/midi/mod.rs
Normal file
1
src/midi/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod core;
|
Loading…
Reference in a new issue