Update to cpal 0.2

This commit is contained in:
Pierre Krieger 2015-09-01 19:35:26 +02:00
parent a2081a9e41
commit 89abf72790
12 changed files with 533 additions and 52 deletions

View file

@ -9,7 +9,8 @@ repository = "https://github.com/tomaka/rodio"
documentation = "http://tomaka.github.io/rodio/rodio/index.html" documentation = "http://tomaka.github.io/rodio/rodio/index.html"
[dependencies] [dependencies]
cpal = "0.1.1" cpal = "0.2.1"
hound = "1.0.0" #hound = "1.0.0"
hound = { git = "https://github.com/tomaka/hound", branch = "wavreader-ownership" }
lazy_static = "0.1.12" lazy_static = "0.1.12"
vorbis = "0.0.12" vorbis = "0.0.12"

3
examples/README.md Normal file
View file

@ -0,0 +1,3 @@
## License
The `music.wav` and `music.ogg` files in this directory are under cc-by-sa.

View file

@ -1,17 +1,19 @@
extern crate rodio; extern crate rodio;
fn main() { fn main() {
let endpoint = rodio::get_default_endpoint().unwrap();
let file = std::fs::File::open("examples/beep.wav").unwrap(); let file = std::fs::File::open("examples/beep.wav").unwrap();
let beep1 = rodio::play_once(file); let beep1 = rodio::play_once(&endpoint, file);
std::thread::sleep_ms(1000); std::thread::sleep_ms(1000);
let file = std::fs::File::open("examples/beep2.wav").unwrap(); let file = std::fs::File::open("examples/beep2.wav").unwrap();
rodio::play_once(file); rodio::play_once(&endpoint, file);
std::thread::sleep_ms(1000); std::thread::sleep_ms(1000);
let file = std::fs::File::open("examples/beep3.ogg").unwrap(); /*let file = std::fs::File::open("examples/beep3.ogg").unwrap();
rodio::play_once(file); rodio::play_once(&endpoint, file);*/
std::thread::sleep_ms(1000); std::thread::sleep_ms(1000);
beep1.stop(); beep1.stop();

BIN
examples/music.ogg Normal file

Binary file not shown.

BIN
examples/music.wav Normal file

Binary file not shown.

10
examples/music_wav.rs Normal file
View file

@ -0,0 +1,10 @@
extern crate rodio;
fn main() {
let endpoint = rodio::get_default_endpoint().unwrap();
let file = std::fs::File::open("examples/music.wav").unwrap();
let _music = rodio::play_once(&endpoint, file);
std::thread::sleep_ms(10000);
}

421
src/decoder/conversions.rs Normal file
View file

@ -0,0 +1,421 @@
/*!
This module contains function that will convert from one PCM format to another.
This includes conversion between samples formats, channels or sample rates.
*/
use std::borrow::Cow;
use std::cmp;
use std::mem;
use std::iter;
use cpal;
use cpal::UnknownTypeBuffer;
use cpal::SampleFormat;
///
pub fn convert_and_write<I, S>(mut samples: I, output: &mut UnknownTypeBuffer)
where I: Iterator<Item=S>, S: Sample
{
match output {
&mut UnknownTypeBuffer::U16(ref mut buffer) => {
for (i, o) in samples.zip(buffer.iter_mut()) {
*o = i.to_u16();
}
},
&mut UnknownTypeBuffer::I16(ref mut buffer) => {
for (i, o) in samples.zip(buffer.iter_mut()) {
*o = i.to_i16();
}
},
&mut UnknownTypeBuffer::F32(ref mut buffer) => {
for (i, o) in samples.zip(buffer.iter_mut()) {
*o = i.to_f32();
}
},
}
}
/// Trait for containers that contain PCM data.
pub trait Sample: cpal::Sample {
/// Returns the average inside a list.
fn average(data: &[Self]) -> Self;
fn to_i16(&self) -> i16;
fn to_u16(&self) -> u16;
fn to_f32(&self) -> f32;
}
impl Sample for u16 {
fn average(data: &[u16]) -> u16 {
let sum: usize = data.iter().fold(0, |acc, &item| acc + item as usize);
(sum / data.len()) as u16
}
fn to_i16(&self) -> i16 {
if *self >= 32768 {
(*self - 32768) as i16
} else {
(*self as i16) - 32767 - 1
}
}
fn to_u16(&self) -> u16 {
*self
}
fn to_f32(&self) -> f32 {
self.to_i16().to_f32()
}
}
impl Sample for i16 {
fn average(data: &[i16]) -> i16 {
let sum: isize = data.iter().fold(0, |acc, &item| acc + item as isize);
(sum / data.len() as isize) as i16
}
fn to_i16(&self) -> i16 {
*self
}
fn to_u16(&self) -> u16 {
if *self < 0 {
(*self + 32767 + 1) as u16
} else {
(*self as u16) + 32768
}
}
fn to_f32(&self) -> f32 {
if *self > 0 {
*self as f32 / 32767.0
} else {
*self as f32 / 32768.0
}
}
}
impl Sample for f32 {
fn average(data: &[f32]) -> f32 {
let sum: f64 = data.iter().fold(0.0, |acc, &item| acc + item as f64);
(sum / data.len() as f64) as f32
}
fn to_i16(&self) -> i16 {
if *self >= 0.0 {
(*self * 32767.0) as i16
} else {
(*self * 32768.0) as i16
}
}
fn to_u16(&self) -> u16 {
if *self >= 0.0 {
((*self * 32767.0) + 32768.0) as u16
} else {
((*self * 32768.0) + 32768.0) as u16
}
}
fn to_f32(&self) -> f32 {
*self
}
}
/// Iterator that converts from a certain channels count to another.
pub struct ChannelsCountConverter<I> where I: Iterator {
input: I,
from: cpal::ChannelsCount,
to: cpal::ChannelsCount,
output_buffer: Vec<I::Item>,
}
impl<I> ChannelsCountConverter<I> where I: Iterator {
///
///
/// # Panic
///
/// Panicks if `from` or `to` are equal to 0.
///
pub fn new(input: I, from: cpal::ChannelsCount, to: cpal::ChannelsCount)
-> ChannelsCountConverter<I>
{
assert!(from >= 1);
assert!(to >= 1);
ChannelsCountConverter {
input: input,
from: from,
to: to,
output_buffer: Vec::with_capacity(to as usize),
}
}
}
impl<I> Iterator for ChannelsCountConverter<I> where I: Iterator, I::Item: Clone {
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
if self.output_buffer.len() == 0 {
// copying common channels from input to output
for _ in (0 .. cmp::min(self.from, self.to)) {
self.output_buffer.push(match self.input.next() {
Some(i) => i,
None => return None
});
}
// adding extra output channels
// TODO: could be done better
if self.to > self.from {
for _ in (0 .. self.to - self.from) {
let val = self.output_buffer[0].clone();
self.output_buffer.push(val);
}
}
// discarding extra channels
if self.from > self.to {
for _ in (0 .. self.from - self.to) {
let _ = self.input.next();
}
}
}
Some(self.output_buffer.remove(0))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (min, max) = self.input.size_hint();
let min = (min / self.from as usize) * self.to as usize + self.output_buffer.len();
let max = max.map(|max| (max / self.from as usize) * self.to as usize + self.output_buffer.len());
(min, max)
}
}
impl<I> ExactSizeIterator for ChannelsCountConverter<I>
where I: ExactSizeIterator, I::Item: Clone {}
/// Iterator that converts from a certain samples rate to another.
pub struct SamplesRateConverter<I> where I: Iterator {
input: I,
from: u32,
to: u32,
output_buffer: Vec<I::Item>,
}
impl<I> SamplesRateConverter<I> where I: Iterator {
///
///
/// # Panic
///
/// Panicks if `from` or `to` are equal to 0.
///
pub fn new(input: I, from: cpal::SamplesRate, to: cpal::SamplesRate)
-> SamplesRateConverter<I>
{
let from = from.0;
let to = to.0;
assert!(from >= 1);
assert!(to >= 1);
// finding greatest common divisor
// TODO: better method
let gcd = {
let mut value = cmp::min(from, to);
while (from % value) != 0 || (to % value) != 0 {
value -= 1;
}
value
};
SamplesRateConverter {
input: input,
from: from / gcd,
to: to / gcd,
output_buffer: Vec::with_capacity(to as usize),
}
}
}
impl<I> Iterator for SamplesRateConverter<I> where I: Iterator, I::Item: Sample + Clone {
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
if self.output_buffer.len() == 0 {
if self.input.size_hint().1.unwrap() == 0 {
return None;
}
// reading samples from the input
let input = self.input.by_ref().take(self.from as usize);
// and duplicating each sample `to` times
let self_to = self.to as usize;
let input = input.flat_map(|val| iter::repeat(val).take(self_to));
let input: Vec<_> = input.collect();
// the length of `input` is `from * to`
// now taking chunks of `from` size and building the average of each chunk
// therefore the remaining list is of size `to`
self.output_buffer = input.chunks(self.from as usize)
.map(|chunk| Sample::average(chunk)).collect();
}
Some(self.output_buffer.remove(0))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (min, max) = self.input.size_hint();
let min = (min / self.from as usize) * self.to as usize + self.output_buffer.len();
let max = max.map(|max| (max / self.from as usize) * self.to as usize + self.output_buffer.len());
(min, max)
}
}
impl<I> ExactSizeIterator for SamplesRateConverter<I>
where I: ExactSizeIterator, I::Item: Sample + Clone {}
/// Converts between a certain number of channels.
///
/// If the target number is inferior to the source number, additional channels are removed.
///
/// If the target number is superior to the source number, the value of channel `N` is equal
/// to the value of channel `N % source_channels`.
///
/// ## Panic
///
/// Panics if `from` is 0, `to` is 0, or if the data length is not a multiple of `from`.
pub fn convert_channels<T>(input: &[T], from: cpal::ChannelsCount, to: cpal::ChannelsCount) -> Vec<T>
where T: Sample
{
assert!(from != 0);
assert!(to != 0);
assert!(input.len() % from as usize == 0);
let mut result = Vec::new();
for element in input.chunks(from as usize) {
// copying the common channels
for i in (0 .. ::std::cmp::min(from, to)) {
result.push(element[i as usize]);
}
// adding extra ones
if to > from {
for i in (0 .. to - from) {
result.push(element[i as usize % element.len()]);
}
}
}
result
}
#[cfg(test)]
mod test {
use super::convert_channels;
use super::convert_samples_rate;
#[test]
fn remove_channels() {
let result = convert_channels(&[1u16, 2, 3, 1, 2, 3], 3, 2);
assert_eq!(result, [1, 2, 1, 2]);
let result = convert_channels(&[1u16, 2, 3, 4, 1, 2, 3, 4], 4, 1);
assert_eq!(result, [1, 1]);
}
#[test]
fn add_channels() {
let result = convert_channels(&[1u16, 2, 1, 2], 2, 3);
assert_eq!(result, [1, 2, 1, 1, 2, 1]);
let result = convert_channels(&[1u16, 2, 1, 2], 2, 4);
assert_eq!(result, [1, 2, 1, 2, 1, 2, 1, 2]);
}
#[test]
#[should_panic]
fn convert_channels_wrong_data_len() {
convert_channels(&[1u16, 2, 3], 2, 1);
}
#[test]
fn half_samples_rate() {
let result = convert_samples_rate(&[1u16, 16, 2, 17, 3, 18, 4, 19],
::SamplesRate(44100), ::SamplesRate(22050), 2);
assert_eq!(result, [1, 16, 3, 18]);
}
#[test]
fn double_samples_rate() {
let result = convert_samples_rate(&[2u16, 16, 4, 18, 6, 20, 8, 22],
::SamplesRate(22050), ::SamplesRate(44100), 2);
assert_eq!(result, [2, 16, 3, 17, 4, 18, 5, 19, 6, 20, 7, 21, 8, 22]);
}
#[test]
fn i16_to_i16() {
let out = Sample::to_vec_i16(&[0i16, -467, 32767, -32768]).into_owned();
assert_eq!(out, vec![0, -467, 32767, -32768]);
}
#[test]
fn i16_to_u16() {
let out = Sample::to_vec_u16(&[0i16, -16384, 32767, -32768]).into_owned();
assert_eq!(out, vec![32768, 16384, 65535, 0]);
}
#[test]
fn i16_to_f32() {
let out = Sample::to_vec_f32(&[0i16, -16384, 32767, -32768]).into_owned();
assert_eq!(out, vec![0.0, -0.5, 1.0, -1.0]);
}
#[test]
fn u16_to_i16() {
let out = Sample::to_vec_i16(&[32768u16, 16384, 65535, 0]).into_owned();
assert_eq!(out, vec![0, -16384, 32767, -32768]);
}
#[test]
fn u16_to_u16() {
let out = Sample::to_vec_u16(&[0u16, 467, 32767, 65535]).into_owned();
assert_eq!(out, vec![0, 467, 32767, 65535]);
}
#[test]
fn u16_to_f32() {
let out = Sample::to_vec_f32(&[0u16, 32768, 65535]).into_owned();
assert_eq!(out, vec![-1.0, 0.0, 1.0]);
}
#[test]
fn f32_to_i16() {
let out = Sample::to_vec_i16(&[0.0f32, -0.5, 1.0, -1.0]).into_owned();
assert_eq!(out, vec![0, -16384, 32767, -32768]);
}
#[test]
fn f32_to_u16() {
let out = Sample::to_vec_u16(&[-1.0f32, 0.0, 1.0]).into_owned();
assert_eq!(out, vec![0, 32768, 65535]);
}
#[test]
fn f32_to_f32() {
let out = Sample::to_vec_f32(&[0.1f32, -0.7, 1.0]).into_owned();
assert_eq!(out, vec![0.1, -0.7, 1.0]);
}
}

View file

@ -1,27 +1,32 @@
use std::io::{Read, Seek}; use std::io::{Read, Seek};
use cpal::Endpoint;
use cpal::Voice; use cpal::Voice;
mod vorbis; mod conversions;
//mod vorbis;
mod wav; mod wav;
/// Trait for objects that produce an audio stream. /// Trait for objects that produce an audio stream.
pub trait Decoder { pub trait Decoder {
/// Appends data to the voice. /// Appends data to the voice.
fn write(&mut self, &mut Voice); fn write(&mut self);
} }
/// Builds a new `Decoder` from a data stream by determining the correct format. /// Builds a new `Decoder` from a data stream by determining the correct format.
pub fn decode<R>(data: R) -> Box<Decoder + Send> where R: Read + Seek + Send + 'static { pub fn decode<R>(endpoint: &Endpoint, data: R) -> Box<Decoder + Send>
let data = match wav::WavDecoder::new(data) { where R: Read + Seek + Send + 'static
{
let data = match wav::WavDecoder::new(endpoint, data) {
Err(data) => data, Err(data) => data,
Ok(decoder) => { Ok(decoder) => {
return Box::new(decoder); return Box::new(decoder);
} }
}; };
if let Ok(decoder) = vorbis::VorbisDecoder::new(data) { /*if let Ok(decoder) = vorbis::VorbisDecoder::new(data) {
return Box::new(decoder); return Box::new(decoder);
} }*/
panic!("Invalid format"); panic!("Invalid format");
} }

View file

@ -1,6 +1,7 @@
use std::io::{Read, Seek}; use std::io::{Read, Seek};
use std::mem; use std::mem;
use super::Decoder; use super::Decoder;
use super::conversions;
use cpal::{self, Voice}; use cpal::{self, Voice};
use vorbis; use vorbis;
@ -49,14 +50,21 @@ impl<R> Decoder for VorbisDecoder<R> where R: Read + Seek {
self.current_packet.as_mut().unwrap() self.current_packet.as_mut().unwrap()
}; };
let mut buffer = voice.append_data(packet.channels, cpal::SamplesRate(packet.rate as u32), let to_channels = voice.get_channels();
packet.data.len()); let to_samples_rate = voice.get_samples_rate();
let mut buffer = voice.append_data(packet.data.len());
let src = mem::replace(&mut packet.data, Vec::new()); let src = mem::replace(&mut packet.data, Vec::new());
conversions::convert_and_write(&src, packet.channels, to_channels,
cpal::SamplesRate(packet.rate as u32), to_samples_rate,
&mut buffer);
/*
let mut src = src.into_iter(); let mut src = src.into_iter();
for (dest, src) in buffer.iter_mut().zip(src.by_ref()) { for (dest, src) in buffer.iter_mut().zip(src.by_ref()) {
*dest = src; *dest = src;
} }
packet.data = src.collect(); packet.data = src.collect();*/
} }
} }

View file

@ -1,51 +1,69 @@
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
use super::Decoder; use super::Decoder;
use super::conversions;
use cpal::{self, Voice}; use cpal::{self, Endpoint, Voice};
use hound::WavIntoSamples;
use hound::WavReader; use hound::WavReader;
use hound::WavSpec; use hound::WavSpec;
pub struct WavDecoder<R> where R: Read { pub struct WavDecoder {
reader: WavReader<R>, reader: Box<Iterator<Item=i16> + Send>,
spec: WavSpec, voice: Voice,
} }
impl<R> WavDecoder<R> where R: Read + Seek { impl WavDecoder {
pub fn new(mut data: R) -> Result<WavDecoder<R>, R> { pub fn new<R>(endpoint: &Endpoint, mut data: R) -> Result<WavDecoder, R> where R: Read + Seek + Send + 'static {
let stream_pos = data.seek(SeekFrom::Current(0)).unwrap(); if !is_wave(data.by_ref()) {
if WavReader::new(data.by_ref()).is_err() {
data.seek(SeekFrom::Start(stream_pos)).unwrap();
return Err(data); return Err(data);
} }
data.seek(SeekFrom::Start(stream_pos)).unwrap();
let reader = WavReader::new(data).unwrap(); let reader = WavReader::new(data).unwrap();
let spec = reader.spec(); let spec = reader.spec();
let voice_format = endpoint.get_supported_formats_list().unwrap().next().unwrap();
let voice = Voice::new(endpoint, &voice_format).unwrap();
let reader = reader.into_samples().map(|s| s.unwrap_or(0));
let reader = conversions::ChannelsCountConverter::new(reader, spec.channels,
voice.get_channels());
let reader = conversions::SamplesRateConverter::new(reader, cpal::SamplesRate(spec.sample_rate),
voice.get_samples_rate());
Ok(WavDecoder { Ok(WavDecoder {
reader: reader, reader: Box::new(reader),
spec: spec, voice: voice,
}) })
} }
} }
impl<R> Decoder for WavDecoder<R> where R: Read { /// Returns true if the stream contains WAV data, then resets it to where it was.
fn write(&mut self, voice: &mut Voice) { fn is_wave<R>(mut data: R) -> bool where R: Read + Seek {
let mut samples = self.reader.samples::<i16>(); let stream_pos = data.seek(SeekFrom::Current(0)).unwrap();
let samples_left = samples.len();
if samples_left == 0 { return; }
// TODO: hack because of a bug in cpal if WavReader::new(data.by_ref()).is_err() {
let samples_left = if samples_left > 512 { 512 } else { samples_left }; data.seek(SeekFrom::Start(stream_pos)).unwrap();
return false;
}
let mut buffer: cpal::Buffer<i16> = data.seek(SeekFrom::Start(stream_pos)).unwrap();
voice.append_data(self.spec.channels, true
cpal::SamplesRate(self.spec.sample_rate), }
samples_left);
for (dest, src) in buffer.iter_mut().zip(&mut samples) { impl Decoder for WavDecoder {
*dest = src.unwrap(); fn write(&mut self) {
} let (min, _) = self.reader.size_hint();
if min == 0 {
// finished
return;
}
{
let mut buffer = self.voice.append_data(min);
conversions::convert_and_write(self.reader.by_ref(), &mut buffer);
}
self.voice.play();
} }
} }

View file

@ -1,9 +1,12 @@
use std::io::{Read, Seek};
use std::thread::{self, Builder}; use std::thread::{self, Builder};
use std::sync::mpsc::{self, Sender, Receiver}; use std::sync::mpsc::{self, Sender, Receiver};
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex; use std::sync::Mutex;
use cpal::Endpoint;
use cpal::Voice; use cpal::Voice;
use decoder;
use decoder::Decoder; use decoder::Decoder;
/// The internal engine of this library. /// The internal engine of this library.
@ -29,9 +32,12 @@ impl Engine {
} }
/// Starts playing a sound and returns a `Handler` to control it. /// Starts playing a sound and returns a `Handler` to control it.
pub fn play(&self, decoder: Box<Decoder + Send>) -> Handle { pub fn play<R>(&self, endpoint: &Endpoint, input: R) -> Handle
let sound_id = self.next_sound_id.fetch_add(1, Ordering::Relaxed); where R: Read + Seek + Send + 'static
{
let decoder = decoder::decode(endpoint, input);
let sound_id = self.next_sound_id.fetch_add(1, Ordering::Relaxed);
let commands = self.commands.lock().unwrap(); let commands = self.commands.lock().unwrap();
commands.send(Command::Play(sound_id, decoder)).unwrap(); commands.send(Command::Play(sound_id, decoder)).unwrap();
@ -63,21 +69,25 @@ pub enum Command {
} }
fn background(rx: Receiver<Command>) { fn background(rx: Receiver<Command>) {
let mut sounds: Vec<(usize, Voice, Box<Decoder + Send>)> = Vec::new(); let mut sounds: Vec<(usize, Box<Decoder + Send>)> = Vec::new();
loop { loop {
// polling for new sounds // polling for new sounds
if let Ok(command) = rx.try_recv() { if let Ok(command) = rx.try_recv() {
match command { match command {
Command::Play(id, decoder) => sounds.push((id, Voice::new(), decoder)), Command::Play(id, decoder) => {
Command::Stop(id) => sounds.retain(|&(id2, _, _)| id2 != id), sounds.push((id, decoder));
},
Command::Stop(id) => {
sounds.retain(|&(id2, _)| id2 != id)
},
} }
} }
// updating the existing sounds // updating the existing sounds
for &mut (_, ref mut voice, ref mut decoder) in sounds.iter_mut() { for &mut (_, ref mut decoder) in sounds.iter_mut() {
decoder.write(voice); decoder.write();
voice.play();
} }
// sleeping a bit? // sleeping a bit?

View file

@ -4,6 +4,8 @@ extern crate hound;
extern crate lazy_static; extern crate lazy_static;
extern crate vorbis; extern crate vorbis;
pub use cpal::{Endpoint, get_endpoints_list, get_default_endpoint};
use std::io::{Read, Seek}; use std::io::{Read, Seek};
mod decoder; mod decoder;
@ -26,7 +28,8 @@ impl Handle {
} }
/// Plays a sound once. Returns a `Handle` that can be used to control the sound. /// Plays a sound once. Returns a `Handle` that can be used to control the sound.
pub fn play_once<R>(input: R) -> Handle where R: Read + Seek + Send + 'static { pub fn play_once<R>(endpoint: &Endpoint, input: R) -> Handle
let decoder = decoder::decode(input); where R: Read + Seek + Send + 'static
Handle(ENGINE.play(decoder)) {
Handle(ENGINE.play(&endpoint, input))
} }