summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraham Northup <grissess@nexusg.org>2017-10-03 14:50:18 -0400
committerGraham Northup <grissess@nexusg.org>2017-10-03 14:50:18 -0400
commit22dd5bc5862e91330c8da0d5f141998cdbe546fb (patch)
tree253cb9f6b080caaad18b2df9f63004266bf73c27
parent145e2771c0d1ad30748da6e6ef1fabbd4cc2478c (diff)
Finished clientside parsing, starting graphics
-rw-r--r--Cargo.toml12
-rw-r--r--gens/basic_saw.gen1
-rw-r--r--gens/itlc_head.gen1
-rw-r--r--gens/itlc_tail.gen1
-rw-r--r--gens/test.gen12
-rw-r--r--src/graphics/draw/mod.rs3
-rw-r--r--src/graphics/mod.rs4
-rw-r--r--src/lang/parser.rs53
-rw-r--r--src/lang/tokenizer.rs121
-rw-r--r--src/lib.rs4
-rw-r--r--src/main.rs17
-rw-r--r--src/monitor.rs36
-rw-r--r--src/synth/mod.rs9
13 files changed, 247 insertions, 27 deletions
diff --git a/Cargo.toml b/Cargo.toml
index ded86ba..26b3d60 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,8 +3,20 @@ name = "synfone"
version = "0.1.0"
authors = ["Graham Northup <grissess@nexusg.org>"]
+[features]
+default = ['graphics']
+
+# The following feature will always refer ultimately to whatever backend
+# graphics library is chosen--which is subject to change.
+graphics = ['glium']
+
[dependencies]
byteorder = "1.1.0"
rand = "0.3"
unicode-xid = "0.1.0"
portaudio = "0.7.0"
+
+[dependencies.glium]
+version = "0.17.1"
+optional = true
+features = ["glutin"]
diff --git a/gens/basic_saw.gen b/gens/basic_saw.gen
new file mode 100644
index 0000000..45ebd41
--- /dev/null
+++ b/gens/basic_saw.gen
@@ -0,0 +1 @@
+#gens/itlc_head.gen# saw(param('v_freq', 500)) #gens/itlc_tail.gen#
diff --git a/gens/itlc_head.gen b/gens/itlc_head.gen
new file mode 100644
index 0000000..ecba4bc
--- /dev/null
+++ b/gens/itlc_head.gen
@@ -0,0 +1 @@
+mul(
diff --git a/gens/itlc_tail.gen b/gens/itlc_tail.gen
new file mode 100644
index 0000000..90297be
--- /dev/null
+++ b/gens/itlc_tail.gen
@@ -0,0 +1 @@
+, ifelse(rel(param('v_frame'), '<', param('v_deadline')), param('v_amp'), 0.0))
diff --git a/gens/test.gen b/gens/test.gen
new file mode 100644
index 0000000..164ba7a
--- /dev/null
+++ b/gens/test.gen
@@ -0,0 +1,12 @@
+[
+ #gens/basic_saw.gen#,
+ #gens/basic_saw.gen#,
+ #gens/basic_saw.gen#,
+ #gens/basic_saw.gen#,
+ #gens/basic_saw.gen#,
+ #gens/basic_saw.gen#,
+ #gens/basic_saw.gen#,
+ #gens/basic_saw.gen#,
+ #gens/basic_saw.gen#,
+ #gens/basic_saw.gen#
+]
diff --git a/src/graphics/draw/mod.rs b/src/graphics/draw/mod.rs
new file mode 100644
index 0000000..75ce9f7
--- /dev/null
+++ b/src/graphics/draw/mod.rs
@@ -0,0 +1,3 @@
+use super::*;
+
+/* TODO */
diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs
new file mode 100644
index 0000000..4bb4388
--- /dev/null
+++ b/src/graphics/mod.rs
@@ -0,0 +1,4 @@
+use super::*;
+use monitor::*;
+
+pub mod draw;
diff --git a/src/lang/parser.rs b/src/lang/parser.rs
index 7419a56..c49cf65 100644
--- a/src/lang/parser.rs
+++ b/src/lang/parser.rs
@@ -55,16 +55,18 @@ impl fmt::Display for ErrorType {
pub struct Parser<T: Iterator<Item=char>> {
tzr: Tokenizer<T>,
+ env: Environment,
token: Token,
pushback: Option<Token>,
factories: HashMap<String, &'static GeneratorFactory>,
}
impl<T: Iterator<Item=char>> Parser<T> {
- pub fn new(mut tzr: Tokenizer<T>) -> Result<Parser<T>, Box<Error>> {
+ pub fn new(mut tzr: Tokenizer<T>, env: Environment) -> Result<Parser<T>, Box<Error>> {
let token = tzr.next_token()?;
Ok(Parser {
tzr: tzr,
+ env: env,
token: token,
pushback: None,
factories: all_factories(),
@@ -105,15 +107,51 @@ impl<T: Iterator<Item=char>> Parser<T> {
}
}
- pub fn parse(&mut self) -> Result<GenBox, Box<Error>> {
- self.parse_gen()
+ pub fn parse_gen_vec(&mut self) -> Result<Vec<GenBox>, Box<Error>> {
+ let mut ret: Vec<GenBox> = Vec::new();
+ self.expect_op('[')?;
+
+
+ loop {
+ if self.expect_op(']').is_ok() {
+ break;
+ }
+
+ /* TODO: Can't yet clone a GenBox safely
+ let repeat = match self.token {
+ Token::Integer(v) => {
+ self.expect_op('*')?;
+ v
+ },
+ _ => 1,
+ };
+ */
+
+ ret.push(self.parse_gen()?);
+
+ if self.expect_op(',').is_err() {
+ self.expect_op(']')?;
+ break;
+ }
+ }
+
+ Ok(ret)
}
pub fn parse_gen(&mut self) -> Result<GenBox, Box<Error>> {
let name = self.expect_ident()?;
+ let mut params = self.parse_factory_params()?;
+ let factory = match self.factories.get(&name) {
+ Some(fac) => fac,
+ None => return Err(ErrorType::new(ErrorKind::UnknownGen(name)).into()),
+ };
+ factory.new(&mut params).map_err(Into::into)
+ }
+ pub fn parse_factory_params(&mut self) -> Result<FactoryParameters, Box<Error>> {
self.expect_op('(')?;
- let mut params: FactoryParameters = Default::default();
+
+ let mut params: FactoryParameters = FactoryParameters { env: self.env.clone(), ..Default::default() };
let mut ctr = 0;
loop {
if self.expect_op(')').is_ok() {
@@ -124,17 +162,12 @@ impl<T: Iterator<Item=char>> Parser<T> {
ctr = new_ctr;
if self.expect_op(',').is_err() {
- eprintln!("No comma: {:?}", self.token);
self.expect_op(')')?;
break;
}
}
- let factory = match self.factories.get(&name) {
- Some(fac) => fac,
- None => return Err(ErrorType::new(ErrorKind::UnknownGen(name)).into()),
- };
- factory.new(&mut params).map_err(Into::into)
+ Ok(params)
}
pub fn parse_param(&mut self, pos: usize) -> Result<(String, ParamValue, usize), Box<Error>> {
diff --git a/src/lang/tokenizer.rs b/src/lang/tokenizer.rs
index 74b304d..1d62f3e 100644
--- a/src/lang/tokenizer.rs
+++ b/src/lang/tokenizer.rs
@@ -1,6 +1,7 @@
use std::collections::HashMap;
use std::error::Error;
-use std::fmt;
+use std::{fmt, io, fs};
+use std::io::Read;
use super::*;
use unicode_xid::UnicodeXID;
@@ -13,6 +14,7 @@ pub struct Lexemes {
esc_oct: char,
com_outer: char,
com_inner: char,
+ include_delim: char,
escapes: HashMap<char, char>
}
@@ -27,6 +29,7 @@ impl Default for Lexemes {
esc_oct: 'o',
com_outer: '/',
com_inner: '*',
+ include_delim: '#',
escapes: HashMap::new(),
};
@@ -44,6 +47,7 @@ impl Default for Lexemes {
pub enum Location {
InString,
InStringEscape,
+ InInclude,
}
#[derive(Debug)]
@@ -64,6 +68,8 @@ pub enum ErrorKind {
BadEscapeValue(EscapeKind, String, Option<Box<Error>>),
BadNumericLiteral(NumericKind, String, Option<Box<Error>>),
UnknownChar(char),
+ IncludeError(io::Error),
+ TooManyRecursions(usize),
}
#[derive(Debug)]
@@ -83,6 +89,7 @@ impl ErrorType {
ErrorKind::UnexpectedEOF(ref loc) => format!("Unexpected EOF {}", match *loc {
Location::InString => "in string constant",
Location::InStringEscape => "in string escape",
+ Location::InInclude => "in include",
}),
ErrorKind::BadEscapeValue(ref kind, ref val, ref err) => format!("Bad {} escape {}: {:?}", match *kind {
EscapeKind::Hexadecimal => "hexadecimal",
@@ -93,6 +100,8 @@ impl ErrorType {
NumericKind::Float => "floating point",
}, val, err),
ErrorKind::UnknownChar(c) => format!("Unknown character {}", c),
+ ErrorKind::IncludeError(ref e) => format!("Error including file: {:?}", e),
+ ErrorKind::TooManyRecursions(n) => format!("Include recursed too many times ({})", n),
};
ret
@@ -117,7 +126,7 @@ impl Error for ErrorType {
Some(ref err) => Some(&**err),
None => None,
},
- ErrorKind::UnexpectedEOF(_) | ErrorKind::UnknownChar(_) => None,
+ _ => None,
}
}
}
@@ -134,16 +143,56 @@ fn char_in(s: &str, c: char) -> bool {
s.chars().find(|&x| x == c).map_or(false, |_| true)
}
+pub struct ResumableChars {
+ string: String,
+ pos: usize,
+}
+
+impl ResumableChars {
+ pub fn new(s: String) -> ResumableChars {
+ ResumableChars {
+ string: s,
+ pos: 0,
+ }
+ }
+}
+
+impl Iterator for ResumableChars {
+ type Item = char;
+
+ fn next(&mut self) -> Option<char> {
+ if self.pos >= self.string.len() {
+ None
+ } else {
+ let mut iter = self.string[self.pos..].char_indices();
+ match iter.next() {
+ Some((pos, ch)) => {
+ self.pos += match iter.next() {
+ Some((pos, _)) => pos,
+ None => self.string.len(),
+ };
+ Some(ch)
+ },
+ None => None,
+ }
+ }
+ }
+}
+
pub struct Tokenizer<T: Iterator<Item=char>> {
reader: T,
+ reader_stack: Vec<ResumableChars>,
pushback: Option<char>,
lexemes: Lexemes,
}
impl<T: Iterator<Item=char>> Tokenizer<T> {
+ const MAX_INCLUDE_RECURSIONS: usize = 256;
+
pub fn new(reader: T) -> Tokenizer<T> {
Tokenizer {
reader: reader,
+ reader_stack: Vec::new(),
pushback: None,
lexemes: Default::default(),
}
@@ -159,23 +208,49 @@ impl<T: Iterator<Item=char>> Tokenizer<T> {
}
}
+ pub fn push_reader(&mut self, rc: ResumableChars) -> Result<(), ErrorType> {
+ if self.reader_stack.len() > Self::MAX_INCLUDE_RECURSIONS {
+ Err(ErrorType::new(ErrorKind::TooManyRecursions(self.reader_stack.len())))
+ } else {
+ self.reader_stack.push(rc);
+ Ok(())
+ }
+ }
+
fn next_char(&mut self) -> Option<char> {
match self.pushback {
Some(c) => {
self.pushback = None;
Some(c)
},
- None => self.reader.next(),
+ None => {
+ let mut ret = None;
+ let mut produced_idx: usize = 0;
+ let len = self.reader_stack.len();
+
+ for (idx, rc) in self.reader_stack.iter_mut().enumerate().rev() {
+ match rc.next() {
+ Some(c) => {
+ ret = Some(c);
+ produced_idx = idx;
+ break;
+ },
+ None => {},
+ }
+ }
+
+ match ret {
+ Some(c) => {
+ self.reader_stack.truncate(produced_idx + 1);
+ Some(c)
+ },
+ None => self.reader.next(),
+ }
+ },
}
}
pub fn next_token(&mut self) -> Result<Token, ErrorType> {
- let res = self._next_token();
- eprintln!("next_token: {:?}", res);
- res
- }
-
- fn _next_token(&mut self) -> Result<Token, ErrorType> {
let mut c = self.next_char();
if c == None {
return Ok(Token::EOF);
@@ -216,6 +291,34 @@ impl<T: Iterator<Item=char>> Tokenizer<T> {
}
}
+ /* Inclusion */
+ if cc == self.lexemes.include_delim {
+ let mut buffer = String::new();
+
+ loop {
+ let nc = self.next_char();
+ if nc == None {
+ return Err(ErrorType::new(ErrorKind::UnexpectedEOF(Location::InInclude)));
+ }
+ let ncc = nc.unwrap();
+
+ if ncc == self.lexemes.include_delim {
+ break;
+ } else {
+ buffer.push(ncc);
+ }
+ }
+
+ let mut f = match fs::File::open(buffer) {
+ Err(err) => return Err(ErrorType::new(ErrorKind::IncludeError(err))),
+ Ok(f) => f,
+ };
+ let mut contents = String::new();
+ f.read_to_string(&mut contents);
+ self.push_reader(ResumableChars::new(contents))?;
+ return self.next_token()
+ }
+
/* Strings */
if char_in(&self.lexemes.string_delim, cc) {
let mut buffer = String::new();
diff --git a/src/lib.rs b/src/lib.rs
index db8a5bf..4e1c372 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,6 +11,10 @@ pub mod synth;
pub mod proto;
pub mod lang;
pub mod client;
+pub mod monitor;
+
+#[cfg(feature = "graphics")]
+pub mod graphics;
#[cfg(test)]
mod tests {
diff --git a/src/main.rs b/src/main.rs
index 9b78146..fb98dfb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,6 @@
-use std::io;
+use std::{io, env};
use std::io::*;
+use std::fs::*;
use std::net::*;
use std::sync::*;
use std::collections::VecDeque;
@@ -13,18 +14,18 @@ use synfone::lang::*;
use synfone::proto::*;
use synfone::client::*;
-const GEN: &'static str = "mul(saw(param('v_freq', 500)), ifelse(rel(param('v_frame'), '<', param('v_deadline')), param('v_amp'), 0.0))";
-
fn main() {
let env = Environment::default();
- let mut gens = Vec::new();
- for _i in 0..25 {
- let gen = Parser::new(Tokenizer::new(GEN.chars())).expect("Failed to get first token").parse().expect("Failed to compile generator");
- gens.push(gen);
- }
+ let mut genfile = File::open(env::args_os().nth(1).expect("Need first argument to be a file with a generator vector")).expect("Failed to open file");
+ let mut genstr = String::new();
+ genfile.read_to_string(&mut genstr);
+
+ let gens = Parser::new(Tokenizer::new(genstr.chars()), env.clone()).expect("Failed to get first token").parse_gen_vec().expect("Failed to compile generators");
let sock = UdpSocket::bind("0.0.0.0:13676").expect("Failed to bind socket");
+ eprintln!("Parsed {} generator definitions", gens.len());
+
let mut client = Arc::new(Mutex::new(Client::new(sock.try_clone().expect("Failed to clone socket"), gens, env.clone()).expect("Failed to create client")));
let pa_inst = pa::PortAudio::new().expect("Failed to create PortAudio interface");
diff --git a/src/monitor.rs b/src/monitor.rs
new file mode 100644
index 0000000..bef3fbc
--- /dev/null
+++ b/src/monitor.rs
@@ -0,0 +1,36 @@
+use super::*;
+use synth::SampleBuffer;
+use std::collections::HashMap;
+
+pub struct VoiceDatum {
+ pitch: Pitch,
+ ampl: f32,
+}
+
+pub enum Datum {
+ Voices(Vec<VoiceDatum>),
+ Samples(SampleBuffer),
+ Playtime(f32, f32),
+}
+
+pub enum DatumKind {
+ Voices,
+ Samples,
+ Playtime,
+}
+
+impl<'a> From<&'a Datum> for DatumKind {
+ fn from(d: &'a Datum) -> DatumKind {
+ match *d {
+ Datum::Voices(_) => DatumKind::Voices,
+ Datum::Samples(_) => DatumKind::Samples,
+ Datum::Playtime(_, _) => DatumKind::Playtime,
+ }
+ }
+}
+
+pub type Data = HashMap<DatumKind, Datum>;
+
+pub trait Monitor {
+ fn process(&mut self, data: &Data);
+}
diff --git a/src/synth/mod.rs b/src/synth/mod.rs
index 60a7372..d48577c 100644
--- a/src/synth/mod.rs
+++ b/src/synth/mod.rs
@@ -163,6 +163,15 @@ impl IndexMut<usize> for SampleBuffer {
fn index_mut(&mut self, idx: usize) -> &mut Sample { &mut self.samples[idx] }
}
+impl Clone for SampleBuffer {
+ fn clone(&self) -> SampleBuffer {
+ SampleBuffer {
+ samples: self.samples.clone(),
+ rate: self.rate,
+ }
+ }
+}
+
pub trait Generator : Debug {
fn eval<'a>(&'a mut self, params: &Parameters) -> &'a SampleBuffer;
fn buffer(&self) -> &SampleBuffer;