소스 검색

Partial commit toward IV/seq implementation!

Graham Northup 7 년 전
부모
커밋
a2d24ef36d
8개의 변경된 파일378개의 추가작업 그리고 3개의 파일을 삭제
  1. 1 0
      Cargo.toml
  2. 2 0
      src/lib.rs
  3. 17 3
      src/main.rs
  4. 72 0
      src/seq/file/iv.rs
  5. 4 0
      src/seq/file/mod.rs
  6. 222 0
      src/seq/mod.rs
  7. 59 0
      src/seq/sequencer.rs
  8. 1 0
      src/types.rs

+ 1 - 0
Cargo.toml

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

+ 2 - 0
src/lib.rs

@@ -3,6 +3,7 @@
 extern crate byteorder;
 extern crate rand;
 extern crate unicode_xid;
+extern crate xml;
 
 pub mod types;
 pub use types::*;
@@ -12,6 +13,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);
 

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

@@ -0,0 +1,72 @@
+use std::io;
+use std::collections::HashMap;
+use std::borrow::Borrow;
+
+use xml::reader;
+use xml::reader::{EventReader, XmlEvent};
+use xml::attribute::OwnedAttribute;
+
+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>(&'a self, key: &'b Q, default: &'c str) -> &'a str where String: Borrow<Q> {
+        self.0.get(key).or(default)
+    }
+}
+
+pub fn read<R: io::Read>(source: R) -> reader::Result<IV> {
+    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;
+
+    for ev in event_reader {
+        match ev? {
+            XmlEvent::StartElement{name, attributes, ..} => {
+                let attrmap = AttrMapping::make(attributes);
+
+                match (name.local_name.as_ref(), &state) {
+                    ("bpms", &ReadState::Idle) => { state = ReadState::InBPMs; },
+                    ("bpm", &ReadState::InBPMs) => {
+                        let entry = BPMEntry {
+                            abstick: 0,
+                            bpm: BPM(0.0),
+                            realtime: Some(Seconds(0.0)),
+                        };
+                    },
+                    ("streams", &ReadState::Idle) => { state = ReadState::InStreams; },
+                    _ => (),
+                }
+            },
+            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),