mirror of
https://github.com/dredozubov/polyrhythmix.git
synced 2024-12-22 14:25:29 +00:00
Cli logic and midi event flattening
This commit is contained in:
parent
8f84491801
commit
f1dfb31b70
7 changed files with 510 additions and 79 deletions
285
Cargo.lock
generated
285
Cargo.lock
generated
|
@ -2,18 +2,127 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is-terminal",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
|
@ -73,7 +182,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -82,6 +191,33 @@ version = "1.8.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
|
@ -91,12 +227,47 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
|
@ -143,14 +314,21 @@ version = "1.15.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.2.6",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "poly"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"derive_more",
|
||||
"midly",
|
||||
"nom",
|
||||
|
@ -205,6 +383,20 @@ dependencies = [
|
|||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.37.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
|
@ -217,6 +409,12 @@ version = "1.0.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
|
@ -228,8 +426,91 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
|
||||
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"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
|
|
@ -12,4 +12,5 @@ path = "src/bin/main.rs"
|
|||
[dependencies]
|
||||
nom = "*"
|
||||
midly = "0.5.3"
|
||||
derive_more = "*"
|
||||
derive_more = "*"
|
||||
clap = { version = "4.2.7", features = ["derive"] }
|
|
@ -1,6 +1,83 @@
|
|||
use poly::dsl;
|
||||
use std::collections::HashMap;
|
||||
use std::process::exit;
|
||||
|
||||
use poly::dsl::dsl;
|
||||
use poly::midi;
|
||||
use poly::midi::core::Part;
|
||||
|
||||
use clap::*;
|
||||
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = "poly")]
|
||||
#[command(author = "Denis Redozubov <denis.redozubov@gmail.com>")]
|
||||
#[command(version = "0.1")]
|
||||
#[command(about = "Polyrhythmically-inclinded Midi Drum generator", long_about = None)]
|
||||
struct Cli {
|
||||
#[arg(short = 'k', default_value = None)]
|
||||
kick: Option<String>,
|
||||
|
||||
#[arg(short = 's', default_value = None)]
|
||||
snare: Option<String>,
|
||||
|
||||
#[arg(short = 'h', default_value = None)]
|
||||
hihat: Option<String>,
|
||||
|
||||
#[arg(short = 'c', default_value = None)]
|
||||
crash: Option<String>,
|
||||
|
||||
#[arg(short = 't', default_value = "120")]
|
||||
tempo: String,
|
||||
|
||||
#[arg(short = 'S', default_value = "4/4")]
|
||||
time_signature: String,
|
||||
|
||||
#[arg(short = 'o', default_value = None)]
|
||||
output: Option<String>
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn part_to_instrument(part: Part) -> String {
|
||||
match part {
|
||||
Part::KickDrum => String::from("Kick Drum"),
|
||||
Part::SnareDrum => String::from("Snare Drum"),
|
||||
Part::HiHat => String::from("Hi-Hat"),
|
||||
Part::CrashCymbal => String::from("Crash Cymbal"),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_and_parse_part(cli: Option<String>, part: Part, patterns: &mut HashMap<Part, Vec<dsl::Group>>) -> () {
|
||||
match cli {
|
||||
None => {},
|
||||
Some(pattern) => {
|
||||
match dsl::groups(pattern.as_str()) {
|
||||
Ok((_, group)) => { patterns.insert(part, group); },
|
||||
Err(e) => {
|
||||
println!("{} pattern is malformed.", part_to_instrument(part));
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
let matches = Cli::parse();
|
||||
let mut drum_patterns : HashMap<Part, Vec<dsl::Group>> = HashMap::new();
|
||||
match matches {
|
||||
Cli { kick, snare, hihat, crash, tempo, time_signature, output} => {
|
||||
if kick == None && snare == None && hihat == None && crash == None {
|
||||
println!("No drum pattern was supplied, exiting...");
|
||||
exit(1)
|
||||
} else if output == None {
|
||||
println!("No output file path was supplied, running a dry run...")
|
||||
} else {
|
||||
validate_and_parse_part(kick, Part::KickDrum, &mut drum_patterns);
|
||||
validate_and_parse_part(snare, Part::SnareDrum, &mut drum_patterns);
|
||||
validate_and_parse_part(hihat, Part::HiHat, &mut drum_patterns);
|
||||
validate_and_parse_part(crash, Part::CrashCymbal, &mut drum_patterns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
122
src/dsl/dsl.rs
122
src/dsl/dsl.rs
|
@ -9,7 +9,7 @@ use nom::branch::alt;
|
|||
|
||||
use nom::combinator::{map, map_res};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum BasicLength {
|
||||
Whole,
|
||||
Half,
|
||||
|
@ -20,26 +20,26 @@ pub enum BasicLength {
|
|||
SixtyFourth
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ModdedLength {
|
||||
Plain(BasicLength),
|
||||
Dotted(BasicLength)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Length {
|
||||
Simple(ModdedLength),
|
||||
Tied(ModdedLength, ModdedLength),
|
||||
Triplet(ModdedLength)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Note {
|
||||
Hit,
|
||||
Rest
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Times(pub u16);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -49,38 +49,76 @@ pub enum GroupOrNote {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Group { notes: Vec<GroupOrNote>, length: Length, times: Times }
|
||||
pub struct Group {
|
||||
pub notes: Vec<GroupOrNote>,
|
||||
pub length: Length,
|
||||
pub times: Times
|
||||
}
|
||||
|
||||
static WHOLE : Length = Length::Simple(ModdedLength::Plain(BasicLength::Whole));
|
||||
static HALF : Length = Length::Simple(ModdedLength::Plain(BasicLength::Half));
|
||||
static FOURTH : Length = Length::Simple(ModdedLength::Plain(BasicLength::Fourth));
|
||||
static EIGHTH : Length = Length::Simple(ModdedLength::Plain(BasicLength::Eighth));
|
||||
static SIXTEENTH : Length = Length::Simple(ModdedLength::Plain(BasicLength::Sixteenth));
|
||||
static THIRTY_SECOND : Length = Length::Simple(ModdedLength::Plain(BasicLength::ThirtySecond));
|
||||
static SIXTY_FOURTH : Length = Length::Simple(ModdedLength::Plain(BasicLength::SixtyFourth));
|
||||
impl std::ops::Deref for Group {
|
||||
type Target = Vec<GroupOrNote>;
|
||||
|
||||
static WHOLE_DOTTED_TRIPLET : Length = Length::Triplet(ModdedLength::Dotted(BasicLength::Whole));
|
||||
static HALF_DOTTED_TRIPLET : Length = Length::Triplet(ModdedLength::Dotted(BasicLength::Half));
|
||||
static FOURTH_DOTTED_TRIPLET : Length = Length::Triplet(ModdedLength::Dotted(BasicLength::Fourth));
|
||||
static EIGHTH_DOTTED_TRIPLET : Length = Length::Triplet(ModdedLength::Dotted(BasicLength::Eighth));
|
||||
static SIXTEENTH_DOTTED_TRIPLET : Length = Length::Triplet(ModdedLength::Dotted(BasicLength::Sixteenth));
|
||||
static THIRTY_SECOND_DOTTED_TRIPLET : Length = Length::Triplet(ModdedLength::Dotted(BasicLength::ThirtySecond));
|
||||
static SIXTY_FOURTH_DOTTED_TRIPLET : Length = Length::Triplet(ModdedLength::Dotted(BasicLength::SixtyFourth));
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.notes
|
||||
}
|
||||
}
|
||||
|
||||
static WHOLE_TRIPLET : Length = Length::Triplet(ModdedLength::Plain(BasicLength::Whole));
|
||||
static HALF_TRIPLET : Length = Length::Triplet(ModdedLength::Plain(BasicLength::Half));
|
||||
static FOURTH_TRIPLET : Length = Length::Triplet(ModdedLength::Plain(BasicLength::Fourth));
|
||||
static EIGHTH_TRIPLET : Length = Length::Triplet(ModdedLength::Plain(BasicLength::Eighth));
|
||||
static SIXTEENTH_TRIPLET : Length = Length::Triplet(ModdedLength::Plain(BasicLength::Sixteenth));
|
||||
static THIRTY_SECOND_TRIPLET : Length = Length::Triplet(ModdedLength::Plain(BasicLength::ThirtySecond));
|
||||
static SIXTY_FOURTH_TRIPLET : Length = Length::Triplet(ModdedLength::Plain(BasicLength::SixtyFourth));
|
||||
#[allow(dead_code)]
|
||||
static WHOLE : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Whole));
|
||||
#[allow(dead_code)]
|
||||
static HALF : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Half));
|
||||
#[allow(dead_code)]
|
||||
static FOURTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Fourth));
|
||||
#[allow(dead_code)]
|
||||
static EIGHTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Eighth));
|
||||
#[allow(dead_code)]
|
||||
static SIXTEENTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::Sixteenth));
|
||||
#[allow(dead_code)]
|
||||
static THIRTY_SECOND : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::ThirtySecond));
|
||||
#[allow(dead_code)]
|
||||
static SIXTY_FOURTH : &Length = &Length::Simple(ModdedLength::Plain(BasicLength::SixtyFourth));
|
||||
|
||||
#[allow(dead_code)]
|
||||
static WHOLE_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Whole));
|
||||
#[allow(dead_code)]
|
||||
static HALF_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Half));
|
||||
#[allow(dead_code)]
|
||||
static FOURTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Fourth));
|
||||
#[allow(dead_code)]
|
||||
static EIGHTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Eighth));
|
||||
#[allow(dead_code)]
|
||||
static SIXTEENTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::Sixteenth));
|
||||
#[allow(dead_code)]
|
||||
static THIRTY_SECOND_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::ThirtySecond));
|
||||
#[allow(dead_code)]
|
||||
static SIXTY_FOURTH_DOTTED_TRIPLET : &Length = &Length::Triplet(ModdedLength::Dotted(BasicLength::SixtyFourth));
|
||||
|
||||
#[allow(dead_code)]
|
||||
static WHOLE_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Whole));
|
||||
#[allow(dead_code)]
|
||||
static HALF_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Half));
|
||||
#[allow(dead_code)]
|
||||
static FOURTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Fourth));
|
||||
#[allow(dead_code)]
|
||||
static EIGHTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Eighth));
|
||||
#[allow(dead_code)]
|
||||
static SIXTEENTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::Sixteenth));
|
||||
#[allow(dead_code)]
|
||||
static THIRTY_SECOND_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::ThirtySecond));
|
||||
#[allow(dead_code)]
|
||||
static SIXTY_FOURTH_TRIPLET : &Length = &Length::Triplet(ModdedLength::Plain(BasicLength::SixtyFourth));
|
||||
|
||||
#[allow(dead_code)]
|
||||
static HIT : GroupOrNote = GroupOrNote::SingleNote(Note::Hit);
|
||||
#[allow(dead_code)]
|
||||
static REST : GroupOrNote = GroupOrNote::SingleNote(Note::Rest);
|
||||
|
||||
static ONCE : Times = Times(1);
|
||||
static TWICE: Times = Times(2);
|
||||
static THRICE : Times = Times(3);
|
||||
#[allow(dead_code)]
|
||||
static ONCE : &Times = &Times(1);
|
||||
#[allow(dead_code)]
|
||||
static TWICE: &Times = &Times(2);
|
||||
#[allow(dead_code)]
|
||||
static THRICE : &Times = &Times(3);
|
||||
|
||||
|
||||
fn hit(input: &str) -> IResult<&str, Note> {
|
||||
|
@ -104,7 +142,7 @@ fn length_basic(input: &str) -> IResult<&str, BasicLength> {
|
|||
Ok((r,16)) => Ok((r, BasicLength::Sixteenth)),
|
||||
Ok((r,32)) => Ok((r, BasicLength::ThirtySecond)),
|
||||
Ok((r, 64)) => Ok((r, BasicLength::SixtyFourth)),
|
||||
Ok((r, i)) => {
|
||||
Ok((r, _)) => {
|
||||
Err(Err::Error(nom::error::make_error(r, nom::error::ErrorKind::Fail)))
|
||||
},
|
||||
Err(e) => Err(e)
|
||||
|
@ -150,35 +188,35 @@ fn group_or_delimited_group(input: &str) -> IResult<&str, Group> {
|
|||
alt((delimited_group, group))(input)
|
||||
}
|
||||
|
||||
fn groups(input: &str) -> IResult<&str, Vec<Group>> {
|
||||
pub fn groups(input: &str) -> IResult<&str, Vec<Group>> {
|
||||
many1(group_or_delimited_group)(input)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_length() {
|
||||
assert_eq!(length("16"), Ok(("", SIXTEENTH.clone())));
|
||||
assert_eq!(length("16"), Ok(("", *SIXTEENTH)));
|
||||
assert_eq!(length("8+16"), Ok(("", Length::Tied(ModdedLength::Plain(BasicLength::Eighth), ModdedLength::Plain(BasicLength::Sixteenth)))));
|
||||
assert_eq!(length("8t"), Ok(("", EIGHTH_TRIPLET.clone())));
|
||||
assert_eq!(length("4.t"), Ok(("", FOURTH_DOTTED_TRIPLET.clone())));
|
||||
assert_eq!(length("8t"), Ok(("", *EIGHTH_TRIPLET)));
|
||||
assert_eq!(length("4.t"), Ok(("", *FOURTH_DOTTED_TRIPLET)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_group() {
|
||||
assert_eq!(group("16x--x-"), Ok(("", Group { times: ONCE.clone(), notes: vec![HIT.clone(), REST.clone(), REST.clone(), HIT.clone(), REST.clone()], length: SIXTEENTH.clone()})));
|
||||
assert_eq!(group("8txxx"), Ok(("", Group { times: ONCE.clone(), notes: vec![HIT.clone(), HIT.clone(), HIT.clone()], length: EIGHTH_TRIPLET.clone()})));
|
||||
assert_eq!(group("16+32x-xx"), Ok(("", Group { times: ONCE.clone(), notes: vec![HIT.clone(), REST.clone(), HIT.clone(), HIT.clone()], length: Length::Tied(ModdedLength::Plain(BasicLength::Sixteenth), ModdedLength::Plain(BasicLength::ThirtySecond))})));
|
||||
assert_eq!(group("3,16xx"), Ok(("", Group { times: THRICE.clone(), length: SIXTEENTH.clone(), notes: vec![HIT.clone(), HIT.clone()] })));
|
||||
assert_eq!(group("16x--x-"), Ok(("", Group { times: *ONCE, notes: vec![HIT.clone(), REST.clone(), REST.clone(), HIT.clone(), REST.clone()], length: *SIXTEENTH})));
|
||||
assert_eq!(group("8txxx"), Ok(("", Group { times: *ONCE, notes: vec![HIT.clone(), HIT.clone(), HIT.clone()], length: *EIGHTH_TRIPLET})));
|
||||
assert_eq!(group("16+32x-xx"), Ok(("", Group { times: *ONCE, notes: vec![HIT.clone(), REST.clone(), HIT.clone(), HIT.clone()], length: Length::Tied(ModdedLength::Plain(BasicLength::Sixteenth), ModdedLength::Plain(BasicLength::ThirtySecond))})));
|
||||
assert_eq!(group("3,16xx"), Ok(("", Group { times: *THRICE, length: *SIXTEENTH, notes: vec![HIT.clone(), HIT.clone()] })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_delimited_group() {
|
||||
assert_eq!(delimited_group("(3,16x--x-)"), Ok(("", Group { times: THRICE.clone(), notes: vec![HIT.clone(), REST.clone(), REST.clone(), HIT.clone(), REST.clone()], length: SIXTEENTH.clone()})));
|
||||
assert_eq!(delimited_group("(3,16x--x-)"), Ok(("", Group { times: *THRICE, notes: vec![HIT.clone(), REST.clone(), REST.clone(), HIT.clone(), REST.clone()], length: *SIXTEENTH})));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_group_or_delimited_group() {
|
||||
assert_eq!(group_or_delimited_group("(3,16x--x-)"), Ok(("", Group { times: THRICE.clone(), notes: vec![HIT.clone(), REST.clone(), REST.clone(), HIT.clone(), REST.clone()], length: SIXTEENTH.clone()})));
|
||||
assert_eq!(group_or_delimited_group("16x--x-"), Ok(("", Group { times: ONCE.clone(), notes: vec![HIT.clone(), REST.clone(), REST.clone(), HIT.clone(), REST.clone()], length: SIXTEENTH.clone()})));
|
||||
assert_eq!(group_or_delimited_group("(3,16x--x-)"), Ok(("", Group { times: *THRICE, notes: vec![HIT.clone(), REST.clone(), REST.clone(), HIT.clone(), REST.clone()], length: *SIXTEENTH})));
|
||||
assert_eq!(group_or_delimited_group("16x--x-"), Ok(("", Group { times: *ONCE, notes: vec![HIT.clone(), REST.clone(), REST.clone(), HIT.clone(), REST.clone()], length: *SIXTEENTH})));
|
||||
}
|
||||
|
||||
// “x” hit
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
pub mod dsl;
|
||||
pub mod process;
|
||||
pub mod dsl;
|
|
@ -1,6 +0,0 @@
|
|||
use super::dsl::{BasicLength};
|
||||
|
||||
pub struct TimeSignature {
|
||||
numerator: u8,
|
||||
denominator: BasicLength
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
extern crate derive_more;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use derive_more::{Mul, Add};
|
||||
use midly::{Smf, Header, live::LiveEvent, MidiMessage, num::u15};
|
||||
use std::convert::{TryFrom};
|
||||
use midly::{Smf, Header, live::LiveEvent, MidiMessage, num::u15, Track};
|
||||
|
||||
use crate::dsl::dsl::{Group, Length, ModdedLength, BasicLength};
|
||||
|
||||
|
@ -17,10 +18,25 @@ pub struct Tick(pub u128);
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum EventType {
|
||||
NoteOn,
|
||||
NoteOff,
|
||||
Tempo,
|
||||
Signature
|
||||
NoteOn(Part),
|
||||
NoteOff(Part),
|
||||
Tempo(u8),
|
||||
Signature(TimeSignature)
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct TimeSignature {
|
||||
pub numerator: u8,
|
||||
pub denominator: BasicLength
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
|
||||
pub enum Part {
|
||||
KickDrum,
|
||||
SnareDrum,
|
||||
HiHat,
|
||||
CrashCymbal
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
@ -32,6 +48,7 @@ pub struct Event {
|
|||
// Events are supposed to be sorted.
|
||||
pub type EventGrid = Vec<Event>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
static TICKS_PER_QUARTER_NOTE : u16 = 48;
|
||||
|
||||
fn basic_length_to_ticks(basic_length: BasicLength) -> Tick {
|
||||
|
@ -69,42 +86,66 @@ fn length_to_ticks(length: Length) -> Tick {
|
|||
}
|
||||
}
|
||||
|
||||
fn flatten_group(Group { notes, length, times }: Group, start: Tick) -> EventGrid {
|
||||
fn flatten_group(Group { notes, length, times }: &Group, part: Part, start: &mut Tick, grid: &mut EventGrid) -> EventGrid {
|
||||
let mut time = start;
|
||||
let note_length = length_to_ticks(length);
|
||||
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::SingleGroup(group) => {
|
||||
flatten_group(&group, part, time , &mut grid).repeat(times.0 as usize);
|
||||
},
|
||||
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 };
|
||||
let note_end = *time + note_length;
|
||||
let note_on = Event { tick: *time, event_type: EventType::NoteOn(part) };
|
||||
let note_off = Event { tick: note_end, event_type: EventType::NoteOff(part) };
|
||||
grid.push(note_on);
|
||||
grid.push(note_off);
|
||||
time = note_end;
|
||||
*time = note_end;
|
||||
},
|
||||
};
|
||||
}
|
||||
grid.repeat(times.0 as usize)
|
||||
grid
|
||||
}
|
||||
|
||||
fn flatten_groups(groups: Vec<Group>) -> EventGrid {
|
||||
let mut out : EventGrid = Vec::new();
|
||||
groups.iter().flat_map(|Group { notes, length, times }| {
|
||||
|
||||
}).collect()
|
||||
fn flatten_groups(part: Part, groups: Vec<Group>) -> EventGrid {
|
||||
let mut time : Tick = Tick(0);
|
||||
let mut grid : EventGrid = Vec::new();
|
||||
groups.iter().for_each(|group| {
|
||||
flatten_group(group, part, &mut time, &mut grid);
|
||||
});
|
||||
grid
|
||||
}
|
||||
|
||||
fn combine_event_grids<'a>(a: &'a mut EventGrid, b : &'a mut EventGrid) -> &'a mut EventGrid {
|
||||
a.append(b);
|
||||
a.sort_by(|e1, e2| { e1.tick.cmp(&e2.tick)} );
|
||||
a
|
||||
}
|
||||
|
||||
fn merge_event_grids(mut eg: Vec<EventGrid>) -> EventGrid {
|
||||
let first = eg.pop().unwrap();
|
||||
eg.iter_mut().fold(first, |mut acc, next| {
|
||||
combine_event_grids(&mut acc, next);
|
||||
acc
|
||||
})
|
||||
}
|
||||
|
||||
fn flatten_and_merge(groups: HashMap<Part, Vec<Group>>) -> EventGrid {
|
||||
let mut eg = Vec::new();
|
||||
for (part, group) in groups {
|
||||
eg.push(flatten_groups(part, group))
|
||||
}
|
||||
merge_event_grids(eg)
|
||||
}
|
||||
|
||||
// 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
|
||||
fn create_smf<'a>(groups: HashMap<Part, Vec<Group>>) -> Smf<'a> {
|
||||
let tracks = vec![]; //create_tracks(groups); // 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()));
|
||||
let metrical = midly::Timing::Metrical(u15::new(TICKS_PER_QUARTER_NOTE));
|
||||
Smf { header: Header { format: midly::Format::Parallel, timing: metrical }, tracks: tracks }
|
||||
}
|
Loading…
Reference in a new issue