From e9fcec8b320ea7590b5f53c8bc85294cc4714523 Mon Sep 17 00:00:00 2001 From: brevalferrari Date: Thu, 5 Jun 2025 01:39:05 +0200 Subject: [PATCH] faster --- src/cli/main.rs | 18 ++-- src/compiler.rs | 213 ++++++++++++++++++++++++++++-------------------- 2 files changed, 135 insertions(+), 96 deletions(-) diff --git a/src/cli/main.rs b/src/cli/main.rs index 470988f..b694a63 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -25,13 +25,13 @@ fn main() -> anyhow::Result<()> { let sink = Sink::try_new(&stream_handle).context("Epic audio playback failure")?; debug!("audio sink acquired"); - let default_variables = HashMap::from([ + let default_variables = [ ('l', 4f64), ('L', 0.0), ('t', 0.0), ('T', 60.0), ('N', opts.notes().len() as f64), - ]); + ]; debug!("building parser"); let parser = Parser::new( @@ -40,7 +40,7 @@ fn main() -> anyhow::Result<()> { .map(|(s, (v, e))| (s, VariableChange(*v, e.clone()))) .collect::>(), opts.variables() - .chain(default_variables.iter()) + .chain(HashMap::from(default_variables).iter()) .map(|(v, _)| *v) .collect::>(), ); @@ -58,15 +58,15 @@ fn main() -> anyhow::Result<()> { debug!("building compiler"); let compiler = Compiler::from(Context::new( - 'L', - 'n', + 'L'.to_string(), + 'n'.to_string(), opts.variables() - .map(|(a, b)| (*a, *b)) - .chain(default_variables), + .map(|(a, b)| (a.to_string(), *b)) + .chain(default_variables.map(|(c, v)| (c.to_string(), v))), opts.instrument().clone(), opts.slopes() - .map(|(_, (a, b))| (*a, b.clone())) - .chain(once(('L', opts.length().clone()))), + .map(|(_, (a, b))| (a.to_string(), b.clone())) + .chain(once(('L'.to_string(), opts.length().clone()))), )); debug!("compiling to samples"); let samples: Vec = compiler diff --git a/src/compiler.rs b/src/compiler.rs index 071e4cd..b0b8a57 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,6 +1,6 @@ use std::{ any::{Any, TypeId, type_name}, - collections::{BTreeMap, HashMap}, + collections::BTreeMap, f64, fmt::Debug, str::FromStr, @@ -73,9 +73,9 @@ impl PartialEq for TokenVec { pub struct Silence; impl Token for Silence { - fn apply(&self, mut context: Context) -> Result { + fn apply(&self, context: Context) -> Result { trace!("⚡ {}", type_name::()); - let mut next = context.render(None)?; + let (mut context, mut next) = context.render(None)?; context.result.append(&mut next); Ok(context) } @@ -98,9 +98,9 @@ impl Token for Marker { pub struct Note(pub u8); impl Token for Note { - fn apply(&self, mut context: Context) -> Result { + fn apply(&self, context: Context) -> Result { trace!("⚡ {}", type_name::()); - let mut next = context.render(Some(self.0))?; + let (mut context, mut next) = context.render(Some(self.0))?; context.result.append(&mut next); Ok(context) } @@ -119,7 +119,7 @@ impl AsRef for VariableChange { impl Token for VariableChange { fn apply(&self, mut context: Context) -> Result { trace!("⚡ {}", type_name::()); - *context.get_mut(self.0)? = context.eval(self.1.as_ref())?; + *context.get_mut(self.0.to_string())? = context.eval(self.1.as_ref())?; Ok(context) } } @@ -147,7 +147,7 @@ impl Token for Loop { let mut old_result = context.result.clone(); let count = match self.0 { LoopCount::Litteral(n) => n, - LoopCount::Variable(v) => *context.get(v)? as usize, + LoopCount::Variable(v) => *context.get(v.to_string())? as usize, }; context.result.clear(); let new_context = self @@ -208,9 +208,10 @@ pub struct Slope(pub VariableChange, pub TokenVec); impl Token for Slope { fn apply(&self, mut context: Context) -> Result { trace!("⚡ {}", type_name::()); - context - .slopes - .push((self.0.as_ref().0, self.0.as_ref().1.as_ref().clone())); + context.slopes.push(( + self.0.as_ref().0.to_string(), + self.0.as_ref().1.as_ref().clone(), + )); context = self .1 .0 @@ -267,26 +268,26 @@ impl FromStr for Expression { #[derive(Debug, Clone, new)] #[cfg_attr(test, derive(PartialEq))] pub struct Context { - note_length_variable: char, - note_index_variable: char, + note_length_variable: String, + note_index_variable: String, #[new(default)] pub result: Vec, - #[new(into_iter = "(char, f64)")] - pub variables: HashMap, + #[new(into_iter = "(String, f64)")] + pub variables: BTreeMap, pub instrument: Expression, - #[new(into_iter = "(char, Expression)")] - pub slopes: Vec<(char, Expression)>, + #[new(into_iter = "(String, Expression)")] + pub slopes: Vec<(String, Expression)>, } #[derive(Debug, Error)] #[cfg_attr(test, derive(PartialEq))] #[error("variable not found: {0}")] -pub struct VariableNotFoundError(char); +pub struct VariableNotFoundError(String); #[derive(Debug, Error)] #[cfg_attr(test, derive(PartialEq))] #[error("expression not found: {0}")] -pub struct SlopeNotFoundError(char); +pub struct SlopeNotFoundError(String); #[derive(Debug, Error)] #[cfg_attr(test, derive(PartialEq))] @@ -300,72 +301,106 @@ pub enum CompilerError { } impl Context { - pub fn current_length(&self) -> Result { - self.get_slopes_for(self.note_length_variable) - .map_err(CompilerError::from)? + pub fn current_length(self) -> Result<(Self, f64), CompilerError> { + let Context { + note_length_variable, + note_index_variable, + result, + mut variables, + instrument, + slopes, + } = self; + let mut slopes_iter = slopes .iter() - .try_fold( - self.clone(), - |mut acc, slope| -> Result { - // just in case next slopes use L too - *acc.get_mut(self.note_length_variable)? = - self.eval(slope).map_err(Into::::into)?; - Ok(acc) - }, - )? - .get(self.note_length_variable) - .map_err(Into::into) - .cloned() + .filter_map(|(c, e)| (c == ¬e_length_variable).then_some(e)) + .peekable(); + if slopes_iter.peek().is_none() { + return Err(SlopeNotFoundError(note_length_variable.clone()).into()); + } + for slope in slopes_iter { + *variables + .get_mut(¬e_length_variable) + .ok_or(VariableNotFoundError(note_length_variable.clone()))? = + slope.instruction.eval(&slope.slab, &mut variables)?; + } + let note_length = *variables + .get(¬e_length_variable) + .ok_or(VariableNotFoundError(note_length_variable.clone()))?; + Ok(( + Context { + note_length_variable, + note_index_variable, + result, + variables, + instrument, + slopes, + }, + note_length, + )) } - pub fn get(&self, name: char) -> Result<&f64, VariableNotFoundError> { - self.variables.get(&name).ok_or(VariableNotFoundError(name)) - } - - pub fn get_mut(&mut self, name: char) -> Result<&mut f64, VariableNotFoundError> { + pub fn get(&self, name: impl AsRef + Into) -> Result<&f64, VariableNotFoundError> { self.variables - .get_mut(&name) - .ok_or(VariableNotFoundError(name)) + .get(name.as_ref()) + .ok_or(VariableNotFoundError(name.into())) } - pub fn get_slopes_for(&self, var: char) -> Result, SlopeNotFoundError> { + pub fn get_mut( + &mut self, + name: impl AsRef + Into, + ) -> Result<&mut f64, VariableNotFoundError> { + self.variables + .get_mut(name.as_ref()) + .ok_or(VariableNotFoundError(name.into())) + } + + pub fn get_slopes_for( + &self, + var: impl for<'a> PartialEq<&'a String> + Into, + ) -> Result, SlopeNotFoundError> { let result: Vec<&Expression> = self .slopes .iter() - .filter_map(|(c, e)| (c == &var).then_some(e)) + .filter_map(|(c, e)| (var == c).then_some(e)) .collect(); if result.is_empty() { - Err(SlopeNotFoundError(var)) + Err(SlopeNotFoundError(var.into())) } else { Ok(result) } } - fn tick(&mut self) -> Result<(), CompilerError> { - *self.get_mut('t')? += 1f64 / (SAMPLE_RATE as f64); - *self = self.slopes.iter().try_fold( - self.clone(), - |mut acc, (v, e)| -> Result { - *acc.get_mut(*v)? = acc.eval(e)?; - Ok(acc) - }, - )?; - Ok(()) + fn tick(mut self) -> Result { + *self.get_mut("t")? += 1f64 / (SAMPLE_RATE as f64); + let Context { + note_length_variable, + note_index_variable, + result, + mut variables, + instrument, + slopes, + } = self; + for (var, expr) in slopes.iter() { + *variables + .get_mut(var) + .ok_or(VariableNotFoundError(var.clone()))? = + expr.instruction.eval(&expr.slab, &mut variables)?; + } + Ok(Context { + note_length_variable, + note_index_variable, + result, + variables, + instrument, + slopes, + }) } - fn namespace_generator(&self) -> impl Iterator { - self.variables.iter().map(|(c, f)| (c.to_string(), *f)) - } - - pub fn eval(&self, expr: &Expression) -> Result { - self.eval_with( - expr, - &mut self.namespace_generator().collect::>(), - ) + pub fn eval(&mut self, expr: &Expression) -> Result { + Self::eval_with(expr, &mut self.variables) } pub fn eval_with( - &self, Expression { from: _, instruction, @@ -376,30 +411,33 @@ impl Context { instruction.eval(slab, ns) } - pub fn render(&mut self, n: Option) -> Result, CompilerError> { - let curr_t = *self.get('t')?; + pub fn render(mut self, n: Option) -> Result<(Self, Vec), CompilerError> { + let curr_t = *self.get("t")?; if let Some(note) = n { let mut result = Vec::new(); - let mut map = self.namespace_generator().collect::>(); - map.insert(self.note_index_variable.to_string(), note as f64); - while self.current_length()? > *self.get('t')? - curr_t + (1f64 / SAMPLE_RATE as f64) { - result.push(self.eval_with(&self.instrument, &mut map)? * f64::MAX); - self.tick()?; - map = self.namespace_generator().fold(map, |mut acc, (k, v)| { - acc.insert(k, v); - acc - }) + self.variables + .insert(self.note_index_variable.clone(), note as f64); + while { + let (new_self, length) = self.current_length()?; + self = new_self; + length + } > *self.get("t")? - curr_t + (1f64 / SAMPLE_RATE as f64) + { + result.push(Self::eval_with(&self.instrument, &mut self.variables)? * f64::MAX); + self = self.tick()?; } - Ok(result) + Ok((self, result)) } else { - while self.current_length()? > *self.get('t')? - curr_t { - self.tick()?; + while { + let (new_self, length) = self.current_length()?; + self = new_self; + length + } > *self.get("t")? - curr_t + { + self = self.tick()?; } - Ok(vec![ - 0.0; - ((*self.get('t')? - curr_t) * SAMPLE_RATE as f64) - as usize - ]) + let len = (*self.get("t")? - curr_t) * SAMPLE_RATE as f64; + Ok((self, vec![0.0; len as usize])) } } @@ -446,8 +484,8 @@ mod tests { fn context_generator() -> Context { Context::new( - 'L', - 'n', + 'L'.to_string(), + 'n'.to_string(), [ ('a', 5.0), ('t', 0.0), @@ -456,9 +494,10 @@ mod tests { ('L', 0.0), ('l', 4.0), ('T', 60.0), - ], + ] + .map(|(c, f)| (c.to_string(), f)), "sin(2*pi()*(442+442*((n+1)/N))*t)".parse().unwrap(), - [('L', "2^(2-log(2, l))*(60/T)")].map(|(c, e)| (c, e.parse().unwrap())), + [('L', "2^(2-log(2, l))*(60/T)")].map(|(c, e)| (c.to_string(), e.parse().unwrap())), ) } @@ -509,7 +548,7 @@ mod tests { compiler = compiler.step(note)?; let first = compiler.0.result.clone(); - *compiler.0.get_mut('t')? = 0.0; + *compiler.0.get_mut('t'.to_string())? = 0.0; compiler.0.result.clear(); compiler = compiler.step(note)?; let second = compiler.0.result.clone();