Add OutputStreamHandle

This commit is contained in:
Alex Butler 2020-02-25 10:09:17 +00:00
parent 7ad840793c
commit d7c77aa52e
No known key found for this signature in database
GPG key ID: E1355A2F8E415521
11 changed files with 75 additions and 52 deletions

View file

@ -3,24 +3,24 @@ use std::thread;
use std::time::Duration; use std::time::Duration;
fn main() { fn main() {
let stream = rodio::OutputStream::try_default().unwrap(); let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();
let file = std::fs::File::open("examples/beep.wav").unwrap(); let file = std::fs::File::open("examples/beep.wav").unwrap();
let beep1 = stream.play_once(BufReader::new(file)).unwrap(); let beep1 = stream_handle.play_once(BufReader::new(file)).unwrap();
beep1.set_volume(0.2); beep1.set_volume(0.2);
println!("Started beep1"); println!("Started beep1");
thread::sleep(Duration::from_millis(1500)); thread::sleep(Duration::from_millis(1500));
let file = std::fs::File::open("examples/beep2.wav").unwrap(); let file = std::fs::File::open("examples/beep2.wav").unwrap();
let beep2 = stream.play_once(BufReader::new(file)).unwrap(); let beep2 = stream_handle.play_once(BufReader::new(file)).unwrap();
beep2.set_volume(0.3); beep2.set_volume(0.3);
beep2.detach(); beep2.detach();
println!("Started beep2"); println!("Started beep2");
thread::sleep(Duration::from_millis(1500)); thread::sleep(Duration::from_millis(1500));
let file = std::fs::File::open("examples/beep3.ogg").unwrap(); let file = std::fs::File::open("examples/beep3.ogg").unwrap();
let beep3 = stream.play_once(file).unwrap(); let beep3 = stream_handle.play_once(file).unwrap();
beep3.set_volume(0.2); beep3.set_volume(0.2);
println!("Started beep3"); println!("Started beep3");

View file

@ -1,8 +1,8 @@
use std::io::BufReader; use std::io::BufReader;
fn main() { fn main() {
let stream = rodio::OutputStream::try_default().unwrap(); let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::new(&stream); let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("examples/music.flac").unwrap(); let file = std::fs::File::open("examples/music.flac").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap()); sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());

View file

@ -1,8 +1,8 @@
use std::io::BufReader; use std::io::BufReader;
fn main() { fn main() {
let stream = rodio::OutputStream::try_default().unwrap(); let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::new(&stream); let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("examples/music.mp3").unwrap(); let file = std::fs::File::open("examples/music.mp3").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap()); sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());

View file

@ -1,8 +1,8 @@
use std::io::BufReader; use std::io::BufReader;
fn main() { fn main() {
let stream = rodio::OutputStream::try_default().unwrap(); let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::new(&stream); let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("examples/music.ogg").unwrap(); let file = std::fs::File::open("examples/music.ogg").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap()); sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());

View file

@ -1,8 +1,8 @@
use std::io::BufReader; use std::io::BufReader;
fn main() { fn main() {
let stream = rodio::OutputStream::try_default().unwrap(); let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::new(&stream); let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("examples/music.wav").unwrap(); let file = std::fs::File::open("examples/music.wav").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap()); sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());

View file

@ -3,8 +3,8 @@ use std::io::BufReader;
use std::time::Duration; use std::time::Duration;
fn main() { fn main() {
let stream = rodio::OutputStream::try_default().unwrap(); let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::new(&stream); let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("examples/music.ogg").unwrap(); let file = std::fs::File::open("examples/music.ogg").unwrap();
let source = rodio::Decoder::new(BufReader::new(file)).unwrap(); let source = rodio::Decoder::new(BufReader::new(file)).unwrap();

View file

@ -3,13 +3,13 @@ use std::thread;
use std::time::Duration; use std::time::Duration;
fn main() { fn main() {
let stream = rodio::OutputStream::try_default().unwrap(); let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::SpatialSink::new( let sink = rodio::SpatialSink::try_new(
&stream, &handle,
[-10.0, 0.0, 0.0], [-10.0, 0.0, 0.0],
[1.0, 0.0, 0.0], [1.0, 0.0, 0.0],
[-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0],
); ).unwrap();
let file = std::fs::File::open("examples/music.ogg").unwrap(); let file = std::fs::File::open("examples/music.ogg").unwrap();
let source = rodio::Decoder::new(BufReader::new(file)).unwrap(); let source = rodio::Decoder::new(BufReader::new(file)).unwrap();

View file

@ -20,11 +20,11 @@
//! use std::io::BufReader; //! use std::io::BufReader;
//! use rodio::Source; //! use rodio::Source;
//! //!
//! let stream = rodio::OutputStream::try_default().unwrap(); //! let (stream, stream_handle) = rodio::OutputStream::try_default().unwrap();
//! //!
//! let file = File::open("sound.ogg").unwrap(); //! let file = File::open("sound.ogg").unwrap();
//! let source = rodio::Decoder::new(BufReader::new(file)).unwrap(); //! let source = rodio::Decoder::new(BufReader::new(file)).unwrap();
//! stream.play_raw(source.convert_samples()); //! stream_handle.play_raw(source.convert_samples());
//! ``` //! ```
//! //!
//! ## Sink //! ## Sink
@ -38,8 +38,8 @@
//! ```no_run //! ```no_run
//! use rodio::Sink; //! use rodio::Sink;
//! //!
//! let stream = rodio::OutputStream::try_default().unwrap(); //! let (stream, stream_handle) = rodio::OutputStream::try_default().unwrap();
//! let sink = rodio::Sink::new(&stream); //! let sink = rodio::Sink::try_new(&stream_handle).unwrap();
//! //!
//! // Add a dummy source of the sake of the example. //! // Add a dummy source of the sake of the example.
//! let source = rodio::source::SineWave::new(440); //! let source = rodio::source::SineWave::new(440);

View file

@ -1,4 +1,4 @@
use crate::stream::OutputStream; use crate::stream::{OutputStreamHandle, PlayError};
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
@ -32,12 +32,12 @@ struct Controls {
} }
impl Sink { impl Sink {
/// Builds a new `Sink`, beginning playback on a Device. /// Builds a new `Sink`, beginning playback on a stream.
#[inline] #[inline]
pub fn new(stream: &OutputStream) -> Sink { pub fn try_new(stream: &OutputStreamHandle) -> Result<Sink, PlayError> {
let (sink, queue_rx) = Sink::new_idle(); let (sink, queue_rx) = Sink::new_idle();
stream.play_raw(queue_rx); stream.play_raw(queue_rx)?;
sink Ok(sink)
} }
/// Builds a new `Sink`. /// Builds a new `Sink`.

View file

@ -1,5 +1,5 @@
use crate::source::Spatial; use crate::source::Spatial;
use crate::stream::OutputStream; use crate::stream::{OutputStreamHandle, PlayError};
use crate::Sample; use crate::Sample;
use crate::Sink; use crate::Sink;
use crate::Source; use crate::Source;
@ -21,21 +21,20 @@ struct SoundPositions {
impl SpatialSink { impl SpatialSink {
/// Builds a new `SpatialSink`. /// Builds a new `SpatialSink`.
#[inline] pub fn try_new(
pub fn new( stream: &OutputStreamHandle,
stream: &OutputStream,
emitter_position: [f32; 3], emitter_position: [f32; 3],
left_ear: [f32; 3], left_ear: [f32; 3],
right_ear: [f32; 3], right_ear: [f32; 3],
) -> SpatialSink { ) -> Result<SpatialSink, PlayError> {
SpatialSink { Ok(SpatialSink {
sink: Sink::new(stream), sink: Sink::try_new(stream)?,
positions: Arc::new(Mutex::new(SoundPositions { positions: Arc::new(Mutex::new(SoundPositions {
emitter_position, emitter_position,
left_ear, left_ear,
right_ear, right_ear,
})), })),
} })
} }
/// Sets the position of the sound emitter in 3 dimensional space. /// Sets the position of the sound emitter in 3 dimensional space.

View file

@ -6,55 +6,79 @@ use cpal::{
traits::{DeviceTrait, HostTrait, StreamTrait}, traits::{DeviceTrait, HostTrait, StreamTrait},
Sample, Sample,
}; };
use std::convert::TryFrom;
use std::io::{Read, Seek}; use std::io::{Read, Seek};
use std::sync::Arc; use std::sync::{Arc, Weak};
use std::{error, fmt}; use std::{error, fmt};
/// Immovable `cpal::Stream` container.
/// If this is dropped playback will end & attached `OutputStreamHandle`s will no longer work.
pub struct OutputStream { pub struct OutputStream {
mixer: Arc<DynamicMixerController<f32>>, mixer: Arc<DynamicMixerController<f32>>,
_stream: cpal::Stream, _stream: cpal::Stream,
} }
impl TryFrom<&'_ cpal::Device> for OutputStream { /// More flexible handle to a `OutputStream` that provides playback.
type Error = StreamError; #[derive(Clone)]
pub struct OutputStreamHandle {
fn try_from(device: &cpal::Device) -> Result<Self, Self::Error> { mixer: Weak<DynamicMixerController<f32>>,
let (mixer, _stream) = device.new_output_stream();
_stream.play()?;
Ok(Self { mixer, _stream })
}
} }
impl OutputStream { impl OutputStream {
pub fn try_default() -> Result<Self, StreamError> { pub fn try_from_device(
device: &cpal::Device,
) -> Result<(Self, OutputStreamHandle), StreamError> {
let (mixer, _stream) = device.new_output_stream();
_stream.play()?;
let out = Self { mixer, _stream };
let handle = OutputStreamHandle {
mixer: Arc::downgrade(&out.mixer),
};
Ok((out, handle))
}
pub fn try_default() -> Result<(Self, OutputStreamHandle), StreamError> {
let device = cpal::default_host() let device = cpal::default_host()
.default_output_device() .default_output_device()
.ok_or(StreamError::NoDevice)?; .ok_or(StreamError::NoDevice)?;
Self::try_from(&device) Self::try_from_device(&device)
} }
}
impl OutputStreamHandle {
/// Plays a source with a device until it ends. /// Plays a source with a device until it ends.
pub fn play_raw<S>(&self, source: S) pub fn play_raw<S>(&self, source: S) -> Result<(), PlayError>
where where
S: Source<Item = f32> + Send + 'static, S: Source<Item = f32> + Send + 'static,
{ {
self.mixer.add(source); let mixer = self.mixer.upgrade().ok_or(PlayError::NoDevice)?;
mixer.add(source);
Ok(())
} }
/// Plays a sound once. Returns a `Sink` that can be used to control the sound. /// Plays a sound once. Returns a `Sink` that can be used to control the sound.
#[inline] pub fn play_once<R>(&self, input: R) -> Result<Sink, PlayError>
pub fn play_once<R>(&self, input: R) -> Result<Sink, decoder::DecoderError>
where where
R: Read + Seek + Send + 'static, R: Read + Seek + Send + 'static,
{ {
let input = decoder::Decoder::new(input)?; let input = decoder::Decoder::new(input)?;
let sink = Sink::new(&self); let sink = Sink::try_new(self)?;
sink.append(input); sink.append(input);
Ok(sink) Ok(sink)
} }
} }
#[derive(Debug)]
pub enum PlayError {
DecoderError(decoder::DecoderError),
NoDevice,
}
impl From<decoder::DecoderError> for PlayError {
fn from(err: decoder::DecoderError) -> Self {
Self::DecoderError(err)
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum StreamError { pub enum StreamError {
PlayStreamError(cpal::PlayStreamError), PlayStreamError(cpal::PlayStreamError),