mirror of
https://github.com/RustAudio/rodio
synced 2024-12-13 13:42:34 +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"
|
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
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;
|
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
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 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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue