Initial commit

This commit is contained in:
Pierre Krieger 2015-07-22 12:14:11 +02:00
commit 824b4f0041
8 changed files with 149 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
target
Cargo.lock

9
Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "rodio"
version = "0.1.0"
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>"]
[dependencies]
cpal = "0.1.1"
hound = "1.0.0"
lazy_static = "0.1.12"

9
examples/basic.rs Normal file
View file

@ -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);
}

BIN
examples/beep.wav Normal file

Binary file not shown.

16
src/decoder/mod.rs Normal file
View file

@ -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<R>(data: R) -> Box<Decoder + Send> where R: Read + Send + 'static {
if let Ok(decoder) = wav::WavDecoder::new(data) {
return Box::new(decoder);
}
panic!("Invalid format");
}

49
src/decoder/wav.rs Normal file
View file

@ -0,0 +1,49 @@
use std::io::Read;
use super::Decoder;
use cpal::{self, Voice};
use hound::WavReader;
use hound::WavSpec;
pub struct WavDecoder<R> where R: Read {
reader: WavReader<R>,
spec: WavSpec,
}
impl<R> WavDecoder<R> where R: Read {
pub fn new(data: R) -> Result<WavDecoder<R>, ()> {
let reader = match WavReader::new(data) {
Err(_) => return Err(()),
Ok(r) => r
};
let spec = reader.spec();
Ok(WavDecoder {
reader: reader,
spec: spec,
})
}
}
impl<R> Decoder for WavDecoder<R> where R: Read {
fn write(&mut self, voice: &mut Voice) {
let mut samples = self.reader.samples::<i16>();
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<u16> =
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;
}
}
}

45
src/engine.rs Normal file
View file

@ -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<Decoder + Send>)
}
pub struct Engine {
commands: Mutex<Sender<Command>>,
}
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<Decoder + Send>) {
let commands = self.commands.lock().unwrap();
commands.send(Command::Play(decoder)).unwrap();
}
}
fn background(rx: Receiver<Command>) {
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();
}
}
}

19
src/lib.rs Normal file
View file

@ -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<R>(input: R) where R: Read + Send + 'static {
let decoder = decoder::decode(input);
ENGINE.play_once(decoder);
}