mirror of
https://github.com/RustAudio/rodio
synced 2024-12-12 13:12:30 +00:00
Update to cpal 0.2
This commit is contained in:
parent
a2081a9e41
commit
89abf72790
12 changed files with 533 additions and 52 deletions
|
@ -9,7 +9,8 @@ repository = "https://github.com/tomaka/rodio"
|
|||
documentation = "http://tomaka.github.io/rodio/rodio/index.html"
|
||||
|
||||
[dependencies]
|
||||
cpal = "0.1.1"
|
||||
hound = "1.0.0"
|
||||
cpal = "0.2.1"
|
||||
#hound = "1.0.0"
|
||||
hound = { git = "https://github.com/tomaka/hound", branch = "wavreader-ownership" }
|
||||
lazy_static = "0.1.12"
|
||||
vorbis = "0.0.12"
|
||||
|
|
3
examples/README.md
Normal file
3
examples/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
## License
|
||||
|
||||
The `music.wav` and `music.ogg` files in this directory are under cc-by-sa.
|
|
@ -1,17 +1,19 @@
|
|||
extern crate rodio;
|
||||
|
||||
fn main() {
|
||||
let endpoint = rodio::get_default_endpoint().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);
|
||||
|
||||
let file = std::fs::File::open("examples/beep2.wav").unwrap();
|
||||
rodio::play_once(file);
|
||||
rodio::play_once(&endpoint, file);
|
||||
|
||||
std::thread::sleep_ms(1000);
|
||||
let file = std::fs::File::open("examples/beep3.ogg").unwrap();
|
||||
rodio::play_once(file);
|
||||
/*let file = std::fs::File::open("examples/beep3.ogg").unwrap();
|
||||
rodio::play_once(&endpoint, file);*/
|
||||
|
||||
std::thread::sleep_ms(1000);
|
||||
beep1.stop();
|
||||
|
|
BIN
examples/music.ogg
Normal file
BIN
examples/music.ogg
Normal file
Binary file not shown.
BIN
examples/music.wav
Normal file
BIN
examples/music.wav
Normal file
Binary file not shown.
10
examples/music_wav.rs
Normal file
10
examples/music_wav.rs
Normal 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
421
src/decoder/conversions.rs
Normal 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]);
|
||||
}
|
||||
}
|
|
@ -1,27 +1,32 @@
|
|||
use std::io::{Read, Seek};
|
||||
|
||||
use cpal::Endpoint;
|
||||
use cpal::Voice;
|
||||
|
||||
mod vorbis;
|
||||
mod conversions;
|
||||
//mod vorbis;
|
||||
mod wav;
|
||||
|
||||
/// Trait for objects that produce an audio stream.
|
||||
pub trait Decoder {
|
||||
/// 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.
|
||||
pub fn decode<R>(data: R) -> Box<Decoder + Send> where R: Read + Seek + Send + 'static {
|
||||
let data = match wav::WavDecoder::new(data) {
|
||||
pub fn decode<R>(endpoint: &Endpoint, data: R) -> Box<Decoder + Send>
|
||||
where R: Read + Seek + Send + 'static
|
||||
{
|
||||
let data = match wav::WavDecoder::new(endpoint, data) {
|
||||
Err(data) => data,
|
||||
Ok(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);
|
||||
}
|
||||
}*/
|
||||
|
||||
panic!("Invalid format");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::io::{Read, Seek};
|
||||
use std::mem;
|
||||
use super::Decoder;
|
||||
use super::conversions;
|
||||
|
||||
use cpal::{self, Voice};
|
||||
use vorbis;
|
||||
|
@ -49,14 +50,21 @@ impl<R> Decoder for VorbisDecoder<R> where R: Read + Seek {
|
|||
self.current_packet.as_mut().unwrap()
|
||||
};
|
||||
|
||||
let mut buffer = voice.append_data(packet.channels, cpal::SamplesRate(packet.rate as u32),
|
||||
packet.data.len());
|
||||
let to_channels = voice.get_channels();
|
||||
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());
|
||||
|
||||
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();
|
||||
for (dest, src) in buffer.iter_mut().zip(src.by_ref()) {
|
||||
*dest = src;
|
||||
}
|
||||
packet.data = src.collect();
|
||||
packet.data = src.collect();*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +1,69 @@
|
|||
use std::io::{Read, Seek, SeekFrom};
|
||||
use super::Decoder;
|
||||
use super::conversions;
|
||||
|
||||
use cpal::{self, Voice};
|
||||
use cpal::{self, Endpoint, Voice};
|
||||
use hound::WavIntoSamples;
|
||||
use hound::WavReader;
|
||||
use hound::WavSpec;
|
||||
|
||||
pub struct WavDecoder<R> where R: Read {
|
||||
reader: WavReader<R>,
|
||||
spec: WavSpec,
|
||||
pub struct WavDecoder {
|
||||
reader: Box<Iterator<Item=i16> + Send>,
|
||||
voice: Voice,
|
||||
}
|
||||
|
||||
impl<R> WavDecoder<R> where R: Read + Seek {
|
||||
pub fn new(mut data: R) -> Result<WavDecoder<R>, R> {
|
||||
let stream_pos = data.seek(SeekFrom::Current(0)).unwrap();
|
||||
|
||||
if WavReader::new(data.by_ref()).is_err() {
|
||||
data.seek(SeekFrom::Start(stream_pos)).unwrap();
|
||||
impl WavDecoder {
|
||||
pub fn new<R>(endpoint: &Endpoint, mut data: R) -> Result<WavDecoder, R> where R: Read + Seek + Send + 'static {
|
||||
if !is_wave(data.by_ref()) {
|
||||
return Err(data);
|
||||
}
|
||||
|
||||
data.seek(SeekFrom::Start(stream_pos)).unwrap();
|
||||
let reader = WavReader::new(data).unwrap();
|
||||
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 {
|
||||
reader: reader,
|
||||
spec: spec,
|
||||
reader: Box::new(reader),
|
||||
voice: voice,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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; }
|
||||
/// Returns true if the stream contains WAV data, then resets it to where it was.
|
||||
fn is_wave<R>(mut data: R) -> bool where R: Read + Seek {
|
||||
let stream_pos = data.seek(SeekFrom::Current(0)).unwrap();
|
||||
|
||||
// TODO: hack because of a bug in cpal
|
||||
let samples_left = if samples_left > 512 { 512 } else { samples_left };
|
||||
if WavReader::new(data.by_ref()).is_err() {
|
||||
data.seek(SeekFrom::Start(stream_pos)).unwrap();
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut buffer: cpal::Buffer<i16> =
|
||||
voice.append_data(self.spec.channels,
|
||||
cpal::SamplesRate(self.spec.sample_rate),
|
||||
samples_left);
|
||||
data.seek(SeekFrom::Start(stream_pos)).unwrap();
|
||||
true
|
||||
}
|
||||
|
||||
for (dest, src) in buffer.iter_mut().zip(&mut samples) {
|
||||
*dest = src.unwrap();
|
||||
impl Decoder for WavDecoder {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
use std::io::{Read, Seek};
|
||||
use std::thread::{self, Builder};
|
||||
use std::sync::mpsc::{self, Sender, Receiver};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use cpal::Endpoint;
|
||||
use cpal::Voice;
|
||||
use decoder;
|
||||
use decoder::Decoder;
|
||||
|
||||
/// The internal engine of this library.
|
||||
|
@ -29,9 +32,12 @@ impl Engine {
|
|||
}
|
||||
|
||||
/// Starts playing a sound and returns a `Handler` to control it.
|
||||
pub fn play(&self, decoder: Box<Decoder + Send>) -> Handle {
|
||||
let sound_id = self.next_sound_id.fetch_add(1, Ordering::Relaxed);
|
||||
pub fn play<R>(&self, endpoint: &Endpoint, input: R) -> Handle
|
||||
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();
|
||||
commands.send(Command::Play(sound_id, decoder)).unwrap();
|
||||
|
||||
|
@ -63,21 +69,25 @@ pub enum 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 {
|
||||
// polling for new sounds
|
||||
if let Ok(command) = rx.try_recv() {
|
||||
match command {
|
||||
Command::Play(id, decoder) => sounds.push((id, Voice::new(), decoder)),
|
||||
Command::Stop(id) => sounds.retain(|&(id2, _, _)| id2 != id),
|
||||
Command::Play(id, decoder) => {
|
||||
sounds.push((id, decoder));
|
||||
},
|
||||
|
||||
Command::Stop(id) => {
|
||||
sounds.retain(|&(id2, _)| id2 != id)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// updating the existing sounds
|
||||
for &mut (_, ref mut voice, ref mut decoder) in sounds.iter_mut() {
|
||||
decoder.write(voice);
|
||||
voice.play();
|
||||
for &mut (_, ref mut decoder) in sounds.iter_mut() {
|
||||
decoder.write();
|
||||
}
|
||||
|
||||
// sleeping a bit?
|
||||
|
|
|
@ -4,6 +4,8 @@ extern crate hound;
|
|||
extern crate lazy_static;
|
||||
extern crate vorbis;
|
||||
|
||||
pub use cpal::{Endpoint, get_endpoints_list, get_default_endpoint};
|
||||
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
mod decoder;
|
||||
|
@ -26,7 +28,8 @@ impl Handle {
|
|||
}
|
||||
|
||||
/// 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 {
|
||||
let decoder = decoder::decode(input);
|
||||
Handle(ENGINE.play(decoder))
|
||||
pub fn play_once<R>(endpoint: &Endpoint, input: R) -> Handle
|
||||
where R: Read + Seek + Send + 'static
|
||||
{
|
||||
Handle(ENGINE.play(&endpoint, input))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue