cpal backend builds. Panics building output stream on my system.

This commit is contained in:
Will Stott 2018-11-15 20:55:52 +00:00
parent 891298171c
commit ac9423d9d9

View file

@ -2,18 +2,21 @@ use super::{Open, Sink};
extern crate cpal; extern crate cpal;
use std::io; use std::io;
use std::thread; use std::thread;
use std::collections::VecDeque; use std::sync::mpsc::{sync_channel, SyncSender};
pub struct CpalSink { pub struct CpalSink {
event_loop: cpal::EventLoop, // event_loop: cpal::EventLoop,
buffer: mut VecDeque<i16>, send: SyncSender<i16>,
stream_id: Option<cpal::StreamId>,
} }
impl Open for CpalSink { impl Open for CpalSink {
fn open(device: Option<String>) -> CpalSink { fn open(device: Option<String>) -> CpalSink {
info!("Using cpal sink"); info!("Using cpal sink");
// buffer for samples from librespot (~10ms)
let (tx, rx) = sync_channel::<i16>(2 * 1024 * 4);
let event_loop = cpal::EventLoop::new();
if device.is_some() { if device.is_some() {
// N.B. This is perfectly possible to support. // N.B. This is perfectly possible to support.
// TODO: First need to enable listing of devices. // TODO: First need to enable listing of devices.
@ -21,72 +24,49 @@ impl Open for CpalSink {
// TODO: Choose cpal sink by name. // TODO: Choose cpal sink by name.
panic!("cpal sink does not support specifying a device 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 { CpalSink {
// Allow an (arbitrary) 2 second buffer before resizing. send: tx,
buffer: VecDeque::with_capacity(44100 * 2 * 2), // event_loop: event_loop,
event_loop: event_loop,
} }
} }
} }
impl Sink for CpalSink { impl Sink for CpalSink {
fn start(&mut self) -> io::Result<()> { 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(()) Ok(())
} }
fn stop(&mut self) -> io::Result<()> { 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(()) Ok(())
} }
fn write(&mut self, data: &[i16]) -> io::Result<()> { fn write(&mut self, data: &[i16]) -> io::Result<()> {
// self.0.as_mut().unwrap().write_interleaved(&data).unwrap(); for s in data.iter() {
// self.buffer.reserve(data.len()); // Unneccessary? let res = self.send.send(*s);
// self.buffer.extend_from_slice(data); if res.is_err() {
self.buffer.extend(data); error!("jackaudio: cannot write to channel");
}
}
Ok(()) Ok(())
} }
} }