diff --git a/Cargo.lock b/Cargo.lock index 19ac521..600f17d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,6 +205,7 @@ dependencies = [ "env_logger", "itertools 0.14.0", "midir", + "regex", "rustysynth", "tinyaudio", ] diff --git a/Cargo.toml b/Cargo.toml index aa438d5..594473e 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,5 +8,6 @@ clap = { version = "4.5.60", features = ["derive"] } env_logger = "0.11.9" itertools = "0.14.0" midir = "0.10.3" +regex = "1.12.3" rustysynth = "1.3.6" tinyaudio = "2.0.0" diff --git a/src/main.rs b/src/main.rs index 9b0ab00..0e4b7b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use itertools::Itertools; mod flags; mod syn; -mod note; +mod tone; fn main() { let flags = flags::Flags::new(); @@ -12,8 +12,8 @@ fn main() { flags.sound_font, flags.sample_rate, ); - // Play some notes (middle C, E, G). // 16 channels actually // 60=c 64=e 67=g //up to 128velocity though dont go below 50 tbh // 12 notes per octave - syn.note_on(0, note::new("any").i32(), 127); + // Play some tones (middle C, E, G). // 16 channels actually // 60=c 64=e 67=g //up to 128velocity though dont go below 50 tbh // 12 tones per octave + syn.note_on(0, tone::new("c").i32(), 127); play(syn, flags.sample_rate, flags.bpm, flags.smallest_note); } diff --git a/src/note.rs b/src/note.rs deleted file mode 100644 index e91c372..0000000 --- a/src/note.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub struct Note(String); - -pub fn new(s: S) -> Note { - Note(s.to_string()) -} - -impl Note { - pub fn i32(&self) -> i32 { - // 60 = middle c - // 64 = middle e - // 67 = middle g - 64 + 12 - } -} diff --git a/src/tone.rs b/src/tone.rs new file mode 100644 index 0000000..9fb18a6 --- /dev/null +++ b/src/tone.rs @@ -0,0 +1,71 @@ +#[derive(Debug)] +pub struct Tone(i32); + +// new parses [a-g][+-]?[1-5]? to tone, sharp or flat, down 2 octave..up 1 octave +pub fn new(s: S) -> Tone { + Tone::new(s.to_string()) +} + +impl Tone { + fn new(s: String) -> Tone { + let re = regex::Regex::new(r"^((?^[a-g])(?[+-]?)(?[1-5]?)|(?[0-9]+))$").unwrap(); + let captures = re.captures(s.as_ref()).unwrap(); + Tone (match captures.name("numeric") { + Some(number) => number.as_str().parse::().unwrap(), + None => { + let mut result = match captures.name("letter").unwrap().as_str() { + "a" => 57, + "b" => 59, + "c" => 60, + "d" => 62, + "e" => 64, + "f" => 65, + _ => 67, + } as i32; + + result += match captures.name("sharpness") { + Some(sharpness) => match sharpness.as_str() { + "+" => 1, + "-" => -1, + _ => 0, + }, + None => 0, + } as i32; + + result += match captures.name("octave") { + Some(octave) => match octave.as_str() { + "" => 0, + _ => (octave.as_str().parse::().unwrap() - 3) * 12, + }, + None => 0, + } as i32; + + result + }, + }) + } + + pub fn i32(&self) -> i32 { + self.0 + } +} + +#[cfg(test)] +mod test { + #[test] + fn test_tone_new() { + assert_eq!(super::new("60").i32(), 60); + + assert_eq!(super::new("c").i32(), 60); + assert_eq!(super::new("e").i32(), 64); + assert_eq!(super::new("g").i32(), 67); + + assert_eq!(super::new("c+").i32(), 60+1); + assert_eq!(super::new("c-").i32(), 60-1); + + assert_eq!(super::new("c3").i32(), 60); + assert_eq!(super::new("c4").i32(), 60+12); + + assert_eq!(super::new("c+4").i32(), 60+12+1); + } +}