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")?;
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::<Vec<_>>(),
opts.variables()
.chain(default_variables.iter())
.chain(HashMap::from(default_variables).iter())
.map(|(v, _)| *v)
.collect::<Vec<_>>(),
);
@ -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<f32> = compiler

View file

@ -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<Context, CompilerError> {
fn apply(&self, context: Context) -> Result<Context, CompilerError> {
trace!("⚡ {}", type_name::<Self>());
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<Context, CompilerError> {
fn apply(&self, context: Context) -> Result<Context, CompilerError> {
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);
Ok(context)
}
@ -119,7 +119,7 @@ impl AsRef<VariableChange> for VariableChange {
impl Token for VariableChange {
fn apply(&self, mut context: Context) -> Result<Context, CompilerError> {
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)
}
}
@ -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<Context, CompilerError> {
trace!("⚡ {}", type_name::<Self>());
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<f64>,
#[new(into_iter = "(char, f64)")]
pub variables: HashMap<char, f64>,
#[new(into_iter = "(String, f64)")]
pub variables: BTreeMap<String, f64>,
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<f64, CompilerError> {
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<Context, CompilerError> {
// just in case next slopes use L too
*acc.get_mut(self.note_length_variable)? =
self.eval(slope).map_err(Into::<CompilerError>::into)?;
Ok(acc)
},
)?
.get(self.note_length_variable)
.map_err(Into::into)
.cloned()
.filter_map(|(c, e)| (c == &note_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(&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,
))
}
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<str> + Into<String>) -> 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<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
.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<Self, CompilerError> {
*acc.get_mut(*v)? = acc.eval(e)?;
Ok(acc)
},
)?;
Ok(())
fn tick(mut self) -> Result<Self, CompilerError> {
*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<Item = (String, f64)> {
self.variables.iter().map(|(c, f)| (c.to_string(), *f))
}
pub fn eval(&self, expr: &Expression) -> Result<f64, fasteval::Error> {
self.eval_with(
expr,
&mut self.namespace_generator().collect::<BTreeMap<_, _>>(),
)
pub fn eval(&mut self, expr: &Expression) -> Result<f64, fasteval::Error> {
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<u8>) -> Result<Vec<f64>, CompilerError> {
let curr_t = *self.get('t')?;
pub fn render(mut self, n: Option<u8>) -> Result<(Self, Vec<f64>), CompilerError> {
let curr_t = *self.get("t")?;
if let Some(note) = n {
let mut result = Vec::new();
let mut map = self.namespace_generator().collect::<BTreeMap<_, _>>();
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();