faster
This commit is contained in:
parent
8024b18850
commit
e9fcec8b32
2 changed files with 135 additions and 96 deletions
|
@ -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
|
||||||
|
|
211
src/compiler.rs
211
src/compiler.rs
|
@ -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 == ¬e_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(¬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,
|
||||||
.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();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue