diff options
author | Graham Northup <grissess@nexusg.org> | 2017-10-03 14:50:18 -0400 |
---|---|---|
committer | Graham Northup <grissess@nexusg.org> | 2017-10-03 14:50:18 -0400 |
commit | 22dd5bc5862e91330c8da0d5f141998cdbe546fb (patch) | |
tree | 253cb9f6b080caaad18b2df9f63004266bf73c27 | |
parent | 145e2771c0d1ad30748da6e6ef1fabbd4cc2478c (diff) |
Finished clientside parsing, starting graphics
-rw-r--r-- | Cargo.toml | 12 | ||||
-rw-r--r-- | gens/basic_saw.gen | 1 | ||||
-rw-r--r-- | gens/itlc_head.gen | 1 | ||||
-rw-r--r-- | gens/itlc_tail.gen | 1 | ||||
-rw-r--r-- | gens/test.gen | 12 | ||||
-rw-r--r-- | src/graphics/draw/mod.rs | 3 | ||||
-rw-r--r-- | src/graphics/mod.rs | 4 | ||||
-rw-r--r-- | src/lang/parser.rs | 53 | ||||
-rw-r--r-- | src/lang/tokenizer.rs | 121 | ||||
-rw-r--r-- | src/lib.rs | 4 | ||||
-rw-r--r-- | src/main.rs | 17 | ||||
-rw-r--r-- | src/monitor.rs | 36 | ||||
-rw-r--r-- | src/synth/mod.rs | 9 |
13 files changed, 247 insertions, 27 deletions
@@ -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(); @@ -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; |