This commit is contained in:
brevalferrari 2025-06-05 01:39:05 +02:00
parent 8024b18850
commit e9fcec8b32
2 changed files with 135 additions and 96 deletions

View file

@ -25,13 +25,13 @@ fn main() -> anyhow::Result<()> {
let sink = Sink::try_new(&stream_handle).context("Epic audio playback failure")?; let sink = Sink::try_new(&stream_handle).context("Epic audio playback failure")?;
debug!("audio sink acquired"); debug!("audio sink acquired");
let default_variables = HashMap::from([ let default_variables = [
('l', 4f64), ('l', 4f64),
('L', 0.0), ('L', 0.0),
('t', 0.0), ('t', 0.0),
('T', 60.0), ('T', 60.0),
('N', opts.notes().len() as f64), ('N', opts.notes().len() as f64),
]); ];
debug!("building parser"); debug!("building parser");
let parser = Parser::new( let parser = Parser::new(
@ -40,7 +40,7 @@ fn main() -> anyhow::Result<()> {
.map(|(s, (v, e))| (s, VariableChange(*v, e.clone()))) .map(|(s, (v, e))| (s, VariableChange(*v, e.clone())))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
opts.variables() opts.variables()
.chain(default_variables.iter()) .chain(HashMap::from(default_variables).iter())
.map(|(v, _)| *v) .map(|(v, _)| *v)
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
@ -58,15 +58,15 @@ fn main() -> anyhow::Result<()> {
debug!("building compiler"); debug!("building compiler");
let compiler = Compiler::from(Context::new( let compiler = Compiler::from(Context::new(
'L', 'L'.to_string(),
'n', 'n'.to_string(),
opts.variables() opts.variables()
.map(|(a, b)| (*a, *b)) .map(|(a, b)| (a.to_string(), *b))
.chain(default_variables), .chain(default_variables.map(|(c, v)| (c.to_string(), v))),
opts.instrument().clone(), opts.instrument().clone(),
opts.slopes() opts.slopes()
.map(|(_, (a, b))| (*a, b.clone())) .map(|(_, (a, b))| (a.to_string(), b.clone()))
.chain(once(('L', opts.length().clone()))), .chain(once(('L'.to_string(), opts.length().clone()))),
)); ));
debug!("compiling to samples"); debug!("compiling to samples");
let samples: Vec<f32> = compiler let samples: Vec<f32> = compiler

View file

@ -1,6 +1,6 @@
use std::{ use std::{
any::{Any, TypeId, type_name}, any::{Any, TypeId, type_name},
collections::{BTreeMap, HashMap}, collections::BTreeMap,
f64, f64,
fmt::Debug, fmt::Debug,
str::FromStr, str::FromStr,
@ -73,9 +73,9 @@ impl PartialEq for TokenVec {
pub struct Silence; pub struct Silence;
impl Token for Silence { impl Token for Silence {
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> { fn apply(&self, context: Context) -> Result<Context, CompilerError> {
trace!("⚡ {}", type_name::<Self>()); trace!("⚡ {}", type_name::<Self>());
let mut next = context.render(None)?; let (mut context, mut next) = context.render(None)?;
context.result.append(&mut next); context.result.append(&mut next);
Ok(context) Ok(context)
} }
@ -98,9 +98,9 @@ impl Token for Marker {
pub struct Note(pub u8); pub struct Note(pub u8);
impl Token for Note { impl Token for Note {
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> { fn apply(&self, context: Context) -> Result<Context, CompilerError> {
trace!("⚡ {}", type_name::<Self>()); trace!("⚡ {}", type_name::<Self>());
let mut next = context.render(Some(self.0))?; let (mut context, mut next) = context.render(Some(self.0))?;
context.result.append(&mut next); context.result.append(&mut next);
Ok(context) Ok(context)
} }
@ -119,7 +119,7 @@ impl AsRef<VariableChange> for VariableChange {
impl Token for VariableChange { impl Token for VariableChange {
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> { fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
trace!("⚡ {}", type_name::<Self>()); trace!("⚡ {}", type_name::<Self>());
*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) Ok(context)
} }
} }
@ -147,7 +147,7 @@ impl Token for Loop {
let mut old_result = context.result.clone(); let mut old_result = context.result.clone();
let count = match self.0 { let count = match self.0 {
LoopCount::Litteral(n) => n, 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(); context.result.clear();
let new_context = self let new_context = self
@ -208,9 +208,10 @@ pub struct Slope(pub VariableChange, pub TokenVec);
impl Token for Slope { impl Token for Slope {
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> { fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
trace!("⚡ {}", type_name::<Self>()); trace!("⚡ {}", type_name::<Self>());
context context.slopes.push((
.slopes self.0.as_ref().0.to_string(),
.push((self.0.as_ref().0, self.0.as_ref().1.as_ref().clone())); self.0.as_ref().1.as_ref().clone(),
));
context = self context = self
.1 .1
.0 .0
@ -267,26 +268,26 @@ impl FromStr for Expression {
#[derive(Debug, Clone, new)] #[derive(Debug, Clone, new)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
pub struct Context { pub struct Context {
note_length_variable: char, note_length_variable: String,
note_index_variable: char, note_index_variable: String,
#[new(default)] #[new(default)]
pub result: Vec<f64>, pub result: Vec<f64>,
#[new(into_iter = "(char, f64)")] #[new(into_iter = "(String, f64)")]
pub variables: HashMap<char, f64>, pub variables: BTreeMap<String, f64>,
pub instrument: Expression, pub instrument: Expression,
#[new(into_iter = "(char, Expression)")] #[new(into_iter = "(String, Expression)")]
pub slopes: Vec<(char, Expression)>, pub slopes: Vec<(String, Expression)>,
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[error("variable not found: {0}")] #[error("variable not found: {0}")]
pub struct VariableNotFoundError(char); pub struct VariableNotFoundError(String);
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[error("expression not found: {0}")] #[error("expression not found: {0}")]
pub struct SlopeNotFoundError(char); pub struct SlopeNotFoundError(String);
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
@ -300,72 +301,106 @@ pub enum CompilerError {
} }
impl Context { impl Context {
pub fn current_length(&self) -> Result<f64, CompilerError> { pub fn current_length(self) -> Result<(Self, f64), CompilerError> {
self.get_slopes_for(self.note_length_variable) let Context {
.map_err(CompilerError::from)? note_length_variable,
note_index_variable,
result,
mut variables,
instrument,
slopes,
} = self;
let mut slopes_iter = slopes
.iter() .iter()
.try_fold( .filter_map(|(c, e)| (c == &note_length_variable).then_some(e))
self.clone(), .peekable();
|mut acc, slope| -> Result<Context, CompilerError> { if slopes_iter.peek().is_none() {
// just in case next slopes use L too return Err(SlopeNotFoundError(note_length_variable.clone()).into());
*acc.get_mut(self.note_length_variable)? = }
self.eval(slope).map_err(Into::<CompilerError>::into)?; for slope in slopes_iter {
Ok(acc) *variables
.get_mut(&note_length_variable)
.ok_or(VariableNotFoundError(note_length_variable.clone()))? =
slope.instruction.eval(&slope.slab, &mut variables)?;
}
let note_length = *variables
.get(&note_length_variable)
.ok_or(VariableNotFoundError(note_length_variable.clone()))?;
Ok((
Context {
note_length_variable,
note_index_variable,
result,
variables,
instrument,
slopes,
}, },
)? note_length,
.get(self.note_length_variable) ))
.map_err(Into::into)
.cloned()
} }
pub fn get(&self, name: char) -> Result<&f64, VariableNotFoundError> { pub fn get(&self, name: impl AsRef<str> + Into<String>) -> Result<&f64, VariableNotFoundError> {
self.variables.get(&name).ok_or(VariableNotFoundError(name))
}
pub fn get_mut(&mut self, name: char) -> Result<&mut f64, VariableNotFoundError> {
self.variables self.variables
.get_mut(&name) .get(name.as_ref())
.ok_or(VariableNotFoundError(name)) .ok_or(VariableNotFoundError(name.into()))
} }
pub fn get_slopes_for(&self, var: char) -> Result<Vec<&Expression>, SlopeNotFoundError> { pub fn get_mut(
&mut self,
name: impl AsRef<str> + Into<String>,
) -> 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<String>,
) -> Result<Vec<&Expression>, SlopeNotFoundError> {
let result: Vec<&Expression> = self let result: Vec<&Expression> = self
.slopes .slopes
.iter() .iter()
.filter_map(|(c, e)| (c == &var).then_some(e)) .filter_map(|(c, e)| (var == c).then_some(e))
.collect(); .collect();
if result.is_empty() { if result.is_empty() {
Err(SlopeNotFoundError(var)) Err(SlopeNotFoundError(var.into()))
} else { } else {
Ok(result) Ok(result)
} }
} }
fn tick(&mut self) -> Result<(), CompilerError> { fn tick(mut self) -> Result<Self, CompilerError> {
*self.get_mut('t')? += 1f64 / (SAMPLE_RATE as f64); *self.get_mut("t")? += 1f64 / (SAMPLE_RATE as f64);
*self = self.slopes.iter().try_fold( let Context {
self.clone(), note_length_variable,
|mut acc, (v, e)| -> Result<Self, CompilerError> { note_index_variable,
*acc.get_mut(*v)? = acc.eval(e)?; result,
Ok(acc) mut variables,
}, instrument,
)?; slopes,
Ok(()) } = 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<Item = (String, f64)> { pub fn eval(&mut self, expr: &Expression) -> Result<f64, fasteval::Error> {
self.variables.iter().map(|(c, f)| (c.to_string(), *f)) Self::eval_with(expr, &mut self.variables)
}
pub fn eval(&self, expr: &Expression) -> Result<f64, fasteval::Error> {
self.eval_with(
expr,
&mut self.namespace_generator().collect::<BTreeMap<_, _>>(),
)
} }
pub fn eval_with( pub fn eval_with(
&self,
Expression { Expression {
from: _, from: _,
instruction, instruction,
@ -376,30 +411,33 @@ impl Context {
instruction.eval(slab, ns) instruction.eval(slab, ns)
} }
pub fn render(&mut self, n: Option<u8>) -> Result<Vec<f64>, CompilerError> { pub fn render(mut self, n: Option<u8>) -> Result<(Self, Vec<f64>), CompilerError> {
let curr_t = *self.get('t')?; let curr_t = *self.get("t")?;
if let Some(note) = n { if let Some(note) = n {
let mut result = Vec::new(); let mut result = Vec::new();
let mut map = self.namespace_generator().collect::<BTreeMap<_, _>>(); self.variables
map.insert(self.note_index_variable.to_string(), note as f64); .insert(self.note_index_variable.clone(), note as f64);
while self.current_length()? > *self.get('t')? - curr_t + (1f64 / SAMPLE_RATE as f64) { while {
result.push(self.eval_with(&self.instrument, &mut map)? * f64::MAX); let (new_self, length) = self.current_length()?;
self.tick()?; self = new_self;
map = self.namespace_generator().fold(map, |mut acc, (k, v)| { length
acc.insert(k, v); } > *self.get("t")? - curr_t + (1f64 / SAMPLE_RATE as f64)
acc {
}) result.push(Self::eval_with(&self.instrument, &mut self.variables)? * f64::MAX);
self = self.tick()?;
} }
Ok(result) Ok((self, result))
} else { } else {
while self.current_length()? > *self.get('t')? - curr_t { while {
self.tick()?; let (new_self, length) = self.current_length()?;
self = new_self;
length
} > *self.get("t")? - curr_t
{
self = self.tick()?;
} }
Ok(vec![ let len = (*self.get("t")? - curr_t) * SAMPLE_RATE as f64;
0.0; Ok((self, vec![0.0; len as usize]))
((*self.get('t')? - curr_t) * SAMPLE_RATE as f64)
as usize
])
} }
} }
@ -446,8 +484,8 @@ mod tests {
fn context_generator() -> Context { fn context_generator() -> Context {
Context::new( Context::new(
'L', 'L'.to_string(),
'n', 'n'.to_string(),
[ [
('a', 5.0), ('a', 5.0),
('t', 0.0), ('t', 0.0),
@ -456,9 +494,10 @@ mod tests {
('L', 0.0), ('L', 0.0),
('l', 4.0), ('l', 4.0),
('T', 60.0), ('T', 60.0),
], ]
.map(|(c, f)| (c.to_string(), f)),
"sin(2*pi()*(442+442*((n+1)/N))*t)".parse().unwrap(), "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)?; compiler = compiler.step(note)?;
let first = compiler.0.result.clone(); 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.0.result.clear();
compiler = compiler.step(note)?; compiler = compiler.step(note)?;
let second = compiler.0.result.clone(); let second = compiler.0.result.clone();