Compare commits

...

2 Commits

Author SHA1 Message Date
Bel LaPointe
beb6595f42 reststs AND rustfmt changed whitespace 2026-03-11 15:32:22 -06:00
Bel LaPointe
a2a5465fb0 rustfmt 2026-03-11 15:32:02 -06:00
4 changed files with 286 additions and 264 deletions

View File

@@ -1,9 +1,9 @@
use itertools::Itertools; use itertools::Itertools;
mod flags; mod flags;
mod seq;
mod syn; mod syn;
mod tone; mod tone;
mod seq;
fn main() { fn main() {
let flags = flags::Flags::new(); let flags = flags::Flags::new();
@@ -15,7 +15,7 @@ fn main() {
)); ));
syn_seq.append(1, "3c1"); syn_seq.append(1, "3c1");
if let Some(play) = flags.play { if let Some(play) = flags.play {
syn_seq.append(0, play); syn_seq.append(0, play);
} }
play(syn_seq, flags.sample_rate, flags.bpm); play(syn_seq, flags.sample_rate, flags.bpm);

View File

@@ -1,158 +1,160 @@
use crate::tone;
use crate::syn; use crate::syn;
use crate::tone;
pub struct SynSeq { pub struct SynSeq {
seqs: std::collections::HashMap<i32, Seq>, seqs: std::collections::HashMap<i32, Seq>,
syn: syn::Syn, syn: syn::Syn,
} }
pub fn new_syn(syn: syn::Syn) -> SynSeq { pub fn new_syn(syn: syn::Syn) -> SynSeq {
SynSeq{ SynSeq {
seqs: std::collections::HashMap::new(), seqs: std::collections::HashMap::new(),
syn: syn, syn: syn,
} }
} }
impl SynSeq { impl SynSeq {
pub fn append<S: ToString>(&mut self, ch: i32, s: S) { pub fn append<S: ToString>(&mut self, ch: i32, s: S) {
match self.seqs.get_mut(&ch) { match self.seqs.get_mut(&ch) {
Some(seq) => seq.append(s), Some(seq) => seq.append(s),
None => { None => {
let mut seq = new(); let mut seq = new();
seq.append(s); seq.append(s);
self.seqs.insert(ch, seq); self.seqs.insert(ch, seq);
}, }
}; };
} }
pub fn render(&mut self, left: &mut [f32], right: &mut [f32]) { pub fn render(&mut self, left: &mut [f32], right: &mut [f32]) {
for (ch, seq) in self.seqs.iter_mut() { for (ch, seq) in self.seqs.iter_mut() {
let ch = ch.clone(); let ch = ch.clone();
match seq.pop() { match seq.pop() {
(Some(tone), changed) if changed => { (Some(tone), changed) if changed => self.syn.set(ch, Some(tone.clone())),
self.syn.set(ch, Some(tone.clone())) (None, changed) if changed => {
}, self.syn.set(ch, None);
(None, changed) if changed => { }
self.syn.set(ch, None); _ => {}
}, };
_ => {}, }
}; self.syn.render(left, right);
} }
self.syn.render(left, right);
}
pub fn beats(&self) -> usize { pub fn beats(&self) -> usize {
let mut longest = 0 as usize; let mut longest = 0 as usize;
for (_, seq) in self.seqs.iter() { for (_, seq) in self.seqs.iter() {
longest = if seq.len() > longest { longest = if seq.len() > longest {
seq.len() seq.len()
} else { } else {
longest longest
}; };
} }
longest longest
} }
} }
#[derive(PartialEq)] #[derive(PartialEq)]
pub struct Seq { pub struct Seq {
beats: Vec<(usize, tone::Tone)>, beats: Vec<(usize, tone::Tone)>,
state: Option<tone::Tone>, state: Option<tone::Tone>,
} }
fn new() -> Seq { fn new() -> Seq {
Seq::new() Seq::new()
} }
impl Seq { impl Seq {
fn new() -> Seq { fn new() -> Seq {
Seq{ Seq {
beats: vec![], beats: vec![],
state: None, state: None,
} }
} }
fn len(&self) -> usize { fn len(&self) -> usize {
let mut sum = 0 as usize; let mut sum = 0 as usize;
for beat in self.beats.iter() { for beat in self.beats.iter() {
sum += beat.0 as usize; sum += beat.0 as usize;
} }
sum sum
} }
fn pop(&mut self) -> (Option<tone::Tone>, bool) { fn pop(&mut self) -> (Option<tone::Tone>, bool) {
let state_before = self.state.clone(); let state_before = self.state.clone();
let tone_after = self._pop(); let tone_after = self._pop();
if state_before != tone_after { if state_before != tone_after {
self.state = tone_after.clone(); self.state = tone_after.clone();
} }
(tone_after, self.state != state_before) (tone_after, self.state != state_before)
} }
fn _pop(&mut self) -> Option<tone::Tone> { fn _pop(&mut self) -> Option<tone::Tone> {
match self.beats.len() { match self.beats.len() {
0 => None, 0 => None,
_ => match self.beats[0].0 { _ => match self.beats[0].0 {
1 => Some(self.beats.remove(0).1), 1 => Some(self.beats.remove(0).1),
_ => { _ => {
self.beats[0].0 -= 1; self.beats[0].0 -= 1;
Some(self.beats[0].1.clone()) Some(self.beats[0].1.clone())
}
}, },
}, }
} }
}
fn append<S: ToString>(&mut self, s: S) { fn append<S: ToString>(&mut self, s: S) {
let s: String = s.to_string(); let s: String = s.to_string();
let s: &str = s.as_ref(); let s: &str = s.as_ref();
for split in s.split_whitespace() { for split in s.split_whitespace() {
self.append_one(split.to_string()); self.append_one(split.to_string());
} }
} }
fn append_one(&mut self, s: String) { fn append_one(&mut self, s: String) {
let re = regex::Regex::new(r"^(?<count>[0-9]*)(?<tone>[a-z].*)$").unwrap(); let re = regex::Regex::new(r"^(?<count>[0-9]*)(?<tone>.*)$").unwrap();
let captures = re.captures(&s).unwrap(); let captures = re.captures(&s).unwrap();
let n = match captures.name("count") { let n = match captures.name("count") {
Some(number) if number.as_str().len() > 0 => number.as_str().parse::<usize>().unwrap(), Some(number) if number.as_str().len() > 0 => number.as_str().parse::<usize>().unwrap(),
_ => 1, _ => 1,
} as usize; } as usize;
let tone = tone::new(captures.name("tone").unwrap().as_str()); let tone = tone::new(captures.name("tone").unwrap().as_str());
self.beats.push((n, tone)); self.beats.push((n, tone));
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
#[test] #[test]
fn test_seq() { fn test_seq() {
let mut seq = new(); let mut seq = new();
seq.append("c"); seq.append("c");
seq.append("4d"); seq.append("4d");
seq.append("g 2e"); seq.append("2-");
seq.append("g 2e");
assert_eq!(seq.beats.len(), 4); assert_eq!(seq.beats.len(), 5);
assert_eq!(seq.len(), 8); assert_eq!(seq.len(), 10);
assert_eq!(seq.beats[0], (1 as usize, tone::new("c"))); assert_eq!(seq.beats[0], (1 as usize, tone::new("c")));
assert_eq!(seq.beats[1], (4 as usize, tone::new("d"))); assert_eq!(seq.beats[1], (4 as usize, tone::new("d")));
assert_eq!(seq.beats[2], (1 as usize, tone::new("g"))); assert_eq!(seq.beats[2], (2 as usize, tone::new("!")));
assert_eq!(seq.beats[3], (2 as usize, tone::new("e"))); assert_eq!(seq.beats[3], (1 as usize, tone::new("g")));
assert_eq!(seq.beats[4], (2 as usize, tone::new("e")));
assert_eq!(seq.pop(), (Some(tone::new("c")), true)); assert_eq!(seq.pop(), (Some(tone::new("c")), true));
assert_eq!(seq.pop(), (Some(tone::new("d")), true)); assert_eq!(seq.pop(), (Some(tone::new("d")), true));
for _ in 1..4 { for _ in 1..4 {
assert_eq!(seq.pop(), (Some(tone::new("d")), false)); assert_eq!(seq.pop(), (Some(tone::new("d")), false));
} }
assert_eq!(seq.pop(), (Some(tone::new("g")), true)); assert_eq!(seq.pop(), (Some(tone::new(".")), true));
assert_eq!(seq.pop(), (Some(tone::new("e")), true)); assert_eq!(seq.pop(), (Some(tone::new("?")), false));
assert_eq!(seq.pop(), (Some(tone::new("e")), false)); assert_eq!(seq.pop(), (Some(tone::new("g")), true));
assert_eq!(seq.pop(), (None, true)); assert_eq!(seq.pop(), (Some(tone::new("e")), true));
assert_eq!(seq.pop(), (None, false)); assert_eq!(seq.pop(), (Some(tone::new("e")), false));
} assert_eq!(seq.pop(), (None, true));
assert_eq!(seq.pop(), (None, false));
}
} }

View File

@@ -1,123 +1,129 @@
use rustysynth::Synthesizer;
use rustysynth::SoundFont; use rustysynth::SoundFont;
use rustysynth::Synthesizer;
use rustysynth::SynthesizerSettings; use rustysynth::SynthesizerSettings;
use std::sync::Arc; use std::sync::Arc;
use crate::tone; use crate::tone;
pub enum Syn { pub enum Syn {
Real(Synthesizer), Real(Synthesizer),
Text{ Text {
m: std::collections::HashMap<i32, std::collections::HashMap<i32, i32>>, m: std::collections::HashMap<i32, std::collections::HashMap<i32, i32>>,
i: u32, i: u32,
}, },
} }
impl Syn { impl Syn {
pub fn new(debug: bool, sound_font: String, sample_rate: usize) -> Syn { pub fn new(debug: bool, sound_font: String, sample_rate: usize) -> Syn {
match debug { match debug {
false => Syn::new_real(sound_font, sample_rate), false => Syn::new_real(sound_font, sample_rate),
true => Syn::Text{m: std::collections::HashMap::new(), i: 0}, true => Syn::Text {
} m: std::collections::HashMap::new(),
} i: 0,
},
}
}
fn new_real(sound_font: String, sample_rate: usize) -> Syn { fn new_real(sound_font: String, sample_rate: usize) -> Syn {
let mut sf2 = std::fs::File::open(sound_font).unwrap(); let mut sf2 = std::fs::File::open(sound_font).unwrap();
let sound_font = Arc::new(SoundFont::new(&mut sf2).unwrap()); let sound_font = Arc::new(SoundFont::new(&mut sf2).unwrap());
let settings = SynthesizerSettings::new(sample_rate as i32); let settings = SynthesizerSettings::new(sample_rate as i32);
let synthesizer = Synthesizer::new(&sound_font, &settings).unwrap(); let synthesizer = Synthesizer::new(&sound_font, &settings).unwrap();
Syn::Real(synthesizer) Syn::Real(synthesizer)
} }
pub fn set(&mut self, ch: i32, b: Option<tone::Tone>) { pub fn set(&mut self, ch: i32, b: Option<tone::Tone>) {
match self { match self {
// channel=[0..16) // channel=[0..16)
// velocity=[0..128) // velocity=[0..128)
Syn::Real(syn) => { Syn::Real(syn) => {
syn.note_off_all_channel(ch, false); syn.note_off_all_channel(ch, false);
}, }
Syn::Text{m, ..} => { Syn::Text { m, .. } => {
m.remove(&ch); m.remove(&ch);
}, }
}; };
if let Some(tone) = b { if let Some(tone) = b {
self.tone_on(ch, tone); self.tone_on(ch, tone);
}; };
} }
fn tone_on(&mut self, ch: i32, b: tone::Tone) { fn tone_on(&mut self, ch: i32, b: tone::Tone) {
match self { match self {
// channel=[0..16) // channel=[0..16)
// velocity=[0..128) // velocity=[0..128)
Syn::Real(syn) => syn.note_on(ch, b.i32(), 127), Syn::Real(syn) => syn.note_on(ch, b.i32(), 127),
Syn::Text{m, ..} => { Syn::Text { m, .. } => {
match m.get_mut(&ch) { match m.get_mut(&ch) {
Some(m2) => { m2.insert(b.i32(), 127); }, Some(m2) => {
None => { m2.insert(b.i32(), 127);
let mut m2 = std::collections::HashMap::new(); }
m2.insert(b.i32(), 127); None => {
m.insert(ch, m2); let mut m2 = std::collections::HashMap::new();
}, m2.insert(b.i32(), 127);
}; m.insert(ch, m2);
}, }
}; };
} }
};
}
fn tone_off(&mut self, ch: i32, b: tone::Tone) { fn tone_off(&mut self, ch: i32, b: tone::Tone) {
match self { match self {
Syn::Real(syn) => syn.note_off(ch, b.i32()), Syn::Real(syn) => syn.note_off(ch, b.i32()),
Syn::Text{m, ..} => { Syn::Text { m, .. } => {
match m.get_mut(&ch) { match m.get_mut(&ch) {
Some(m) => { m.remove(&b.i32()); }, Some(m) => {
None => {}, m.remove(&b.i32());
}; }
None => {}
};
}
};
}
}, pub fn render(&mut self, a: &mut [f32], b: &mut [f32]) {
}; match self {
} Syn::Real(syn) => syn.render(a, b),
Syn::Text { m, i } => {
pub fn render(&mut self, a: &mut [f32], b: &mut [f32]) { eprintln!("{} | render[{}]({:?})", chrono::prelude::Utc::now(), i, m);
match self { *i += 1;
Syn::Real(syn) => syn.render(a, b), }
Syn::Text{m, i} => { };
eprintln!("{} | render[{}]({:?})", chrono::prelude::Utc::now(), i, m); }
*i += 1;
},
};
}
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
#[test] #[test]
fn test_new_real() { fn test_new_real() {
let mut syn = Syn::new(false, "super_small_font.sf2".to_string(), 44100); let mut syn = Syn::new(false, "super_small_font.sf2".to_string(), 44100);
syn.tone_on(0, tone::new("c")); syn.tone_on(0, tone::new("c"));
syn.tone_on(0, tone::new("d")); syn.tone_on(0, tone::new("d"));
syn.tone_off(0, tone::new("d")); syn.tone_off(0, tone::new("d"));
let mut buffer1 = Vec::<f32>::new(); let mut buffer1 = Vec::<f32>::new();
let mut buffer2 = Vec::<f32>::new(); let mut buffer2 = Vec::<f32>::new();
syn.render(&mut buffer1, &mut buffer2); syn.render(&mut buffer1, &mut buffer2);
} }
#[test] #[test]
fn test_text() { fn test_text() {
let mut syn = Syn::new(true, ".sf2".to_string(), 1); let mut syn = Syn::new(true, ".sf2".to_string(), 1);
syn.tone_on(0, tone::new("c")); syn.tone_on(0, tone::new("c"));
syn.tone_on(0, tone::new("d")); syn.tone_on(0, tone::new("d"));
syn.tone_off(0, tone::new("d")); syn.tone_off(0, tone::new("d"));
let mut buffer1 = Vec::<f32>::new(); let mut buffer1 = Vec::<f32>::new();
let mut buffer2 = Vec::<f32>::new(); let mut buffer2 = Vec::<f32>::new();
syn.render(&mut buffer1, &mut buffer2); syn.render(&mut buffer1, &mut buffer2);
} }
} }

View File

@@ -3,69 +3,83 @@ pub struct Tone(i32);
// new parses [a-g][+-]?[1-5]? to tone, sharp or flat, down 2 octave..up 1 octave // new parses [a-g][+-]?[1-5]? to tone, sharp or flat, down 2 octave..up 1 octave
pub fn new<S: ToString>(s: S) -> Tone { pub fn new<S: ToString>(s: S) -> Tone {
Tone::new(s.to_string()) Tone::new(s.to_string())
} }
impl Tone { impl Tone {
fn new(s: String) -> Tone { fn new(s: String) -> Tone {
let re = regex::Regex::new(r"^((?<letter>^[a-g])(?<sharpness>[+-]?)(?<octave>[1-5]?)|(?<numeric>[0-9]+))$").unwrap(); let re = regex::Regex::new(r"^((?<letter>^[a-g])(?<sharpness>[+-]?)(?<octave>[1-5]?)|(?<numeric>[0-9]+)|(?<rest>[^a-zA-Z0-9]))$").unwrap();
let captures = re.captures(s.as_ref()).unwrap(); let captures = re
Tone (match captures.name("numeric") { .captures(&s)
Some(number) => number.as_str().parse::<i32>().unwrap(), .expect(format!("tone '{}' does not match regex", s).as_ref());
None => { Tone(match captures.name("numeric") {
let mut result = match captures.name("letter").unwrap().as_str() { Some(number) => number.as_str().parse::<i32>().unwrap(),
"a" => 57, None => match captures.name("rest") {
"b" => 59, Some(_) => 0,
"c" => 60, _ => {
"d" => 62, let mut result = match captures.name("letter").unwrap().as_str() {
"e" => 64, "a" => 57,
"f" => 65, "b" => 59,
_ => 67, "c" => 60,
} as i32; "d" => 62,
"e" => 64,
"f" => 65,
_ => 67,
} as i32;
result += match captures.name("sharpness") { result += match captures.name("sharpness") {
Some(sharpness) => match sharpness.as_str() { Some(sharpness) => match sharpness.as_str() {
"+" => 1, "+" => 1,
"-" => -1, "-" => -1,
_ => 0, _ => 0,
}, },
None => 0, None => 0,
} as i32; } as i32;
result += match captures.name("octave") { result += match captures.name("octave") {
Some(octave) => match octave.as_str() { Some(octave) => match octave.as_str() {
"" => 0, "" => 0,
_ => (octave.as_str().parse::<i32>().unwrap() - 3) * 12, _ => (octave.as_str().parse::<i32>().unwrap() - 3) * 12,
}, },
None => 0, None => 0,
} as i32; } as i32;
result result
}, }
}) },
} })
}
pub fn i32(&self) -> i32 { pub fn i32(&self) -> i32 {
self.0 self.0
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
#[test] #[test]
fn test_tone_new() { fn test_tone_new() {
assert_eq!(super::new("60").i32(), 60); eprintln!("numeric");
assert_eq!(super::new("60").i32(), 60);
assert_eq!(super::new("c").i32(), 60); eprintln!("rests");
assert_eq!(super::new("e").i32(), 64); assert_eq!(super::new("-").i32(), 0);
assert_eq!(super::new("g").i32(), 67); assert_eq!(super::new(".").i32(), 0);
assert_eq!(super::new("c+").i32(), 60+1); eprintln!("alpha");
assert_eq!(super::new("c-").i32(), 60-1); 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("c3").i32(), 60); eprintln!("alpha mod");
assert_eq!(super::new("c4").i32(), 60+12); assert_eq!(super::new("c+").i32(), 60 + 1);
assert_eq!(super::new("c-").i32(), 60 - 1);
assert_eq!(super::new("c+4").i32(), 60+12+1); eprintln!("alpha octave");
} assert_eq!(super::new("c3").i32(), 60);
assert_eq!(super::new("c4").i32(), 60 + 12);
eprintln!("alpha mod octave");
assert_eq!(super::new("c+4").i32(), 60 + 12 + 1);
}
} }