Browse Source

Merge branch 'work' into nogfx

Graham Northup 6 năm trước cách đây
mục cha
commit
5c39a9c26d
12 tập tin đã thay đổi với 459 bổ sung5 xóa
  1. 2 0
      Cargo.toml
  2. 10 0
      gens/test.gen
  3. 1 1
      gens/test_voice.gen
  4. 11 1
      src/client.rs
  5. 3 0
      src/lib.rs
  6. 17 3
      src/main.rs
  7. 17 0
      src/proto.rs
  8. 112 0
      src/seq/file/iv.rs
  9. 4 0
      src/seq/file/mod.rs
  10. 222 0
      src/seq/mod.rs
  11. 59 0
      src/seq/sequencer.rs
  12. 1 0
      src/types.rs

+ 2 - 0
Cargo.toml

@@ -19,6 +19,8 @@ byteorder = "1.1.0"
 rand = "0.3"
 unicode-xid = "0.1.0"
 portaudio = "0.7.0"
+xml-rs = "0.8.0"
+failure = "0.1"
 
 #[dependencies.glium]
 ##version = "0.17.1"

+ 10 - 0
gens/test.gen

@@ -19,5 +19,15 @@
 	#gens/test_basic.gen#,
 	#gens/test_basic.gen#,
 	#gens/test_basic.gen#,
+	#gens/test_basic.gen#,
+	#gens/test_basic.gen#,
+	#gens/test_basic.gen#,
+	#gens/test_basic.gen#,
+	#gens/test_basic.gen#,
+	#gens/test_basic.gen#,
+	#gens/test_basic.gen#,
+	#gens/test_basic.gen#,
+	#gens/test_basic.gen#,
+	#gens/test_basic.gen#,
 	#gens/test_basic.gen#
 ]

+ 1 - 1
gens/test_voice.gen

@@ -1 +1 @@
-(lutgen(sine(lut_freq), 128, v_freq) * controlrate(dahdsr(v_frame < v_deadline, 0, 4 / samplerate(), 0, 1, 1, 8 / samplerate()))) * v_amp
+(lutgen(saw(lut_freq), 128, v_freq) * dahdsr(v_frame < v_deadline, 0, 8 / samplerate(), 0, 0, 1, 4 / samplerate())) * v_amp

+ 11 - 1
src/client.rs

@@ -1,5 +1,5 @@
 use std::net::{UdpSocket, SocketAddr};
-use std::{io, mem};
+use std::{io, mem, iter};
 
 use synth::*;
 use proto::*;
@@ -111,6 +111,16 @@ impl Client {
                 self.socket.send_to(&reply_buffer, sender);
             },
             Command::PCM{..} => { /* TODO */ },
+            Command::PCMSyn{..} => { /* TODO */},
+            Command::ArtParam{voice, index, value} => {
+                dprintln!("Articulation parameter voice {:?} index {} value {}", voice, index, value);
+                for vidx in match voice {
+                    Some(vidx) => ((vidx as usize)..((vidx+1) as usize)),
+                    None => (0..self.voices.len()),
+                } {
+                    *self.voices[vidx].params.vars.entry(format!("artp{}", index)).or_insert_with(Default::default) = value;
+                }
+            },
             Command::Unknown{data} => {
                 dprintln!("Dropping packet: unknown data {:?}", (&data as &[u8]));
             },

+ 3 - 0
src/lib.rs

@@ -1,6 +1,8 @@
 extern crate byteorder;
 extern crate rand;
 extern crate unicode_xid;
+extern crate xml;
+#[macro_use] extern crate failure;
 
 pub mod types;
 pub use types::*;
@@ -10,6 +12,7 @@ pub mod proto;
 pub mod lang;
 pub mod client;
 pub mod monitor;
+pub mod seq;
 
 #[cfg(feature = "graphics")]
 pub mod graphics;

+ 17 - 3
src/main.rs

@@ -1,4 +1,4 @@
-use std::{io, env, thread, iter, time, mem};
+use std::{io, env, thread, iter, time, mem, ffi};
 use std::io::*;
 use std::fs::*;
 use std::net::*;
@@ -20,12 +20,26 @@ use synfone::lang::*;
 use synfone::proto::*;
 use synfone::client::*;
 
+fn main() {
+    let cmd = env::args_os().nth(1).expect("Please pass a command as the first argument; use `help` as a command for more information.");
+    let cmds = cmd.into_string().expect("Couldn't parse command");
+
+    let new_args: Vec<ffi::OsString> = env::args_os().skip(1).collect();
+
+    match &*cmds {
+        "help" => eprintln!("TODO! Commands are help, client."),
+        "client" => main_client(new_args),
+        _ => eprintln!("Unknown command; `help` for help."),
+    }
+}
+
+
 const GFX: bool = false;
 
-fn main() {
+fn main_client(args: Vec<ffi::OsString>) {
     let env = Environment::default();
 
-    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 genfile = File::open(args.iter().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);
 

+ 17 - 0
src/proto.rs

@@ -14,6 +14,8 @@ pub enum Command {
     Play{sec: u32, usec: u32, freq: u32, amp: f32, voice: u32},
     Caps{voices: u32, tp: [u8; 4], ident: [u8; 24]},
     PCM{samples: [i16; 16]},
+    PCMSyn{buffered: u32},
+    ArtParam{voice: Option<u32>, index: u32, value: f32},
     Unknown{data: [u8; Command::SIZE]},
 }
 
@@ -60,6 +62,13 @@ impl Command {
                 NetworkEndian::write_u32(&mut ret[..4], 5);
                 NetworkEndian::write_i16_into(&samples, &mut ret[4..]);
             },
+            Command::PCMSyn{buffered} => {
+                NetworkEndian::write_u32_into(&[6u32, buffered], &mut ret[..8]);
+            },
+            Command::ArtParam{voice, index, value} => {
+                NetworkEndian::write_u32_into(&[7u32, voice.unwrap_or(OBLIGATE_POLYPHONE), index], &mut ret[..12]);
+                NetworkEndian::write_f32(&mut ret[12..16], value);
+            },
             Command::Unknown{data} => {
                 ret.copy_from_slice(&data);
             },
@@ -79,6 +88,8 @@ impl fmt::Debug for Command {
             Command::Play{sec, usec, freq, amp, voice} => f.debug_struct("Play").field("sec", &sec).field("usec", &usec).field("freq", &freq).field("amp", &amp).field("voice", &voice).finish(),
             Command::Caps{voices, tp, ident} => f.debug_struct("Caps").field("voices", &voices).field("tp", &tp).field("ident", &ident).finish(),
             Command::PCM{samples} => f.debug_struct("PCM").field("samples", &samples).finish(),
+            Command::PCMSyn{buffered} => f.debug_struct("PCMSyn").field("buffered", &buffered).finish(),
+            Command::ArtParam{voice, index, value} => f.debug_struct("ArtParam").field("voice", &voice).field("index", &index).field("value", &value).finish(),
             Command::Unknown{data} => f.debug_struct("Unknown").field("data", &(&data as &[u8])).finish(),
         }
     }
@@ -122,6 +133,12 @@ impl<'a> From<&'a [u8; Command::SIZE]> for Command {
                 ::byteorder::LittleEndian::read_i16_into(&packet[4..], &mut samples);
                 Command::PCM{samples: samples}
             },
+            6 => Command::PCMSyn{buffered: fields_u32[1]},
+            7 => Command::ArtParam {
+                voice: if fields_u32[1] == OBLIGATE_POLYPHONE { None } else { Some(fields_u32[1]) },
+                index: fields_u32[2],
+                value: fields_f32[3],
+            },
             _ => {
                 let mut data: [u8; Command::SIZE] = unsafe { mem::uninitialized() };
                 data.copy_from_slice(packet);

+ 112 - 0
src/seq/file/iv.rs

@@ -0,0 +1,112 @@
+use std::io;
+use std::collections::HashMap;
+use std::borrow::Borrow;
+
+use xml::reader;
+use xml::reader::{EventReader, XmlEvent};
+use xml::attribute::OwnedAttribute;
+use std::hash::Hash;
+use std::cmp::Eq;
+use std::str::FromStr;
+use std::fmt::Display;
+use failure::Error;
+use super::*;
+
+struct AttrMapping(HashMap<String, String>);
+
+impl AttrMapping {
+    pub fn make(attrs: Vec<OwnedAttribute>) -> AttrMapping {
+        let mut output = HashMap::new();
+
+        for attr in attrs {
+            output.insert(attr.name.local_name.clone(), attr.value.clone());
+        }
+
+        AttrMapping(output)
+    }
+
+    pub fn get_str<'a, 'b, 'c: 'a, Q: Hash+Eq+Display+?Sized>(&'a self, key: &'b Q, default: &'c str) -> &'a str where String: Borrow<Q> {
+        self.0.get(key).map(|x| &**x).unwrap_or(default)
+    }
+
+    pub fn req<V: FromStr, Q: Hash+Eq+Display+?Sized>(&self, key: &Q) -> Result<V, Error> where String: Borrow<Q>, V::Err: failure::Fail {
+        match self.0.get(key){ 
+            Some(x) => Ok(x.parse()?),
+            None => bail!("{} not found in attrs", key)
+        }
+    }
+
+    pub fn req_midi_pitch<Q: Hash+Eq+Display+?Sized>(&self, key: &Q) -> Result<Pitch, Error> where String: Borrow<Q> {
+        Ok(Pitch::MIDI(self.req::<f32, Q>(key)?))
+    }
+}
+
+fn parse_note(ev: XmlEvent, into: &mut Vec<Note>) -> Result<bool, Error> {
+    match ev {
+        XmlEvent::StartElement{name, attributes, ..} => {
+            if name.local_name.as_ref() != "note" { bail!("malformed iv: non-note attr in note stream"); }
+            let attrs = AttrMapping::make(attributes);
+            into.push(Note {
+                time: attrs.req("time")?,
+                ampl: attrs.req("ampl")?,
+                dur: attrs.req("dur")?,
+                pitch: attrs.req_midi_pitch("pitch")?,
+                start_tick: None,
+                dur_ticks: None
+            });
+            Ok(false)
+        },
+        _ => Ok(true)
+    }
+}
+
+pub fn read<R: io::Read>(source: R) -> Result<IV, Error> {
+    let mut output: IV = Default::default();
+    let mut event_reader = EventReader::new(source);
+
+    #[derive(Debug)]
+    enum ReadState<'a> {
+        Idle,
+        InStreams,
+        InBPMs,
+        InNoteStream(&'a mut NoteStream),
+        InAuxStream(&'a mut AuxStream),
+    }
+
+    let mut state = ReadState::Idle;
+
+    loop {
+        match event_reader.next()? {
+            XmlEvent::StartElement{name, attributes, ..} => {
+                let attrmap = AttrMapping::make(attributes);
+
+                match name.local_name.as_ref() {
+                    "bpms" => { }
+                    "streams" => {
+                        match attrmap.get_str("type", "") {
+                            "ns" => {
+                                let mut notes = Vec::new();
+
+                                loop {
+                                    if !parse_note(event_reader.next()?, &mut notes)? { break; }
+                                }
+
+                            },
+                            _ => unimplemented!()
+                        }
+                    },
+                    _ => unimplemented!()
+                }
+            }
+            XmlEvent::EndElement{name} => match (name.local_name.as_ref(), &state) {
+                ("bpms", _) => { state = ReadState::Idle; },
+                ("streams", _) => { state = ReadState::Idle; },
+                _ => (),
+            },
+            _ => (),
+        }
+    }
+        
+
+    Ok(output)
+}

+ 4 - 0
src/seq/file/mod.rs

@@ -0,0 +1,4 @@
+//pub mod iv;
+//pub mod midi;
+
+use super::*;

+ 222 - 0
src/seq/mod.rs

@@ -0,0 +1,222 @@
+pub mod sequencer;
+pub use self::sequencer::*;
+pub mod file;
+
+use std::{cmp, iter, ops};
+use std::collections::{hash_map, HashMap};
+
+use super::*;
+
+#[derive(Debug,Clone,Copy,PartialEq)]
+pub struct Seconds(pub f32);
+
+impl Eq for Seconds {}
+
+impl PartialOrd for Seconds {
+    fn partial_cmp(&self, other: &Seconds) -> Option<cmp::Ordering> {
+        self.0.partial_cmp(&other.0)
+    }
+}
+
+impl Ord for Seconds {
+    fn cmp(&self, other: &Seconds) -> cmp::Ordering {
+        self.partial_cmp(other).expect("Encountered NaN Seconds")
+    }
+}
+
+impl ops::Add for Seconds {
+    type Output = Seconds;
+    fn add(self, rhs: Seconds) -> Seconds { Seconds(self.0 + rhs.0) }
+}
+
+impl ops::Sub for Seconds {
+    type Output = Seconds;
+    fn sub(self, rhs: Seconds) -> Seconds { Seconds(self.0 - rhs.0) }
+}
+
+impl<RHS> ops::Mul<RHS> for Seconds where f32: ops::Mul<RHS, Output=f32> {
+    type Output = Seconds;
+    fn mul(self, rhs: RHS) -> Seconds { Seconds(self.0.mul(rhs)) }
+}
+
+impl<RHS> ops::Div<RHS> for Seconds where f32: ops::Div<RHS, Output=f32> {
+    type Output = Seconds;
+    fn div(self, rhs: RHS) -> Seconds { Seconds(self.0.div(rhs)) }
+}
+
+pub type Ticks = u64;
+
+#[derive(Debug,Clone)]
+pub enum Time {
+    Seconds(Seconds),
+    Ticks(Ticks),
+}
+
+impl From<Seconds> for Time {
+    fn from(s: Seconds) -> Time {
+        Time::Seconds(s)
+    }
+}
+
+impl From<Ticks> for Time {
+    fn from(t: Ticks) -> Time {
+        Time::Ticks(t)
+    }
+}
+
+#[derive(Debug,Clone,Copy,PartialEq)]
+pub struct BPM(pub f32);
+
+impl Eq for BPM {}
+
+impl PartialOrd for BPM {
+    fn partial_cmp(&self, other: &BPM) -> Option<cmp::Ordering> {
+        self.0.partial_cmp(&other.0)
+    }
+}
+
+impl Ord for BPM {
+    fn cmp(&self, other: &BPM) -> cmp::Ordering {
+        self.partial_cmp(other).expect("Encountered NaN BPM")
+    }
+}
+
+impl<RHS> ops::Mul<RHS> for BPM where f32: ops::Mul<RHS, Output=f32> {
+    type Output = BPM;
+    fn mul(self, rhs: RHS) -> BPM { BPM(self.0.mul(rhs)) }
+}
+impl<RHS> ops::Div<RHS> for BPM where f32: ops::Div<RHS, Output=f32> {
+    type Output = BPM;
+    fn div(self, rhs: RHS) -> BPM { BPM(self.0.div(rhs)) }
+}
+
+#[derive(Debug,Clone)]
+pub struct Note {
+    pub time: Seconds,
+    pub dur: Seconds,
+    pub start_tick: Option<Ticks>,
+    pub dur_ticks: Option<Ticks>,
+    pub ampl: f32,
+    pub pitch: Pitch,
+}
+
+#[derive(Debug,Clone)]
+pub struct Aux {
+    pub time: Seconds,
+    pub data: String,
+}
+
+pub type NoteStream = Vec<Note>;
+pub type AuxStream = Vec<Aux>;
+
+#[derive(Debug,Clone)]
+pub enum Stream {
+    Note(NoteStream),
+    Aux(AuxStream),
+}
+
+impl Stream {
+    pub fn note_stream(&self) -> Option<&NoteStream> {
+        match self {
+            &Stream::Note(ref ns) => Some(ns),
+            _ => None,
+        }
+    }
+}
+
+pub type Group = Vec<NoteStream>;
+
+#[derive(Debug,Clone,Copy)]
+pub struct BPMEntry {
+    pub abstick: Ticks,
+    pub bpm: BPM,
+    pub realtime: Option<Seconds>,
+}
+
+#[derive(Debug,Clone)]
+pub struct BPMTableInput {
+    pub entries: Vec<BPMEntry>,
+    pub resolution: f32,
+}
+
+impl From<BPMTableInput> for BPMTable {
+    fn from(input: BPMTableInput) -> BPMTable {
+        let mut range = input.entries.clone();
+        range.sort_unstable_by_key(|&ent| ent.abstick);
+        for ent in range.iter_mut() {
+            ent.realtime = Some(Seconds(0.0));
+        }
+        for idx in 1 .. (range.len() - 1) {
+            let tick = range[idx].abstick;
+            let BPMEntry {abstick: ptick, bpm: pbpm, realtime: ptm} = range[idx - 1];
+            range[idx].realtime = Some(ptm.unwrap() + Seconds((60.0 * ((tick - ptick) as f32)) / (pbpm * input.resolution).0));
+        }
+        BPMTable {
+            range: range,
+            resolution: input.resolution,
+        }
+    }
+}
+
+pub struct BPMTable {
+    range: Vec<BPMEntry>,
+    resolution: f32,
+}
+
+impl BPMTable {
+    pub fn to_seconds(&self, tm: Time) -> Seconds {
+        match tm {
+            Time::Seconds(s) => s,
+            Time::Ticks(t) => match self.range.binary_search_by_key(&t, |&ent| ent.abstick) {
+                Ok(idx) => self.range[idx].realtime.unwrap(),
+                Err(idx) => {
+                    let effidx = cmp::max(0, idx - 1);
+                    let BPMEntry {abstick: tick, bpm, realtime: sec} = self.range[effidx];
+                    sec.unwrap() + Seconds((60.0 * ((t - tick) as f32)) / (bpm * self.resolution).0)
+                },
+            },
+        }
+    }
+
+    pub fn to_seconds_time(&self, tm: Time) -> Time {
+        Time::Seconds(self.to_seconds(tm))
+    }
+
+    pub fn to_ticks(&self, tm: Time) -> Ticks {
+        match tm {
+            Time::Ticks(t) => t,
+            Time::Seconds(s) => match self.range.binary_search_by_key(&s, |&ent| ent.realtime.unwrap()) {
+                Ok(idx) => self.range[idx].abstick,
+                Err(idx) => {
+                    let effidx = cmp::max(0, idx - 1);
+                    let BPMEntry {abstick: tick, bpm, realtime: sec} = self.range[effidx];
+                    tick + ((((s - sec.unwrap()).0 * bpm.0 * self.resolution) / 60.0) as Ticks)
+                },
+            },
+        }
+    }
+
+    pub fn to_ticks_time(&self, tm: Time) -> Time {
+        Time::Ticks(self.to_ticks(tm))
+    }
+}
+
+#[derive(Default)]
+pub struct IVMeta {
+    pub bpms: Option<BPMTable>,
+    pub args: Option<String>,
+    pub app: Option<String>,
+}
+
+#[derive(Default)]
+pub struct IV {
+    pub default_group: Group,
+    pub groups: HashMap<String, Group>,
+    pub meta: IVMeta,
+}
+
+impl IV {
+    fn iter_streams(&self) -> impl Iterator<Item=&NoteStream> {
+        self.groups.values().chain(iter::once(&self.default_group)).flat_map(|x| x.iter())
+    }
+}

+ 59 - 0
src/seq/sequencer.rs

@@ -0,0 +1,59 @@
+use super::*;
+
+pub fn coalesce<'a, I: Iterator<Item=&'a NoteStream>>(stream_iter: I) -> NoteStream {
+    let mut output = NoteStream::new();
+
+    for ns in stream_iter {
+        output.extend(ns.iter().cloned());
+    }
+
+    output
+}
+
+pub struct SchedParams {
+    pub epsilon: f32,
+}
+
+impl Default for SchedParams {
+    fn default() -> SchedParams {
+        SchedParams {
+            epsilon: 0.0,
+        }
+    }
+}
+
+pub fn schedule<'a, 'b: 'a, I: Iterator<Item=&'a Note>, F: FnMut(&'a Note) -> Option<&'b str>>(notes: I, mut classifier: F, params: &SchedParams) -> IV {
+    let mut output: IV = Default::default();
+
+    for note in notes {
+        let grp_name = classifier(note);
+        let grp = if let Some(name) = grp_name {
+            if !output.groups.contains_key(name) {
+                output.groups.insert(name.into(), Vec::new());
+            }
+            output.groups.get_mut(name).unwrap()
+        } else {
+            &mut output.default_group
+        };
+
+        let mut found: Option<usize> = None;
+        for (idx, ns) in grp.iter().enumerate() {
+            if ns.len() > 0 {
+                let nt = &ns[ns.len() - 1];
+                if note.time.0 < nt.time.0 + nt.dur.0 + params.epsilon {
+                    continue
+                }
+            }
+            found = Some(idx);
+            break;
+        }
+
+        if let Some(nidx) = found {
+            grp[nidx].push(note.clone());
+        } else {
+            grp.push(vec![note.clone()]);
+        }
+    }
+
+    output
+}

+ 1 - 0
src/types.rs

@@ -1,5 +1,6 @@
 pub type Sample = f32;
 
+#[derive(Debug,Clone)]
 pub enum Pitch {
     Freq(f32),
     MIDI(f32),