Skip to content
Binary file added assets/RL.ogg
Binary file not shown.
56 changes: 37 additions & 19 deletions examples/spatial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,51 @@ use std::io::BufReader;
use std::thread;
use std::time::Duration;

use rodio::Source;

fn main() {
let iter_duration = Duration::from_secs(5);
let iter_distance = 5.;

let refresh_duration = Duration::from_millis(10);

let num_steps = iter_duration.as_secs_f32() / refresh_duration.as_secs_f32();
let step_distance = iter_distance / num_steps;
let num_steps = num_steps as u32;

let repeats = 5;

let total_duration = iter_duration * 2 * repeats;

let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::SpatialSink::try_new(
&handle,
[-10.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[-1.0, 0.0, 0.0],
)
.unwrap();
let mut positions = ([0., 0., 0.], [-1., 0., 0.], [1., 0., 0.]);
let sink = rodio::SpatialSink::try_new(&handle, positions.0, positions.1, positions.2).unwrap();

let file = std::fs::File::open("assets/music.ogg").unwrap();
let source = rodio::Decoder::new(BufReader::new(file)).unwrap();
let source = rodio::Decoder::new(BufReader::new(file))
.unwrap()
.repeat_infinite()
.take_duration(total_duration);
sink.append(source);

// A sound emitter playing the music starting at the left gradually moves to the right
// eventually passing through the listener, then it continues on to the right for a distance
// A sound emitter playing the music starting at the centre gradually moves to the right
// until it stops and begins traveling to the left, it will eventually pass through the
// listener again.
// listener again and go to the far left.
// This is repeated 5 times.
for _ in 0..5 {
for i in 1..1001 {
thread::sleep(Duration::from_millis(5));
sink.set_emitter_position([(i - 500) as f32 / 50.0, 0.0, 0.0]);
for _ in 0..repeats {
for _ in 0..num_steps {
thread::sleep(refresh_duration);
positions.0[0] += step_distance;
sink.set_emitter_position(positions.0);
}
for _ in 0..(num_steps * 2) {
thread::sleep(refresh_duration);
positions.0[0] -= step_distance;
sink.set_emitter_position(positions.0);
}
for i in 1..1001 {
thread::sleep(Duration::from_millis(5));
sink.set_emitter_position([-(i - 500) as f32 / 50.0, 0.0, 0.0]);
for _ in 0..num_steps {
thread::sleep(refresh_duration);
positions.0[0] += step_distance;
sink.set_emitter_position(positions.0);
}
}
sink.sleep_until_end();
Expand Down
12 changes: 12 additions & 0 deletions examples/stereo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! Plays a tone alternating between right and left ears, with right being first.
use std::io::BufReader;

fn main() {
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::try_new(&handle).unwrap();

let file = std::fs::File::open("assets/RL.ogg").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());

sink.sleep_until_end();
}
14 changes: 8 additions & 6 deletions src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ pub struct SourcesQueueOutput<S> {
input: Arc<SourcesQueueInput<S>>,
}

const THRESHOLD: usize = 512;
impl<S> Source for SourcesQueueOutput<S>
where
S: Sample + Send + 'static,
Expand All @@ -116,12 +117,16 @@ where
// If the `size_hint` is `None` as well, we are in the worst case scenario. To handle this
// situation we force a frame to have a maximum number of samples indicate by this
// constant.
const THRESHOLD: usize = 512;

// Try the current `current_frame_len`.
if let Some(val) = self.current.current_frame_len() {
if val != 0 {
return Some(val);
} else if self.input.keep_alive_if_empty.load(Ordering::Acquire)
&& self.input.next_sounds.lock().unwrap().is_empty()
{
// The next source will be a filler silence which will have the length of `THRESHOLD`
return Some(THRESHOLD);
}
}

Expand Down Expand Up @@ -198,13 +203,10 @@ where
let mut next = self.input.next_sounds.lock().unwrap();

if next.len() == 0 {
let silence = Box::new(Zero::<S>::new_samples(1, 44100, THRESHOLD)) as Box<_>;
if self.input.keep_alive_if_empty.load(Ordering::Acquire) {
// Play a short silence in order to avoid spinlocking.
let silence = Zero::<S>::new(1, 44100); // TODO: meh
(
Box::new(silence.take_duration(Duration::from_millis(10))) as Box<_>,
None,
)
(silence, None)
} else {
return Err(());
}
Expand Down
24 changes: 22 additions & 2 deletions src/source/zero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{Sample, Source};
pub struct Zero<S> {
channels: u16,
sample_rate: u32,
num_samples: Option<usize>,
marker: PhantomData<S>,
}

Expand All @@ -17,6 +18,16 @@ impl<S> Zero<S> {
Zero {
channels,
sample_rate,
num_samples: None,
marker: PhantomData,
}
}
#[inline]
pub fn new_samples(channels: u16, sample_rate: u32, num_samples: usize) -> Zero<S> {
Zero {
channels,
sample_rate,
num_samples: Some(num_samples),
marker: PhantomData,
}
}
Expand All @@ -30,7 +41,16 @@ where

#[inline]
fn next(&mut self) -> Option<S> {
Some(S::zero_value())
if let Some(num_samples) = self.num_samples {
if num_samples > 0 {
self.num_samples = Some(num_samples - 1);
Some(S::zero_value())
} else {
None
}
} else {
Some(S::zero_value())
}
}
}

Expand All @@ -40,7 +60,7 @@ where
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
self.num_samples
}

#[inline]
Expand Down