diff options
| author | Graham Northup <grissess@nexusg.org> | 2017-09-18 23:12:14 -0400 | 
|---|---|---|
| committer | Graham Northup <grissess@nexusg.org> | 2017-09-18 23:12:14 -0400 | 
| commit | 85925d69e08455bd91d32a27cd3690c9cb634a6b (patch) | |
| tree | 1023c044a3350aadb4eb06bf73e209f81f21f8ae | |
Initial work on modular synthesis
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Cargo.toml | 6 | ||||
| -rw-r--r-- | src/lib.rs | 11 | ||||
| -rw-r--r-- | src/main.rs | 23 | ||||
| -rw-r--r-- | src/synth/math.rs | 41 | ||||
| -rw-r--r-- | src/synth/mod.rs | 155 | ||||
| -rw-r--r-- | src/synth/param.rs | 14 | ||||
| -rw-r--r-- | src/synth/sine.rs | 24 | ||||
| -rw-r--r-- | src/types.rs | 1 | 
9 files changed, 278 insertions, 0 deletions
| diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4308d82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target/ +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2da6bc8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "synfone" +version = "0.1.0" +authors = ["Graham Northup <grissess@nexusg.org>"] + +[dependencies] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9a1eaff --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,11 @@ +pub mod types; +pub use types::*; + +pub mod synth; + +#[cfg(test)] +mod tests { +    #[test] +    fn it_works() { +    } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..6db2bcf --- /dev/null +++ b/src/main.rs @@ -0,0 +1,23 @@ +use std::io; +use std::io::*; + +extern crate synfone; +use synfone::synth::*; + +const FRAMES: usize = 44100 * 2; + +fn main() { +    let mut params = Default::default(); +     +    let mut freq: GenBox = Box::new(Param { name: "freq".to_string(), default: 440.0, buf: SampleBuffer::new(1) }); +    let mut sg: GenBox = Box::new(Sine { freq: freq, phase: 0.0, buf: SampleBuffer::new(params.env.default_buffer_size) }); + +    let mut counter = 0; +    let mut out = io::stderr(); + +    while counter < FRAMES { +        let buf = sg.eval(¶ms); +        out.write_all(buf.bytes()); +        counter += buf.len(); +    } +} diff --git a/src/synth/math.rs b/src/synth/math.rs new file mode 100644 index 0000000..b016103 --- /dev/null +++ b/src/synth/math.rs @@ -0,0 +1,41 @@ +use super::*; + +pub struct Add { +    terms: Vec<GenBox>, +    buf: SampleBuffer, +} + +impl Generator for Add { +    fn eval<'a>(&'a mut self, params: &Parameters) -> &'a SampleBuffer { +        if self.terms.is_empty() { +            self.buf.zero(); +        } else { +            let (first, next) = self.terms.split_at_mut(1); +            self.buf.update_from(first[0].eval(params)); +            for term in next { +                self.buf.sum_into(term.eval(params)); +            } +        } +        &self.buf +    } +} + +pub struct Mul { +    factors: Vec<GenBox>, +    buf: SampleBuffer, +} + +impl Generator for Mul { +    fn eval<'a>(&'a mut self, params: &Parameters) -> &'a SampleBuffer { +        if self.factors.is_empty() { +            self.buf.zero(); +        } else { +            let (first, next) = self.factors.split_at_mut(1); +            self.buf.update_from(first[0].eval(params)); +            for factor in next { +                self.buf.mul_into(factor.eval(params)); +            } +        } +        &self.buf +    } +} diff --git a/src/synth/mod.rs b/src/synth/mod.rs new file mode 100644 index 0000000..d0c837a --- /dev/null +++ b/src/synth/mod.rs @@ -0,0 +1,155 @@ +use std::{iter, cmp, slice, mem}; +use std::ops::{Index, IndexMut}; +use std::collections::HashMap; +use super::*; + +#[derive(PartialEq,Eq,Clone,Copy)] +pub enum Rate { +    Sample, +    Control, +} + +pub struct SampleBuffer { +    pub samples: Vec<Sample>, +    pub rate: Rate, +} + +pub struct Environment { +    pub sample_rate: f32, +    pub default_buffer_size: usize, +} + +impl Default for Environment { +    fn default() -> Environment { +        Environment { +            sample_rate: 44100.0, +            default_buffer_size: 64, +        } +    } +} + +pub struct Parameters { +    pub env: Environment, +    pub vars: HashMap<String, f32>, +} + +impl Default for Parameters { +    fn default() -> Parameters { +        Parameters { +            env: Default::default(), +            vars: HashMap::new(), +        } +    } +} + +impl SampleBuffer { +    pub fn new(sz: usize) -> SampleBuffer { +        let mut samples = Vec::with_capacity(sz); +        samples.extend(iter::repeat(0 as Sample).take(sz)); +        SampleBuffer { +            samples: samples, +            rate: Rate::Sample, +        } +    } + +    pub fn len(&self) -> usize { +        self.samples.len() +    } + +    pub fn first(&self) -> Sample { +        *self.samples.first().unwrap() +    } + +    pub fn set(&mut self, val: Sample) { +        self.samples[0] = val; +        self.rate = Rate::Control; +    } + +    pub fn update_from(&mut self, other: &SampleBuffer) { +        self.rate = other.rate; +        match self.rate { +            Rate::Sample => { +                for i in 0..cmp::min(self.len(), other.len()) { +                    self.samples[i] = other.samples[i]; +                } +            }, +            Rate::Control => { +                self.samples[0] = other.samples[0]; +            }, +        } +    } + +    pub fn sum_into(&mut self, other: &SampleBuffer) { +        match self.rate { +            Rate::Sample => { +                for i in 0..cmp::min(self.len(), other.len()) { +                    self.samples[i] += match other.rate { +                        Rate::Sample => other.samples[i], +                        Rate::Control => other.samples[0], +                    }; +                } +            }, +            Rate::Control => { +                self.samples[0] += other.samples[0]; +            }, +        } +    } + +    pub fn mul_into(&mut self, other: &SampleBuffer) { +        match self.rate { +            Rate::Sample => { +                for i in 0..cmp::min(self.len(), other.len()) { +                    self.samples[i] *= match other.rate { +                        Rate::Sample => other.samples[i], +                        Rate::Control => other.samples[0], +                    }; +                } +            }, +            Rate::Control => { +                self.samples[0] *= other.samples[0]; +            }, +        } +    } + +    pub fn zero(&mut self) { +        for i in 0..self.len() { +            self.samples[i] = 0.0; +        } +    } + +    pub fn bytes<'a>(&'a self) -> &'a [u8] { +        unsafe { +            slice::from_raw_parts( +                self.samples.as_ptr() as *const u8, +                self.samples.len() * mem::size_of::<Sample>(), +            ) +        } +    } +} + +impl Index<usize> for SampleBuffer { +    type Output = Sample; +    fn index(&self, idx: usize) -> &Sample { &self.samples[idx] } +} + +impl IndexMut<usize> for SampleBuffer { +    fn index_mut(&mut self, idx: usize) -> &mut Sample { &mut self.samples[idx] } +} + +pub trait Generator { +    fn eval<'a>(&'a mut self, params: &Parameters) -> &'a SampleBuffer; +} + +pub type GenBox = Box<Generator>; + +pub mod param; +pub use self::param::Param; +pub mod math; +pub use self::math::{Add, Mul}; +pub mod sine; +pub use self::sine::Sine; +//pub mod saw; +//pub use saw::Saw; +//pub mod triangle; +//pub use triangle::Triangle; + diff --git a/src/synth/param.rs b/src/synth/param.rs new file mode 100644 index 0000000..3177ffd --- /dev/null +++ b/src/synth/param.rs @@ -0,0 +1,14 @@ +use super::*; + +pub struct Param { +    name: String, +    default: Sample, +    buf: SampleBuffer, +} + +impl Generator for Param { +    fn eval<'a>(&'a mut self, params: &Parameters) -> &'a SampleBuffer { +        self.buf.set(*params.vars.get(&self.name).unwrap_or(&self.default)); +        &self.buf +    } +} diff --git a/src/synth/sine.rs b/src/synth/sine.rs new file mode 100644 index 0000000..3ee4857 --- /dev/null +++ b/src/synth/sine.rs @@ -0,0 +1,24 @@ +use std::f32::consts::PI; +use super::*; + +const TAU: f32 = 2f32 * PI; + +pub struct Sine { +    freq: GenBox, +    phase: f32, +    buf: SampleBuffer, +} + +impl Generator for Sine { +    fn eval<'a>(&'a mut self, params: &Parameters) -> &'a SampleBuffer { +        self.buf.rate = Rate::Control; + +        let pvel = TAU * self.freq.eval(params).first() / params.env.sample_rate; +        for i in 0..self.buf.len() { +            self.buf[i] = (self.phase + pvel * (i as f32)).sin() +        } + +        self.phase = (self.phase + pvel * (self.buf.len() as f32)) % TAU; +        &self.buf +    } +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..b9ff15f --- /dev/null +++ b/src/types.rs @@ -0,0 +1 @@ +pub type Sample = f32; | 
