瀏覽代碼

It plays network noises now!

Graham Northup 8 年之前
父節點
當前提交
145e2771c0
共有 5 個文件被更改,包括 142 次插入52 次删除
  1. 1 0
      Cargo.toml
  2. 72 44
      src/client.rs
  3. 49 7
      src/main.rs
  4. 16 1
      src/proto.rs
  5. 4 0
      src/synth/mod.rs

+ 1 - 0
Cargo.toml

@@ -7,3 +7,4 @@ authors = ["Graham Northup <grissess@nexusg.org>"]
 byteorder = "1.1.0"
 rand = "0.3"
 unicode-xid = "0.1.0"
+portaudio = "0.7.0"

+ 72 - 44
src/client.rs

@@ -1,4 +1,4 @@
-use std::net::{UdpSocket};
+use std::net::{UdpSocket, SocketAddr};
 use std::{io, mem};
 
 use synth::*;
@@ -15,6 +15,7 @@ pub struct Client {
     pub env: Environment,
     pub frames: usize,
     pub buf: SampleBuffer,
+    norm: SampleBuffer
 }
 
 macro_rules! dprintln {
@@ -23,7 +24,6 @@ macro_rules! dprintln {
 
 impl Client {
     pub fn new(socket: UdpSocket, gens: Vec<GenBox>, env: Environment) -> io::Result<Client> {
-        socket.set_nonblocking(true)?;
         let buf = SampleBuffer::new(env.default_buffer_size);
         let voices = gens.into_iter().map(|g| Voice { gen: g, params: Parameters { env: env.clone(), ..Default::default() } }).collect();
         Ok(Client {
@@ -32,10 +32,13 @@ impl Client {
             env: env,
             frames: 0,
             buf: buf,
+            norm: SampleBuffer::new(1),
         })
     }
 
-    pub fn pump(&mut self, out_buffer: &mut Vec<u8>) -> bool {
+    // NB: Loops indefinitely (until timeout, quit, or error) iff self.socket blocks
+    /*
+    pub fn process_packets(&mut self) -> bool {
         if self.voices.len() == 0 {
             return false;
         }
@@ -51,45 +54,8 @@ impl Client {
                     }
 
                     let cmd = Command::from(&buffer);
-                    dprintln!("Packet {:?} from {:?}", (&buffer as &[u8]), sender);
-                    match cmd {
-                        Command::KeepAlive => {},
-                        Command::Ping{..} => {
-                            self.socket.send_to(&buffer, sender);
-                        },
-                        Command::Quit => {
-                            return false;
-                        },
-                        Command::Play{voice, freq, amp, ..} => {
-                            if (voice as usize) >= self.voices.len() {
-                                dprintln!("Dropping packet: tried to send to voice {} >= number of voices {}", voice, self.voices.len());
-                                continue;
-                            }
-                            let dur = cmd.duration().unwrap();
-                            let frac_secs = (dur.as_secs() as f32) + (dur.subsec_nanos() as f32) / 1.0e9;
-                            let frames = frac_secs * (self.env.sample_rate as f32);
-
-                            dprintln!("Playing on voice {} freq {} amp {} from frame {} until frame {}", voice, freq, amp, self.frames, (self.frames as f32) + frames);
-
-                            let mut vars = &mut self.voices[voice as usize].params.vars;
-                            *vars.entry("v_deadline".to_string()).or_insert_with(Default::default) = (self.frames as f32) + frames;
-                            *vars.entry("v_freq".to_string()).or_insert_with(Default::default) = freq as f32;
-                            *vars.entry("v_amp".to_string()).or_insert_with(Default::default) = amp;
-                        },
-                        Command::Caps{..} => {
-                            let reply = Command::Caps {
-                                voices: self.voices.len() as u32,
-                                tp: ['S' as u8, 'Y' as u8, 'N' as u8, 'F' as u8],
-                                ident: [0u8; 24],
-                            };
-                            let mut reply_buffer: [u8; Command::SIZE] = [0u8; Command::SIZE];
-                            reply.write_into(&mut reply_buffer);
-                            self.socket.send_to(&reply_buffer, sender);
-                        },
-                        Command::PCM{..} => { /* TODO */ },
-                        Command::Unknown{data} => {
-                            dprintln!("Dropping packet: unknown data {:?}", (&data as &[u8]));
-                        },
+                    if !self.handle_command(cmd, sender) {
+                        return false;
                     }
                 },
                 Err(err) => {
@@ -101,6 +67,60 @@ impl Client {
             }
         }
 
+        true
+    }
+    */
+
+    pub fn handle_command(&mut self, cmd: Command, sender: SocketAddr) -> bool {
+        dprintln!("Packet {:?} from {:?}", cmd, sender);
+        match cmd {
+            Command::KeepAlive => {},
+            Command::Ping{..} => {
+                let mut reply_buffer: [u8; Command::SIZE] = [0u8; Command::SIZE];
+                cmd.write_into(&mut reply_buffer);
+                self.socket.send_to(&reply_buffer, sender);
+            },
+            Command::Quit => {
+                return false;
+            },
+            Command::Play{voice, freq, amp, ..} => {
+                if (voice as usize) >= self.voices.len() {
+                    dprintln!("Dropping packet: tried to send to voice {} >= number of voices {}", voice, self.voices.len());
+                    return true;
+                }
+                let dur = cmd.duration().unwrap();
+                let frac_secs = (dur.as_secs() as f32) + (dur.subsec_nanos() as f32) / 1.0e9;
+                let frames = frac_secs * (self.env.sample_rate as f32);
+
+                dprintln!("Playing on voice {} freq {} amp {} from frame {} until frame {}", voice, freq, amp, self.frames, (self.frames as f32) + frames);
+
+                let mut vars = &mut self.voices[voice as usize].params.vars;
+                *vars.entry("v_deadline".to_string()).or_insert_with(Default::default) = (self.frames as f32) + frames;
+                *vars.entry("v_freq".to_string()).or_insert_with(Default::default) = freq as f32;
+                *vars.entry("v_amp".to_string()).or_insert_with(Default::default) = amp;
+            },
+            Command::Caps{..} => {
+                let reply = Command::Caps {
+                    voices: self.voices.len() as u32,
+                    tp: ['S' as u8, 'Y' as u8, 'N' as u8, 'F' as u8],
+                    ident: [0u8; 24],
+                };
+                let mut reply_buffer: [u8; Command::SIZE] = [0u8; Command::SIZE];
+                reply.write_into(&mut reply_buffer);
+                self.socket.send_to(&reply_buffer, sender);
+            },
+            Command::PCM{..} => { /* TODO */ },
+            Command::Unknown{data} => {
+                dprintln!("Dropping packet: unknown data {:?}", (&data as &[u8]));
+            },
+        }
+
+        true
+    }
+
+    pub fn next_frames(&mut self) {
+        let len = self.voices.len();
+
         for voice in self.voices.iter_mut() {
             *voice.params.vars.entry("v_frame".to_string()).or_insert_with(Default::default) = self.frames as f32;
         }
@@ -112,11 +132,19 @@ impl Client {
             self.buf.sum_into(voice.gen.eval(&voice.params));
         }
 
+        self.norm.set(1.0 / (len as f32));
+        self.buf.mul_into(&self.norm);
+        self.frames += self.buf.len();
+    }
+
+    pub fn buffer(&self) -> &SampleBuffer {
+        &self.buf
+    }
+
+    pub fn write_frames_bytes(&self, out_buffer: &mut Vec<u8>) {
         let current = out_buffer.len();
         out_buffer.reserve_exact(self.buf.size() - current);
         unsafe { out_buffer.set_len(self.buf.size()); }
         self.buf.write_bytes(out_buffer);
-        self.frames += self.buf.len();
-        return true;
     }
 }

+ 49 - 7
src/main.rs

@@ -1,28 +1,70 @@
 use std::io;
 use std::io::*;
 use std::net::*;
+use std::sync::*;
+use std::collections::VecDeque;
 
 extern crate synfone;
+extern crate portaudio;
+use portaudio as pa;
+use synfone::*;
 use synfone::synth::*;
 use synfone::lang::*;
+use synfone::proto::*;
 use synfone::client::*;
 
-const GEN: &'static str = "mul(sine(param('v_freq', 500)), ifelse(rel(param('v_frame'), '<', param('v_deadline')), param('v_amp'), 0.0))";
+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 gen = Parser::new(Tokenizer::new(GEN.chars())).expect("Failed to get first token").parse().expect("Failed to compile generator");
+    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 sock = UdpSocket::bind("0.0.0.0:13676").expect("Failed to bind socket");
 
-    let mut client = Client::new(sock, vec![gen], env).expect("Failed to create client");
-    let mut buf: Vec<u8> = Vec::new();
-    let mut out = io::stdout();
+    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");
+    let settings = pa_inst.default_output_stream_settings(1, env.sample_rate as f64, env.default_buffer_size as u32).expect("Failed to instantiate stream settings");
+    let mut stream;
+    {
+        let client = client.clone();
+        let mut ring: VecDeque<Sample> = VecDeque::new();
+        ring.reserve_exact(2 * env.default_buffer_size);
+        stream = pa_inst.open_non_blocking_stream(settings, move |pa::OutputStreamCallbackArgs { buffer, frames, .. }| {
+            while frames > ring.len() {
+                let mut cli = client.lock().unwrap();
+                cli.next_frames();
+                ring.append(&mut cli.buffer().iter().map(|&x| x).collect());
+            }
+            let samps = ring.drain(..frames).collect::<Vec<f32>>();
+            buffer.copy_from_slice(&samps);
+            pa::Continue
+        }).expect("Failed to create stream");
+    }
+
 
     eprintln!("Starting.");
 
-    while client.pump(&mut buf) {
-        out.write_all(&buf).expect("Failed to write samples");
+    stream.start().expect("Failed to start stream");
+
+    let mut buffer: [u8; Command::SIZE] = [0u8; Command::SIZE];
+    loop {
+        let (bytes, sender) = sock.recv_from(&mut buffer).unwrap();
+        if bytes < Command::SIZE {
+            continue;
+        }
+
+        let cmd = Command::from(&buffer);
+        {
+            let mut cli = client.lock().unwrap();
+            if !cli.handle_command(cmd, sender) {
+                break;
+            }
+        }
     }
 
     eprintln!("Exiting.");

+ 16 - 1
src/proto.rs

@@ -1,4 +1,4 @@
-use std::mem;
+use std::{mem, fmt};
 use std::time::Duration;
 use super::*;
 
@@ -69,6 +69,21 @@ impl Command {
     }
 }
 
+impl fmt::Debug for Command {
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(f, "Command::")?;
+        match *self {
+            Command::KeepAlive => write!(f, "KeepAlive"),
+            Command::Ping{data} => write!(f, "Ping {{ data: {:?} }}", &data),
+            Command::Quit => write!(f, "Quit"),
+            Command::Play{sec, usec, freq, amp, voice} => write!(f, "Play {{ sec: {:?}, usec: {:?}, freq: {:?}, amp: {:?}, voice: {:?} }}", sec, usec, freq, amp, voice),
+            Command::Caps{voices, tp, ident} => write!(f, "Caps {{ voices: {:?}, tp: {:?}, ident: {:?} }}", voices, &tp, &ident),
+            Command::PCM{samples} => write!(f, "PCM {{ samples: {:?} }}", &samples),
+            Command::Unknown{data} => write!(f, "Unknown {{ data: {:?} }}", &data as &[u8]),
+        }
+    }
+}
+
 impl<'a> From<&'a [u8; Command::SIZE]> for Command {
     fn from(packet: &'a [u8; Command::SIZE]) -> Command {
         let mut fields_u32: [u32; Command::SIZE / 4] = unsafe { mem::uninitialized() };

+ 4 - 0
src/synth/mod.rs

@@ -64,6 +64,10 @@ impl SampleBuffer {
         self.samples.len()
     }
 
+    pub fn iter(&self) -> slice::Iter<f32> {
+        self.samples.iter()
+    }
+
     pub fn iter_mut(&mut self) -> slice::IterMut<f32> {
         self.samples.iter_mut()
     }