commit 824b4f0041f888e6f73ed3ca8e108ae9dee12a1c Author: Pierre Krieger Date: Wed Jul 22 12:14:11 2015 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..29133de --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rodio" +version = "0.1.0" +authors = ["Pierre Krieger "] + +[dependencies] +cpal = "0.1.1" +hound = "1.0.0" +lazy_static = "0.1.12" diff --git a/examples/basic.rs b/examples/basic.rs new file mode 100644 index 0000000..37f524a --- /dev/null +++ b/examples/basic.rs @@ -0,0 +1,9 @@ +extern crate rodio; + +fn main() { + let file = std::fs::File::open("examples/beep.wav").unwrap(); + + rodio::play_once(file); + + std::thread::sleep_ms(10000); +} diff --git a/examples/beep.wav b/examples/beep.wav new file mode 100644 index 0000000..7321665 Binary files /dev/null and b/examples/beep.wav differ diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs new file mode 100644 index 0000000..884cf1a --- /dev/null +++ b/src/decoder/mod.rs @@ -0,0 +1,16 @@ +use std::io::Read; +use cpal::Voice; + +mod wav; + +pub trait Decoder { + fn write(&mut self, &mut Voice); +} + +pub fn decode(data: R) -> Box where R: Read + Send + 'static { + if let Ok(decoder) = wav::WavDecoder::new(data) { + return Box::new(decoder); + } + + panic!("Invalid format"); +} diff --git a/src/decoder/wav.rs b/src/decoder/wav.rs new file mode 100644 index 0000000..4d8b2d1 --- /dev/null +++ b/src/decoder/wav.rs @@ -0,0 +1,49 @@ +use std::io::Read; +use super::Decoder; + +use cpal::{self, Voice}; +use hound::WavReader; +use hound::WavSpec; + +pub struct WavDecoder where R: Read { + reader: WavReader, + spec: WavSpec, +} + +impl WavDecoder where R: Read { + pub fn new(data: R) -> Result, ()> { + let reader = match WavReader::new(data) { + Err(_) => return Err(()), + Ok(r) => r + }; + + let spec = reader.spec(); + + Ok(WavDecoder { + reader: reader, + spec: spec, + }) + } +} + +impl Decoder for WavDecoder where R: Read { + fn write(&mut self, voice: &mut Voice) { + let mut samples = self.reader.samples::(); + let samples_left = samples.len(); + if samples_left == 0 { return; } + + // TODO: hack because of a bug in cpal + let samples_left = if samples_left > 512 { 512 } else { samples_left }; + + let mut buffer: cpal::Buffer = + voice.append_data(self.spec.channels, + cpal::SamplesRate(self.spec.sample_rate), + samples_left); + + for (dest, src) in buffer.iter_mut().zip(&mut samples) { + // TODO: There is a bug in cpal that handles signed samples in the + // wrong manner, so we cast it to `u16` for now. + *dest = src.unwrap() as u16; + } + } +} diff --git a/src/engine.rs b/src/engine.rs new file mode 100644 index 0000000..73626f2 --- /dev/null +++ b/src/engine.rs @@ -0,0 +1,45 @@ +use std::thread; +use std::sync::mpsc::{self, Sender, Receiver}; +use std::sync::Mutex; + +use cpal::Voice; +use decoder::Decoder; + +pub enum Command { + Play(Box) +} + +pub struct Engine { + commands: Mutex>, +} + +impl Engine { + pub fn new() -> Engine { + let (tx, rx) = mpsc::channel(); + thread::spawn(move || background(rx)); + Engine { commands: Mutex::new(tx) } + } + + pub fn play_once(&self, decoder: Box) { + let commands = self.commands.lock().unwrap(); + commands.send(Command::Play(decoder)).unwrap(); + } +} + +fn background(rx: Receiver) { + let mut sounds = Vec::new(); + + loop { + // polling for new sounds + if let Ok(command) = rx.try_recv() { + match command { + Command::Play(decoder) => sounds.push((Voice::new(), decoder)), + } + } + + for &mut (ref mut voice, ref mut decoder) in sounds.iter_mut() { + decoder.write(voice); + voice.play(); + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..04f0c45 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,19 @@ +extern crate cpal; +extern crate hound; +#[macro_use] +extern crate lazy_static; + +use std::io::Read; + +mod decoder; +mod engine; + +lazy_static! { + static ref ENGINE: engine::Engine = engine::Engine::new(); +} + +/// Plays a sound once. There's no way to stop the sound except by exiting the program. +pub fn play_once(input: R) where R: Read + Send + 'static { + let decoder = decoder::decode(input); + ENGINE.play_once(decoder); +}