diff --git a/playback/src/audio_backend/cpal.rs b/playback/src/audio_backend/cpal.rs index e493c8ac..693ee88d 100644 --- a/playback/src/audio_backend/cpal.rs +++ b/playback/src/audio_backend/cpal.rs @@ -2,18 +2,21 @@ use super::{Open, Sink}; extern crate cpal; use std::io; use std::thread; -use std::collections::VecDeque; +use std::sync::mpsc::{sync_channel, SyncSender}; pub struct CpalSink { - event_loop: cpal::EventLoop, - buffer: mut VecDeque, - stream_id: Option, + // event_loop: cpal::EventLoop, + send: SyncSender, } impl Open for CpalSink { fn open(device: Option) -> CpalSink { info!("Using cpal sink"); + // buffer for samples from librespot (~10ms) + let (tx, rx) = sync_channel::(2 * 1024 * 4); + let event_loop = cpal::EventLoop::new(); + if device.is_some() { // N.B. This is perfectly possible to support. // TODO: First need to enable listing of devices. @@ -21,72 +24,49 @@ impl Open for CpalSink { // TODO: Choose cpal sink by name. panic!("cpal sink does not support specifying a device name"); } + let cpal_device = cpal::default_output_device().expect("no output device available"); + // TODO: Support more formats? Surely cpal will handle that. + let format = cpal::Format{channels: 2, sample_rate: cpal::SampleRate(44100), data_type: cpal::SampleFormat::I16}; - let event_loop = cpal::EventLoop::new(); + let stream_id = event_loop.build_output_stream(&cpal_device, &format).expect("could not build output stream"); + event_loop.play_stream(stream_id); + + thread::spawn(move |/*event_loop, rx*/| { + event_loop.run(move |_stream_id, stream_data| { + match stream_data { + cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::I16(mut buffer) } => { + for (sample, recv) in buffer.iter_mut().zip(rx.try_iter()) { + *sample = recv; + } + }, + _ => (), + } + }); + }); CpalSink { - // Allow an (arbitrary) 2 second buffer before resizing. - buffer: VecDeque::with_capacity(44100 * 2 * 2), - event_loop: event_loop, + send: tx, + // event_loop: event_loop, } } } impl Sink for CpalSink { fn start(&mut self) -> io::Result<()> { - - if self.stream_id.is_none() { - - let device = cpal::default_output_device().expect("no output device available"); - // TODO: Support more formats. - let format = cpal::Format(2, 44100, cpal::SampleFormat::I16); - - self.stream_id = self.event_loop.build_output_stream(&device, &format)?; - - self.event_loop.play_stream(self.stream_id.clone()); - } - - if self.thread.is_none() { - let event_loop = self.event_loop; - let source = self.buffer; - thread::spawn(move |event_loop, source| { - event_loop.run(move |_stream_id, mut stream_data| { - match data { - cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::I16(mut buffer) } => { - let sl = source.len(); - if (sl > buffer.len()) { - sl = buffer.len(); - } - // let u: Vec<_> = source.drain(..sl).collect(); - // buffer[..s1].copy_from_slice(u[..s1]); - - for (sample, data) in buffer.iter_mut().zip(source.drain(..sl)) { - *sample = data; - } - }, - _ => (), - } - }); - }) - } - Ok(()) } fn stop(&mut self) -> io::Result<()> { - if !self.stream_id.is_none() { - self.event_loop.destroy_stream(self.stream_id); - self.stream_id = None; - self.buffer.clear(); - } Ok(()) } fn write(&mut self, data: &[i16]) -> io::Result<()> { - // self.0.as_mut().unwrap().write_interleaved(&data).unwrap(); - // self.buffer.reserve(data.len()); // Unneccessary? - // self.buffer.extend_from_slice(data); - self.buffer.extend(data); + for s in data.iter() { + let res = self.send.send(*s); + if res.is_err() { + error!("jackaudio: cannot write to channel"); + } + } Ok(()) } }