Start using one voice per endpoint and mixing samples manually

This commit is contained in:
Pierre Krieger 2015-09-27 09:56:29 +02:00
parent a6b5197a59
commit 92a4567f08
6 changed files with 174 additions and 162 deletions

View file

@ -4,10 +4,8 @@ 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::iter;
use cpal::UnknownTypeBuffer;
pub use self::sample::Sample;
pub use self::sample::DataConverter;
pub use self::channels::ChannelsCountConverter;
pub use self::samples_rate::SamplesRateConverter;
pub use self::amplifier::AmplifierIterator;
@ -16,34 +14,3 @@ mod amplifier;
mod channels;
mod sample;
mod samples_rate;
///
pub fn convert_and_write<I, S>(samples: I, output: &mut UnknownTypeBuffer)
where I: Iterator<Item=S>, S: Sample
{
let samples = samples.chain(iter::repeat(Sample::zero_value()));
// note that it is important to do `buffer.zip(samples)` instead of `samples.zip(buffer)`
// otherwise when the buffer's iterator is exhausted the value obtained from `samples` is
// discarded
match output {
&mut UnknownTypeBuffer::U16(ref mut buffer) => {
for (o, i) in buffer.iter_mut().zip(samples) {
*o = i.to_u16();
}
},
&mut UnknownTypeBuffer::I16(ref mut buffer) => {
for (o, i) in buffer.iter_mut().zip(samples) {
*o = i.to_i16();
}
},
&mut UnknownTypeBuffer::F32(ref mut buffer) => {
for (o, i) in buffer.iter_mut().zip(samples) {
*o = i.to_f32();
}
},
}
}

View file

@ -1,5 +1,41 @@
use std::marker::PhantomData;
use cpal;
/// Converts the samples data type to `O`.
pub struct DataConverter<I, O> {
input: I,
marker: PhantomData<O>,
}
impl<I, O> DataConverter<I, O> {
/// Builds a new converter.
#[inline]
pub fn new(input: I) -> DataConverter<I, O> {
DataConverter {
input: input,
marker: PhantomData,
}
}
}
impl<I, O> Iterator for DataConverter<I, O> where I: Iterator, I::Item: Sample, O: Sample {
type Item = O;
#[inline]
fn next(&mut self) -> Option<O> {
self.input.next().map(|s| Sample::from(&s))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I, O> ExactSizeIterator for DataConverter<I, O>
where I: ExactSizeIterator, I::Item: Sample, O: Sample {}
/// Trait for containers that contain PCM data.
pub trait Sample: cpal::Sample {
fn lerp(first: Self, second: Self, numerator: u32, denominator: u32) -> Self;
@ -10,6 +46,8 @@ pub trait Sample: cpal::Sample {
fn to_i16(&self) -> i16;
fn to_u16(&self) -> u16;
fn to_f32(&self) -> f32;
fn from<S>(&S) -> Self where S: Sample;
}
impl Sample for u16 {
@ -46,6 +84,11 @@ impl Sample for u16 {
fn to_f32(&self) -> f32 {
self.to_i16().to_f32()
}
#[inline]
fn from<S>(sample: &S) -> Self where S: Sample {
sample.to_u16()
}
}
impl Sample for i16 {
@ -86,6 +129,11 @@ impl Sample for i16 {
*self as f32 / ::std::i16::MAX as f32
}
}
#[inline]
fn from<S>(sample: &S) -> Self where S: Sample {
sample.to_i16()
}
}
impl Sample for f32 {
@ -122,6 +170,11 @@ impl Sample for f32 {
fn to_f32(&self) -> f32 {
*self
}
#[inline]
fn from<S>(sample: &S) -> Self where S: Sample {
sample.to_f32()
}
}
#[cfg(test)]

View file

@ -8,34 +8,27 @@ mod vorbis;
mod wav;
/// Trait for objects that produce an audio stream.
pub trait Decoder {
/// Appends 17ms of data to the voice.
///
/// Returns false if the sound is over.
fn write(&mut self) -> bool;
pub trait Decoder: Iterator /*+ ExactSizeIterator*/ { // TODO: should be exact size, but not enforced yet
/// Changes the volume of the sound.
fn set_volume(&mut self, f32);
/// Returns the total duration of the second in milliseconds.
fn get_total_duration_ms(&self) -> u32;
/// Returns the number of milliseconds before the end of the sound.
fn get_remaining_duration_ms(&self) -> u32;
}
/// Builds a new `Decoder` from a data stream by determining the correct format.
pub fn decode<R>(endpoint: &Endpoint, data: R) -> Arc<Mutex<Decoder + Send>>
pub fn decode<R>(data: R, output_channels: u16, output_samples_rate: u32)
-> Arc<Mutex<Decoder<Item=f32> + Send>>
where R: Read + Seek + Send + 'static
{
let data = match wav::WavDecoder::new(endpoint, data) {
let data = match wav::WavDecoder::new(data, output_channels, output_samples_rate) {
Err(data) => data,
Ok(decoder) => {
return Arc::new(Mutex::new(decoder));
}
};
if let Ok(decoder) = vorbis::VorbisDecoder::new(endpoint, data) {
if let Ok(decoder) = vorbis::VorbisDecoder::new(data, output_channels, output_samples_rate) {
return Arc::new(Mutex::new(decoder));
}

View file

@ -7,12 +7,12 @@ use cpal::{self, Endpoint, Voice};
use vorbis;
pub struct VorbisDecoder {
reader: conversions::AmplifierIterator<Box<Iterator<Item=i16> + Send>>,
voice: Voice,
reader: conversions::AmplifierIterator<Box<Iterator<Item=f32> + Send>>,
}
impl VorbisDecoder {
pub fn new<R>(endpoint: &Endpoint, data: R) -> Result<VorbisDecoder, ()>
pub fn new<R>(data: R, output_channels: u16, output_samples_rate: u32)
-> Result<VorbisDecoder, ()>
where R: Read + Seek + Send + 'static
{
let decoder = match vorbis::Decoder::new(data) {
@ -20,43 +20,23 @@ impl VorbisDecoder {
Ok(r) => r
};
// building the voice
let voice_format = endpoint.get_supported_formats_list().unwrap().next().unwrap();
let voice = Voice::new(endpoint, &voice_format).unwrap();
let to_channels = voice.get_channels();
let to_samples_rate = voice.get_samples_rate();
let reader = decoder.into_packets().filter_map(|p| p.ok()).flat_map(move |packet| {
let reader = packet.data.into_iter();
let reader = conversions::ChannelsCountConverter::new(reader, packet.channels,
to_channels);
output_channels);
let reader = conversions::SamplesRateConverter::new(reader, cpal::SamplesRate(packet.rate as u32),
to_samples_rate, to_channels);
cpal::SamplesRate(output_samples_rate), output_channels);
let reader = conversions::DataConverter::new(reader);
reader
});
Ok(VorbisDecoder {
reader: conversions::AmplifierIterator::new(Box::new(reader), 1.0),
voice: voice,
})
}
}
impl Decoder for VorbisDecoder {
fn write(&mut self) -> bool {
// TODO: handle end
{
let samples = self.voice.get_samples_rate().0 * self.voice.get_channels() as u32;
let mut buffer = self.voice.append_data(samples as usize);
conversions::convert_and_write(self.reader.by_ref(), &mut buffer);
}
self.voice.play();
true
}
fn set_volume(&mut self, value: f32) {
self.reader.set_amplification(value);
}
@ -64,8 +44,20 @@ impl Decoder for VorbisDecoder {
fn get_total_duration_ms(&self) -> u32 {
unimplemented!()
}
}
fn get_remaining_duration_ms(&self) -> u32 {
unimplemented!()
impl Iterator for VorbisDecoder {
type Item = f32;
#[inline]
fn next(&mut self) -> Option<f32> {
self.reader.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.reader.size_hint()
}
}
impl ExactSizeIterator for VorbisDecoder {}

View file

@ -7,13 +7,13 @@ use cpal::{self, Endpoint, Voice};
use hound::WavReader;
pub struct WavDecoder {
reader: conversions::AmplifierIterator<Box<Iterator<Item=i16> + Send>>,
voice: Voice,
reader: conversions::AmplifierIterator<Box<Iterator<Item=f32> + Send>>,
total_duration_ms: u32,
}
impl WavDecoder {
pub fn new<R>(endpoint: &Endpoint, mut data: R) -> Result<WavDecoder, R>
pub fn new<R>(mut data: R, output_channels: u16, output_samples_rate: u32)
-> Result<WavDecoder, R>
where R: Read + Seek + Send + 'static
{
if !is_wave(data.by_ref()) {
@ -24,52 +24,14 @@ impl WavDecoder {
let spec = reader.spec();
let total_duration_ms = reader.duration() * 1000 / spec.sample_rate;
// choosing a format amongst the ones available
let voice_format = endpoint.get_supported_formats_list().unwrap().fold(None, |f1, f2| {
if f1.is_none() {
return Some(f2);
}
let f1 = f1.unwrap();
if f1.samples_rate.0 % spec.sample_rate == 0 {
return Some(f1);
}
if f2.samples_rate.0 % spec.sample_rate == 0 {
return Some(f2);
}
if f1.channels.len() >= spec.channels as usize {
return Some(f1);
}
if f2.channels.len() >= spec.channels as usize {
return Some(f2);
}
if f1.data_type == cpal::SampleFormat::I16 {
return Some(f1);
}
if f2.data_type == cpal::SampleFormat::I16 {
return Some(f2);
}
Some(f1)
}).unwrap();
let voice = Voice::new(endpoint, &voice_format).unwrap();
let reader = SamplesIterator { reader: reader, samples_read: 0 };
let reader = conversions::ChannelsCountConverter::new(reader, spec.channels,
voice.get_channels());
let reader = conversions::ChannelsCountConverter::new(reader, spec.channels, 2);
let reader = conversions::SamplesRateConverter::new(reader, cpal::SamplesRate(spec.sample_rate),
voice.get_samples_rate(), voice.get_channels());
cpal::SamplesRate(output_samples_rate), output_channels);
let reader = conversions::DataConverter::new(reader);
Ok(WavDecoder {
reader: conversions::AmplifierIterator::new(Box::new(reader), 1.0),
voice: voice,
total_duration_ms: total_duration_ms,
})
}
@ -116,21 +78,6 @@ fn is_wave<R>(mut data: R) -> bool where R: Read + Seek {
}
impl Decoder for WavDecoder {
fn write(&mut self) -> bool {
if let (0, _) = self.reader.size_hint() {
return false;
}
{
let samples = self.voice.get_samples_rate().0 * self.voice.get_channels() as u32;
let mut buffer = self.voice.append_data(samples as usize);
conversions::convert_and_write(self.reader.by_ref(), &mut buffer);
}
self.voice.play();
true
}
fn set_volume(&mut self, value: f32) {
self.reader.set_amplification(value);
}
@ -138,12 +85,20 @@ impl Decoder for WavDecoder {
fn get_total_duration_ms(&self) -> u32 {
self.total_duration_ms
}
}
fn get_remaining_duration_ms(&self) -> u32 {
let (num_samples, _) = self.reader.size_hint();
let num_samples = num_samples + self.voice.get_pending_samples();
impl Iterator for WavDecoder {
type Item = f32;
(num_samples as u64 * 1000 /
(self.voice.get_samples_rate().0 as u64 * self.voice.get_channels() as u64)) as u32
#[inline]
fn next(&mut self) -> Option<f32> {
self.reader.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.reader.size_hint()
}
}
impl ExactSizeIterator for WavDecoder {}

View file

@ -1,14 +1,18 @@
use std::cmp;
use std::mem;
use std::collections::HashMap;
use std::io::{Read, Seek};
use std::thread::{self, Builder, Thread};
use std::sync::mpsc::{self, Sender, Receiver};
use std::sync::Arc;
use std::sync::Mutex;
use cpal::UnknownTypeBuffer;
use cpal::Voice;
use cpal::Endpoint;
use decoder;
use decoder::Decoder;
use conversions::Sample;
use time;
@ -44,10 +48,10 @@ impl Engine {
pub fn play<R>(&self, endpoint: &Endpoint, input: R) -> Handle
where R: Read + Seek + Send + 'static
{
let decoder = decoder::decode(endpoint, input);
let decoder = decoder::decode(input, 2, 44100);
let commands = self.commands.lock().unwrap();
commands.send(Command::Play(decoder.clone())).unwrap();
commands.send(Command::Play(endpoint.clone(), decoder.clone())).unwrap();
if let Some(ref thread) = self.thread {
thread.unpark();
@ -65,7 +69,7 @@ impl Engine {
/// Note that dropping the handle doesn't stop the sound. You must call `stop` explicitely.
pub struct Handle<'a> {
engine: &'a Engine,
decoder: Arc<Mutex<Decoder + Send>>,
decoder: Arc<Mutex<Decoder<Item=f32> + Send>>,
}
impl<'a> Handle<'a> {
@ -101,41 +105,61 @@ impl<'a> Handle<'a> {
#[inline]
pub fn get_remaining_duration_ms(&self) -> u32 {
let decoder = self.decoder.lock().unwrap();
decoder.get_remaining_duration_ms()
let (num_samples, _) = decoder.size_hint();
//let num_samples = num_samples + self.voice.get_pending_samples(); // TODO: !
(num_samples as u64 * 1000 / 44100 * 2) as u32 // FIXME: arbitrary values
}
}
pub enum Command {
Play(Arc<Mutex<Decoder + Send>>),
Stop(Arc<Mutex<Decoder + Send>>),
SetVolume(Arc<Mutex<Decoder + Send>>, f32),
Play(Endpoint, Arc<Mutex<Decoder<Item=f32> + Send>>),
Stop(Arc<Mutex<Decoder<Item=f32> + Send>>),
SetVolume(Arc<Mutex<Decoder<Item=f32> + Send>>, f32),
}
fn background(rx: Receiver<Command>) {
let mut sounds: Vec<Arc<Mutex<Decoder + Send>>> = Vec::new();
let mut sounds_to_remove: Vec<Arc<Mutex<Decoder + Send>>> = Vec::new();
// for each endpoint name, stores the voice and the list of sounds
let mut voices: HashMap<String, (Voice, Vec<Arc<Mutex<Decoder<Item=f32> + Send>>>)> = HashMap::new();
// list of sounds to stop playing
let mut sounds_to_remove: Vec<Arc<Mutex<Decoder<Item=f32> + Send>>> = Vec::new();
loop {
// polling for new commands
if let Ok(command) = rx.try_recv() {
match command {
Command::Play(decoder) => {
sounds.push(decoder);
Command::Play(endpoint, decoder) => {
let mut entry = voices.entry(endpoint.get_name()).or_insert_with(|| {
// TODO: handle possible errors here
// TODO: choose format better
let format = endpoint.get_supported_formats_list().unwrap().next().unwrap();
let voice = Voice::new(&endpoint, &format).unwrap();
(voice, Vec::new())
});
entry.1.push(decoder);
},
Command::Stop(decoder) => {
let decoder = &*decoder as *const _;
sounds.retain(|dec| {
&**dec as *const _ != decoder
})
for (_, &mut (_, ref mut sounds)) in voices.iter_mut() {
sounds.retain(|dec| {
&**dec as *const _ != decoder
})
}
},
Command::SetVolume(decoder, volume) => {
let decoder = &*decoder as *const _;
if let Some(d) = sounds.iter_mut()
.find(|dec| &***dec as *const _ != decoder)
{
d.lock().unwrap().set_volume(volume);
for (_, &mut (_, ref mut sounds)) in voices.iter_mut() {
if let Some(d) = sounds.iter_mut()
.find(|dec| &***dec as *const _ != decoder)
{
d.lock().unwrap().set_volume(volume);
}
}
},
}
@ -144,16 +168,44 @@ fn background(rx: Receiver<Command>) {
// removing sounds that have finished playing
for decoder in mem::replace(&mut sounds_to_remove, Vec::new()) {
let decoder = &*decoder as *const _;
sounds.retain(|dec| &**dec as *const _ != decoder)
for (_, &mut (_, ref mut sounds)) in voices.iter_mut() {
sounds.retain(|dec| &**dec as *const _ != decoder);
}
}
let before_updates = time::precise_time_ns();
// updating the existing sounds
for decoder in &sounds {
if !decoder.lock().unwrap().write() {
sounds_to_remove.push(decoder.clone());
let before_updates = time::precise_time_ns();
for (_, &mut (ref mut voice, ref mut sounds)) in voices.iter_mut() {
// building an iterator that produces samples from `sounds`
let num_sounds = sounds.len() as f32;
let samples_iter = (0..).map(|_| {
// FIXME: locking is slow
sounds.iter().map(|s| s.lock().unwrap().next().unwrap_or(0.0) / num_sounds)
.fold(0.0, |a, b| a + b)
});
// starting the output
{
let mut buffer = {
let samples_to_write = voice.get_samples_rate().0 * voice.get_channels() as u32;
voice.append_data(samples_to_write as usize)
};
match buffer {
UnknownTypeBuffer::U16(ref mut buffer) => {
for (o, i) in buffer.iter_mut().zip(samples_iter) { *o = i.to_u16(); }
},
UnknownTypeBuffer::I16(ref mut buffer) => {
for (o, i) in buffer.iter_mut().zip(samples_iter) { *o = i.to_i16(); }
},
UnknownTypeBuffer::F32(ref mut buffer) => {
for (o, i) in buffer.iter_mut().zip(samples_iter) { *o = i; }
},
}
}
// TODO: do better
voice.play();
}
// sleeping so that we get a loop every 17ms