summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraham Northup <grissess@nexusg.org>2017-09-19 23:56:15 -0400
committerGraham Northup <grissess@nexusg.org>2017-09-19 23:56:15 -0400
commit9866c0f34c268a09ecaaa9a4361c1c267799358e (patch)
treed10bea3717c25105ced7457535412072d343686f
parent26e95364ad9073dd7cb571454cd52ae66f320a73 (diff)
Starting work on proto decode
-rw-r--r--Cargo.toml1
-rw-r--r--src/lib.rs5
-rw-r--r--src/main.rs22
-rw-r--r--src/proto.rs58
-rw-r--r--src/synth/math.rs9
-rw-r--r--src/synth/mod.rs48
-rw-r--r--src/synth/param.rs4
-rw-r--r--src/synth/saw.rs25
-rw-r--r--src/synth/sine.rs6
-rw-r--r--src/synth/square.rs29
-rw-r--r--src/synth/triangle.rs32
-rw-r--r--src/types.rs25
12 files changed, 244 insertions, 20 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 2da6bc8..5b3f2ae 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,3 +4,4 @@ version = "0.1.0"
authors = ["Graham Northup <grissess@nexusg.org>"]
[dependencies]
+byteorder = "1.1.0"
diff --git a/src/lib.rs b/src/lib.rs
index 9a1eaff..08d71d3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,7 +1,12 @@
+#![feature(associated_consts)]
+
+extern crate byteorder;
+
pub mod types;
pub use types::*;
pub mod synth;
+pub mod proto;
#[cfg(test)]
mod tests {
diff --git a/src/main.rs b/src/main.rs
index 711463b..05538e9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,17 +10,33 @@ fn main() {
let mut params = Parameters::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 sg: GenBox = Box::new(Saw { freq: freq, phase: 0.0, buf: SampleBuffer::new(params.env.default_buffer_size) });
+
+ let mut freq2: GenBox = Box::new(Param { name: "freq2".to_string(), default: 660.0, buf: SampleBuffer::new(1) });
+ let mut sg2: GenBox = Box::new(Sine { freq: freq2, phase: 0.0, buf: SampleBuffer::new(params.env.default_buffer_size) });
+
+ let mut half1: GenBox = Box::new(Param { name: "half".to_string(), default: 1.0, buf: SampleBuffer::new(1) });
+ let mut half2: GenBox = Box::new(Param { name: "half".to_string(), default: 0.0, buf: SampleBuffer::new(1) });
+ let mut sc1: GenBox = Box::new(Mul { factors: vec![sg, half1], buf: SampleBuffer::new(params.env.default_buffer_size) });
+ let mut sc2: GenBox = Box::new(Mul { factors: vec![sg2, half2], buf: SampleBuffer::new(params.env.default_buffer_size) });
+ let mut gen: GenBox = Box::new(Add { terms: vec![sc1, sc2], buf: SampleBuffer::new(params.env.default_buffer_size) });
let mut counter = 0;
let mut out = io::stdout();
+ let mut outbuf: Vec<u8> = Vec::new();
params.vars.insert("freq".to_string(), 440.0);
+ params.vars.insert("freq2".to_string(), 660.0);
while counter < FRAMES {
*params.vars.get_mut("freq").unwrap() = 440.0 + 440.0 * ((counter as f32) / (FRAMES as f32));
- let buf = sg.eval(&params);
- out.write_all(buf.bytes());
+ *params.vars.get_mut("freq2").unwrap() = 660.0 + 220.0 * ((counter as f32) / (FRAMES as f32));
+ let buf = gen.eval(&params);
+ let curlen = outbuf.len();
+ outbuf.reserve_exact(buf.size() - curlen);
+ unsafe { outbuf.set_len(buf.size()); }
+ buf.bytes(&mut outbuf);
+ out.write_all(&outbuf);
counter += buf.len();
}
}
diff --git a/src/proto.rs b/src/proto.rs
new file mode 100644
index 0000000..06d7b49
--- /dev/null
+++ b/src/proto.rs
@@ -0,0 +1,58 @@
+use std::mem;
+use std::time::Duration;
+use super::*;
+
+use ::byteorder::{ByteOrder, NetworkEndian};
+
+const OBLIGATE_POLYPHONE: u32 = 0xffffffff;
+
+pub enum Command {
+ KeepAlive,
+ Ping{data: [u8; 32]},
+ Quit,
+ Play{sec: u32, usec: u32, freq: u32, amp: f32, voice: u32},
+ Caps{voices: u32, tp: [u8; 4], ident: [u8; 24]},
+ PCM{samples: [i16; 16]},
+ Unknown{data: [u8; 36]},
+}
+
+impl Command {
+ const SIZE: usize = 36;
+
+ fn duration(&self) -> Option<Duration> {
+ match *self {
+ Command::Play{sec, usec, ..} => Some(Duration::new(sec as u64, usec * 1000)),
+ _ => None,
+ }
+ }
+
+ fn pitch(&self) -> Option<Pitch> {
+ match *self {
+ Command::Play{freq, ..} => Some(Pitch::Freq(freq as f32)),
+ _ => None,
+ }
+ }
+}
+
+impl<'a> From<&'a [u8; 36]> for Command {
+ fn from(packet: &'a [u8; 36]) -> Command {
+ let mut fields_u32: [u32; 9] = unsafe { mem::uninitialized() };
+ let mut fields_f32: [f32; 9] = unsafe { mem::uninitialized() };
+ NetworkEndian::read_u32_into(packet, &mut fields_u32);
+ unsafe { NetworkEndian::read_f32_into_unchecked(packet, &mut fields_f32); }
+
+ match fields_u32[0] {
+ 0 => Command::KeepAlive,
+ 1 => {
+ let mut data: [u8; 32] = unsafe { mem::uninitialized() };
+ data.copy_from_slice(&packet[4..]);
+ Command::Ping{data: data}
+ }
+ _ => {
+ let mut data: [u8; 36] = unsafe { mem::uninitialized() };
+ data.copy_from_slice(packet);
+ Command::Unknown{data: data}
+ }
+ }
+ }
+}
diff --git a/src/synth/math.rs b/src/synth/math.rs
index 66a86a0..91e58e3 100644
--- a/src/synth/math.rs
+++ b/src/synth/math.rs
@@ -1,5 +1,7 @@
use super::*;
+use std::mem;
+#[derive(Debug)]
pub struct Add {
pub terms: Vec<GenBox>,
pub buf: SampleBuffer,
@@ -18,8 +20,12 @@ impl Generator for Add {
}
&self.buf
}
+ fn set_buffer(&mut self, buf: SampleBuffer) -> SampleBuffer {
+ mem::replace(&mut self.buf, buf)
+ }
}
+#[derive(Debug)]
pub struct Mul {
pub factors: Vec<GenBox>,
pub buf: SampleBuffer,
@@ -38,4 +44,7 @@ impl Generator for Mul {
}
&self.buf
}
+ fn set_buffer(&mut self, buf: SampleBuffer) -> SampleBuffer {
+ mem::replace(&mut self.buf, buf)
+ }
}
diff --git a/src/synth/mod.rs b/src/synth/mod.rs
index d0c837a..bf73200 100644
--- a/src/synth/mod.rs
+++ b/src/synth/mod.rs
@@ -1,14 +1,18 @@
use std::{iter, cmp, slice, mem};
+use std::fmt::Debug;
use std::ops::{Index, IndexMut};
use std::collections::HashMap;
use super::*;
-#[derive(PartialEq,Eq,Clone,Copy)]
+use ::byteorder::ByteOrder;
+
+#[derive(PartialEq,Eq,Clone,Copy,Debug)]
pub enum Rate {
Sample,
Control,
}
+#[derive(Debug)]
pub struct SampleBuffer {
pub samples: Vec<Sample>,
pub rate: Rate,
@@ -82,7 +86,11 @@ impl SampleBuffer {
pub fn sum_into(&mut self, other: &SampleBuffer) {
match self.rate {
Rate::Sample => {
- for i in 0..cmp::min(self.len(), other.len()) {
+ let bound = match other.rate {
+ Rate::Sample => cmp::min(self.len(), other.len()),
+ Rate::Control => self.len(),
+ };
+ for i in 0..bound {
self.samples[i] += match other.rate {
Rate::Sample => other.samples[i],
Rate::Control => other.samples[0],
@@ -98,7 +106,11 @@ impl SampleBuffer {
pub fn mul_into(&mut self, other: &SampleBuffer) {
match self.rate {
Rate::Sample => {
- for i in 0..cmp::min(self.len(), other.len()) {
+ let bound = match other.rate {
+ Rate::Sample => cmp::min(self.len(), other.len()),
+ Rate::Control => self.len(),
+ };
+ for i in 0..bound {
self.samples[i] *= match other.rate {
Rate::Sample => other.samples[i],
Rate::Control => other.samples[0],
@@ -117,13 +129,13 @@ impl SampleBuffer {
}
}
- 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>(),
- )
- }
+ pub fn size(&self) -> usize {
+ mem::size_of::<Sample>() * self.samples.len()
+ }
+
+ pub fn bytes(&self, buf: &mut [u8]) {
+ // FIXME: Depends on f32 instead of Sample alias
+ ::byteorder::LittleEndian::write_f32_into(&self.samples, buf);
}
}
@@ -136,8 +148,9 @@ impl IndexMut<usize> for SampleBuffer {
fn index_mut(&mut self, idx: usize) -> &mut Sample { &mut self.samples[idx] }
}
-pub trait Generator {
+pub trait Generator : Debug {
fn eval<'a>(&'a mut self, params: &Parameters) -> &'a SampleBuffer;
+ fn set_buffer(&mut self, buf: SampleBuffer) -> SampleBuffer;
}
pub type GenBox = Box<Generator>;
@@ -148,8 +161,11 @@ 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;
-
+pub mod saw;
+pub use self::saw::Saw;
+pub mod triangle;
+pub use self::triangle::Triangle;
+pub mod square;
+pub use self::square::Square;
+//pub mod asdr;
+//pub use self::asdr::ASDR;
diff --git a/src/synth/param.rs b/src/synth/param.rs
index ced5900..f679b56 100644
--- a/src/synth/param.rs
+++ b/src/synth/param.rs
@@ -1,5 +1,6 @@
use super::*;
+#[derive(Debug)]
pub struct Param {
pub name: String,
pub default: Sample,
@@ -11,4 +12,7 @@ impl Generator for Param {
self.buf.set(*params.vars.get(&self.name).unwrap_or(&self.default));
&self.buf
}
+ fn set_buffer(&mut self, buf: SampleBuffer) -> SampleBuffer {
+ mem::replace(&mut self.buf, buf)
+ }
}
diff --git a/src/synth/saw.rs b/src/synth/saw.rs
new file mode 100644
index 0000000..bfd3cb0
--- /dev/null
+++ b/src/synth/saw.rs
@@ -0,0 +1,25 @@
+use super::*;
+
+#[derive(Debug)]
+pub struct Saw {
+ pub freq: GenBox,
+ pub phase: f32,
+ pub buf: SampleBuffer,
+}
+
+impl Generator for Saw {
+ fn eval<'a>(&'a mut self, params: &Parameters) -> &'a SampleBuffer {
+ self.buf.rate = Rate::Sample;
+
+ let pvel = self.freq.eval(params).first() / params.env.sample_rate;
+ for i in 0..self.buf.len() {
+ self.buf[i] = 2.0 * ((self.phase + pvel * (i as f32)) % 1.0) - 1.0;
+ }
+
+ self.phase = (self.phase + pvel * (self.buf.len() as f32)) % 1.0;
+ &self.buf
+ }
+ fn set_buffer(&mut self, buf: SampleBuffer) -> SampleBuffer {
+ mem::replace(&mut self.buf, buf)
+ }
+}
diff --git a/src/synth/sine.rs b/src/synth/sine.rs
index 212ab21..90f3200 100644
--- a/src/synth/sine.rs
+++ b/src/synth/sine.rs
@@ -3,6 +3,7 @@ use super::*;
const TAU: f32 = 2f32 * PI;
+#[derive(Debug)]
pub struct Sine {
pub freq: GenBox,
pub phase: f32,
@@ -11,7 +12,7 @@ pub struct Sine {
impl Generator for Sine {
fn eval<'a>(&'a mut self, params: &Parameters) -> &'a SampleBuffer {
- self.buf.rate = Rate::Control;
+ self.buf.rate = Rate::Sample;
let pvel = TAU * self.freq.eval(params).first() / params.env.sample_rate;
for i in 0..self.buf.len() {
@@ -21,4 +22,7 @@ impl Generator for Sine {
self.phase = (self.phase + pvel * (self.buf.len() as f32)) % TAU;
&self.buf
}
+ fn set_buffer(&mut self, buf: SampleBuffer) -> SampleBuffer {
+ mem::replace(&mut self.buf, buf)
+ }
}
diff --git a/src/synth/square.rs b/src/synth/square.rs
new file mode 100644
index 0000000..8197110
--- /dev/null
+++ b/src/synth/square.rs
@@ -0,0 +1,29 @@
+use super::*;
+
+#[derive(Debug)]
+pub struct Square {
+ pub freq: GenBox,
+ pub phase: f32,
+ pub buf: SampleBuffer,
+}
+
+impl Generator for Square {
+ fn eval<'a>(&'a mut self, params: &Parameters) -> &'a SampleBuffer {
+ self.buf.rate = Rate::Sample;
+
+ let pvel = self.freq.eval(params).first() / params.env.sample_rate;
+ for i in 0..self.buf.len() {
+ self.buf[i] = if ((self.phase + pvel * (i as f32)) % 1.0) < 0.5 {
+ -1.0
+ } else {
+ 1.0
+ };
+ }
+
+ self.phase = (self.phase + pvel * (self.buf.len() as f32)) % 1.0;
+ &self.buf
+ }
+ fn set_buffer(&mut self, buf: SampleBuffer) -> SampleBuffer {
+ mem::replace(&mut self.buf, buf)
+ }
+}
diff --git a/src/synth/triangle.rs b/src/synth/triangle.rs
new file mode 100644
index 0000000..c8380a1
--- /dev/null
+++ b/src/synth/triangle.rs
@@ -0,0 +1,32 @@
+use super::*;
+
+#[derive(Debug)]
+pub struct Triangle {
+ pub freq: GenBox,
+ pub phase: f32,
+ pub buf: SampleBuffer,
+}
+
+impl Generator for Triangle {
+ fn eval<'a>(&'a mut self, params: &Parameters) -> &'a SampleBuffer {
+ self.buf.rate = Rate::Sample;
+
+ let pvel = self.freq.eval(params).first() / params.env.sample_rate;
+ for i in 0..self.buf.len() {
+ let ph = (self.phase + pvel * (i as f32)) % 1.0;
+ self.buf[i] = if ph < 0.25 {
+ 4.0 * ph
+ } else if ph > 0.75 {
+ 4.0 * ph - 4.0
+ } else {
+ -4.0 * ph + 2.0
+ };
+ }
+
+ self.phase = (self.phase + pvel * (self.buf.len() as f32)) % 1.0;
+ &self.buf
+ }
+ fn set_buffer(&mut self, buf: SampleBuffer) -> SampleBuffer {
+ mem::replace(&mut self.buf, buf)
+ }
+}
diff --git a/src/types.rs b/src/types.rs
index b9ff15f..dd450bb 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -1 +1,26 @@
pub type Sample = f32;
+
+pub enum Pitch {
+ Freq(f32),
+ MIDI(f32),
+}
+
+impl Pitch {
+ pub fn to_midi(&self) -> f32 {
+ match *self {
+ Pitch::MIDI(x) => x,
+ Pitch::Freq(x) =>
+ 12.0 * (x / 440.0).log2() + 69.0,
+ }
+ }
+ pub fn to_midi_pitch(&self) -> Pitch { Pitch::MIDI(self.to_midi()) }
+
+ pub fn to_freq(&self) -> f32 {
+ match *self {
+ Pitch::MIDI(x) =>
+ 440.0 * (2.0f32).powf((x - 69.0) / 12.0),
+ Pitch::Freq(x) => x,
+ }
+ }
+ pub fn to_freq_pitch(&self) -> Pitch { Pitch::Freq(self.to_freq()) }
+}