diff --git a/src/bmixer.rs b/src/bmixer.rs index cf7989b..c1f5e9a 100644 --- a/src/bmixer.rs +++ b/src/bmixer.rs @@ -1,11 +1,11 @@ -use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; use std::time::Duration; use rodio::{Sample, Source}; use bformat::{Bformat, Bweights}; -use bstream::Bstream; +use bstream::{self, Bstream, BstreamController}; pub fn bmixer(sample_rate: u32) -> (BstreamMixer, Arc) { let controller = Arc::new(BmixerController { @@ -54,7 +54,10 @@ impl Iterator for BstreamMixer { fn next(&mut self) -> Option { if self.controller.has_pending.load(Ordering::SeqCst) { - let mut pending = self.controller.pending_streams.lock().expect("Cannot lock pending streams"); + let mut pending = self.controller + .pending_streams + .lock() + .expect("Cannot lock pending streams"); self.active_streams.extend(pending.drain(..)); self.controller.has_pending.store(false, Ordering::SeqCst); } @@ -85,15 +88,20 @@ pub struct BmixerController { } impl BmixerController { - pub fn play(&self, input: I, pos: [f32; 3]) + pub fn play(&self, input: I, pos: [f32; 3]) -> Arc where - I: Source + Send + 'static + I: Source + Send + 'static, { assert_eq!(input.channels(), 1); - let bstream = Bstream::new(input, pos); + let (bstream, sound_ctl) = bstream::bstream(input, pos); - self.pending_streams.lock().expect("Cannot lock pending streams").push(bstream); + self.pending_streams + .lock() + .expect("Cannot lock pending streams") + .push(bstream); self.has_pending.store(true, Ordering::SeqCst); + + sound_ctl } -} \ No newline at end of file +} diff --git a/src/bstream.rs b/src/bstream.rs index a6cfda8..215ee88 100644 --- a/src/bstream.rs +++ b/src/bstream.rs @@ -1,22 +1,34 @@ -use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; use std::time::Duration; use rodio::Source; use bformat::{Bformat, Bweights}; +pub fn bstream + Send + 'static>( + source: I, + pos: [f32; 3], +) -> (Bstream, Arc) { + let controller = Arc::new(BstreamController { + commands: Mutex::new(Vec::new()), + pending_commands: AtomicBool::new(false), + stopped: AtomicBool::new(false), + }); + + let stream = Bstream { + input: Box::new(source), + bweights: Bweights::from_position(pos), + controller: controller.clone(), + }; + + (stream, controller) +} + pub struct Bstream { input: Box + Send>, bweights: Bweights, -} - -impl Bstream { - pub fn new + Send + 'static>(source: I, pos: [f32; 3]) -> Self { - Bstream { - input: Box::new(source), - bweights: Bweights::from_position(pos), - } - } + controller: Arc, } impl Source for Bstream { @@ -46,7 +58,57 @@ impl Iterator for Bstream { type Item = Bformat; fn next(&mut self) -> Option { - let x = self.input.next()?; - Some(self.bweights.scale(x)) + if self.controller.pending_commands.load(Ordering::SeqCst) { + let mut commands = self.controller.commands.lock().unwrap(); + let mut new_pos = None; + + for cmd in commands.drain(..) { + match cmd { + Command::SetPos(p) => new_pos = Some(p), + Command::Stop => { + self.controller.stopped.store(true, Ordering::SeqCst); + return None; + } + } + } + + if let Some(pos) = new_pos { + self.bweights = Bweights::from_position(pos); + } + + self.controller + .pending_commands + .store(false, Ordering::SeqCst); + } + match self.input.next() { + Some(x) => Some(self.bweights.scale(x)), + None => { + self.controller.stopped.store(true, Ordering::SeqCst); + None + } + } + } +} + +enum Command { + SetPos([f32; 3]), + Stop, +} + +pub struct BstreamController { + commands: Mutex>, + pending_commands: AtomicBool, + stopped: AtomicBool, +} + +impl BstreamController { + pub fn set_position(&self, pos: [f32; 3]) { + self.commands.lock().unwrap().push(Command::SetPos(pos)); + self.pending_commands.store(true, Ordering::SeqCst); + } + + pub fn stop(&self) { + self.commands.lock().unwrap().push(Command::Stop); + self.pending_commands.store(true, Ordering::SeqCst); } } diff --git a/src/lib.rs b/src/lib.rs index 0c3f14c..dc30fa2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,8 +2,8 @@ extern crate cpal; pub extern crate rodio; mod bformat; -mod bstream; mod bmixer; +mod bstream; mod renderer; use std::sync::Arc; @@ -37,7 +37,7 @@ impl AmbisonicBuilder { } } - pub fn build(self) -> Engine { + pub fn build(self) -> Ambisonic { let device = self.device .unwrap_or_else(|| rodio::default_output_device().unwrap()); let sink = rodio::Sink::new(&device); @@ -47,14 +47,11 @@ impl AmbisonicBuilder { sink.append(output); - Engine { - sink, - controller, - } + Ambisonic { sink, controller } } } -pub struct Engine { +pub struct Ambisonic { sink: rodio::Sink, controller: Arc, } @@ -70,13 +67,18 @@ mod tests { let engine = AmbisonicBuilder::new().build(); let source = rodio::source::SineWave::new(440); - engine.controller.play(source, [1.0, 0.0, 0.0]); + let first = engine.controller.play(source, [1.0, 0.0, 0.0]); sleep(Duration::from_millis(1000)); let source = rodio::source::SineWave::new(330); - engine.controller.play(source, [-1.0, 0.0, 0.0]); + let second = engine.controller.play(source, [-1.0, 0.0, 0.0]); - sleep(Duration::from_millis(2000)); + sleep(Duration::from_millis(1000)); + + first.stop(); + second.set_position([0.0, 1.0, 0.0]); + + sleep(Duration::from_millis(1000)); } }