2015-03-26 09:03:13 +00:00
|
|
|
extern crate alsa_sys as alsa;
|
2014-12-16 15:45:45 +00:00
|
|
|
extern crate libc;
|
|
|
|
|
2017-10-12 09:54:09 +00:00
|
|
|
pub use self::enumerate::{EndpointsIterator, default_endpoint};
|
2015-09-01 15:15:49 +00:00
|
|
|
|
2015-09-10 10:43:37 +00:00
|
|
|
use ChannelPosition;
|
2015-09-01 15:15:49 +00:00
|
|
|
use CreationError;
|
|
|
|
use Format;
|
|
|
|
use FormatsEnumerationError;
|
|
|
|
use SampleFormat;
|
|
|
|
use SamplesRate;
|
2017-10-20 19:18:40 +00:00
|
|
|
use SupportedFormat;
|
2016-08-02 20:28:37 +00:00
|
|
|
use UnknownTypeBuffer;
|
2015-09-01 15:15:49 +00:00
|
|
|
|
2017-10-11 11:24:49 +00:00
|
|
|
use std::{cmp, ffi, iter, mem, ptr};
|
2017-11-02 09:30:15 +00:00
|
|
|
use std::sync::Mutex;
|
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
2017-10-11 11:24:49 +00:00
|
|
|
use std::vec::IntoIter as VecIntoIter;
|
2016-08-02 20:28:37 +00:00
|
|
|
|
2017-10-20 19:18:40 +00:00
|
|
|
pub type SupportedFormatsIterator = VecIntoIter<SupportedFormat>;
|
2015-09-01 15:15:49 +00:00
|
|
|
|
|
|
|
mod enumerate;
|
|
|
|
|
2017-07-13 11:58:01 +00:00
|
|
|
|
|
|
|
struct Trigger {
|
|
|
|
// [read fd, write fd]
|
|
|
|
fds: [libc::c_int; 2],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Trigger {
|
|
|
|
fn new() -> Self {
|
2017-10-11 11:24:49 +00:00
|
|
|
let mut fds = [0, 0];
|
2017-07-13 11:58:01 +00:00
|
|
|
match unsafe { libc::pipe(fds.as_mut_ptr()) } {
|
|
|
|
0 => Trigger { fds: fds },
|
2017-10-11 11:24:49 +00:00
|
|
|
_ => panic!("Could not create pipe"),
|
2017-07-13 11:58:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
fn read_fd(&self) -> libc::c_int {
|
|
|
|
self.fds[0]
|
|
|
|
}
|
|
|
|
fn write_fd(&self) -> libc::c_int {
|
|
|
|
self.fds[1]
|
|
|
|
}
|
|
|
|
fn wakeup(&self) {
|
|
|
|
let buf = 1u64;
|
|
|
|
let ret = unsafe { libc::write(self.write_fd(), &buf as *const u64 as *const _, 8) };
|
|
|
|
assert!(ret == 8);
|
|
|
|
}
|
|
|
|
fn clear_pipe(&self) {
|
|
|
|
let mut out = 0u64;
|
|
|
|
let ret = unsafe { libc::read(self.read_fd(), &mut out as *mut u64 as *mut _, 8) };
|
|
|
|
assert_eq!(ret, 8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for Trigger {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
libc::close(self.fds[0]);
|
|
|
|
libc::close(self.fds[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-01 15:15:49 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub struct Endpoint(String);
|
|
|
|
|
|
|
|
impl Endpoint {
|
2017-10-23 14:41:38 +00:00
|
|
|
pub fn supported_formats(&self) -> Result<SupportedFormatsIterator, FormatsEnumerationError> {
|
2015-09-10 17:48:39 +00:00
|
|
|
unsafe {
|
|
|
|
let mut playback_handle = mem::uninitialized();
|
2016-01-15 20:16:34 +00:00
|
|
|
let device_name = ffi::CString::new(self.0.clone()).expect("Unable to get device name");
|
2015-09-22 13:46:56 +00:00
|
|
|
|
|
|
|
match alsa::snd_pcm_open(&mut playback_handle, device_name.as_ptr() as *const _,
|
|
|
|
alsa::SND_PCM_STREAM_PLAYBACK, alsa::SND_PCM_NONBLOCK)
|
2016-01-28 20:27:09 +00:00
|
|
|
{
|
2015-10-13 10:22:07 +00:00
|
|
|
-2 |
|
2015-09-22 13:46:56 +00:00
|
|
|
-16 /* determined empirically */ => return Err(FormatsEnumerationError::DeviceNotAvailable),
|
2016-01-15 20:16:34 +00:00
|
|
|
e => check_errors(e).expect("device not available")
|
2015-09-22 13:46:56 +00:00
|
|
|
}
|
2015-09-10 17:48:39 +00:00
|
|
|
|
|
|
|
let hw_params = HwParams::alloc();
|
2015-09-22 13:46:56 +00:00
|
|
|
match check_errors(alsa::snd_pcm_hw_params_any(playback_handle, hw_params.0)) {
|
|
|
|
Err(_) => return Ok(Vec::new().into_iter()),
|
2017-10-11 11:24:49 +00:00
|
|
|
Ok(_) => (),
|
2015-09-22 13:46:56 +00:00
|
|
|
};
|
2015-09-10 17:48:39 +00:00
|
|
|
|
2015-09-22 13:20:11 +00:00
|
|
|
// TODO: check endianess
|
2017-10-11 11:24:49 +00:00
|
|
|
const FORMATS: [(SampleFormat, alsa::snd_pcm_format_t); 3] =
|
|
|
|
[
|
|
|
|
//SND_PCM_FORMAT_S8,
|
|
|
|
//SND_PCM_FORMAT_U8,
|
|
|
|
(SampleFormat::I16, alsa::SND_PCM_FORMAT_S16_LE),
|
|
|
|
//SND_PCM_FORMAT_S16_BE,
|
|
|
|
(SampleFormat::U16, alsa::SND_PCM_FORMAT_U16_LE),
|
|
|
|
//SND_PCM_FORMAT_U16_BE,
|
2015-09-10 17:48:39 +00:00
|
|
|
/*SND_PCM_FORMAT_S24_LE,
|
|
|
|
SND_PCM_FORMAT_S24_BE,
|
|
|
|
SND_PCM_FORMAT_U24_LE,
|
|
|
|
SND_PCM_FORMAT_U24_BE,
|
|
|
|
SND_PCM_FORMAT_S32_LE,
|
|
|
|
SND_PCM_FORMAT_S32_BE,
|
|
|
|
SND_PCM_FORMAT_U32_LE,
|
|
|
|
SND_PCM_FORMAT_U32_BE,*/
|
2017-10-23 14:41:38 +00:00
|
|
|
(SampleFormat::F32, alsa::SND_PCM_FORMAT_FLOAT_LE) /*SND_PCM_FORMAT_FLOAT_BE,
|
2015-09-10 17:48:39 +00:00
|
|
|
SND_PCM_FORMAT_FLOAT64_LE,
|
|
|
|
SND_PCM_FORMAT_FLOAT64_BE,
|
|
|
|
SND_PCM_FORMAT_IEC958_SUBFRAME_LE,
|
|
|
|
SND_PCM_FORMAT_IEC958_SUBFRAME_BE,
|
|
|
|
SND_PCM_FORMAT_MU_LAW,
|
|
|
|
SND_PCM_FORMAT_A_LAW,
|
|
|
|
SND_PCM_FORMAT_IMA_ADPCM,
|
|
|
|
SND_PCM_FORMAT_MPEG,
|
|
|
|
SND_PCM_FORMAT_GSM,
|
|
|
|
SND_PCM_FORMAT_SPECIAL,
|
|
|
|
SND_PCM_FORMAT_S24_3LE,
|
|
|
|
SND_PCM_FORMAT_S24_3BE,
|
|
|
|
SND_PCM_FORMAT_U24_3LE,
|
|
|
|
SND_PCM_FORMAT_U24_3BE,
|
|
|
|
SND_PCM_FORMAT_S20_3LE,
|
|
|
|
SND_PCM_FORMAT_S20_3BE,
|
|
|
|
SND_PCM_FORMAT_U20_3LE,
|
|
|
|
SND_PCM_FORMAT_U20_3BE,
|
|
|
|
SND_PCM_FORMAT_S18_3LE,
|
|
|
|
SND_PCM_FORMAT_S18_3BE,
|
|
|
|
SND_PCM_FORMAT_U18_3LE,
|
2017-10-11 11:24:49 +00:00
|
|
|
SND_PCM_FORMAT_U18_3BE,*/,
|
|
|
|
];
|
2015-09-10 17:48:39 +00:00
|
|
|
|
|
|
|
let mut supported_formats = Vec::new();
|
|
|
|
for &(sample_format, alsa_format) in FORMATS.iter() {
|
2017-10-11 11:24:49 +00:00
|
|
|
if alsa::snd_pcm_hw_params_test_format(playback_handle,
|
|
|
|
hw_params.0,
|
|
|
|
alsa_format) == 0
|
|
|
|
{
|
2015-09-10 17:48:39 +00:00
|
|
|
supported_formats.push(sample_format);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut min_rate = mem::uninitialized();
|
2017-10-11 11:24:49 +00:00
|
|
|
check_errors(alsa::snd_pcm_hw_params_get_rate_min(hw_params.0,
|
|
|
|
&mut min_rate,
|
|
|
|
ptr::null_mut()))
|
|
|
|
.expect("unable to get minimum supported rete");
|
2015-09-10 17:48:39 +00:00
|
|
|
let mut max_rate = mem::uninitialized();
|
2017-10-11 11:24:49 +00:00
|
|
|
check_errors(alsa::snd_pcm_hw_params_get_rate_max(hw_params.0,
|
|
|
|
&mut max_rate,
|
|
|
|
ptr::null_mut()))
|
|
|
|
.expect("unable to get maximum supported rate");
|
2015-09-10 17:48:39 +00:00
|
|
|
|
|
|
|
let samples_rates = if min_rate == max_rate {
|
2017-10-20 19:18:40 +00:00
|
|
|
vec![(min_rate, max_rate)]
|
2017-10-23 14:41:38 +00:00
|
|
|
} else if alsa::snd_pcm_hw_params_test_rate(playback_handle,
|
|
|
|
hw_params.0,
|
|
|
|
min_rate + 1,
|
|
|
|
0) == 0
|
|
|
|
{
|
2017-10-20 19:18:40 +00:00
|
|
|
vec![(min_rate, max_rate)]
|
2015-09-10 17:48:39 +00:00
|
|
|
} else {
|
|
|
|
const RATES: [libc::c_uint; 13] = [
|
|
|
|
5512,
|
|
|
|
8000,
|
|
|
|
11025,
|
|
|
|
16000,
|
|
|
|
22050,
|
|
|
|
32000,
|
|
|
|
44100,
|
|
|
|
48000,
|
|
|
|
64000,
|
|
|
|
88200,
|
|
|
|
96000,
|
|
|
|
176400,
|
|
|
|
192000,
|
|
|
|
];
|
|
|
|
|
2016-01-28 20:27:09 +00:00
|
|
|
let mut rates = Vec::new();
|
2015-09-10 17:48:39 +00:00
|
|
|
for &rate in RATES.iter() {
|
2017-10-11 11:24:49 +00:00
|
|
|
if alsa::snd_pcm_hw_params_test_rate(playback_handle,
|
|
|
|
hw_params.0,
|
|
|
|
rate,
|
|
|
|
0) == 0
|
|
|
|
{
|
2017-10-20 19:18:40 +00:00
|
|
|
rates.push((rate, rate));
|
2015-09-10 17:48:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-20 19:18:40 +00:00
|
|
|
if rates.len() == 0 {
|
|
|
|
vec![(min_rate, max_rate)]
|
|
|
|
} else {
|
|
|
|
rates
|
|
|
|
}
|
2015-09-10 17:48:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let mut min_channels = mem::uninitialized();
|
2017-10-11 11:24:49 +00:00
|
|
|
check_errors(alsa::snd_pcm_hw_params_get_channels_min(hw_params.0, &mut min_channels))
|
|
|
|
.expect("unable to get minimum supported channel count");
|
2015-09-10 17:48:39 +00:00
|
|
|
let mut max_channels = mem::uninitialized();
|
2017-10-11 11:24:49 +00:00
|
|
|
check_errors(alsa::snd_pcm_hw_params_get_channels_max(hw_params.0, &mut max_channels))
|
|
|
|
.expect("unable to get maximum supported channel count");
|
|
|
|
let max_channels = cmp::min(max_channels, 32); // TODO: limiting to 32 channels or too much stuff is returned
|
|
|
|
let supported_channels = (min_channels .. max_channels + 1)
|
|
|
|
.filter_map(|num| if alsa::snd_pcm_hw_params_test_channels(
|
|
|
|
playback_handle,
|
|
|
|
hw_params.0,
|
|
|
|
num,
|
|
|
|
) == 0
|
|
|
|
{
|
|
|
|
Some(
|
|
|
|
[
|
|
|
|
ChannelPosition::FrontLeft,
|
|
|
|
ChannelPosition::FrontRight,
|
|
|
|
ChannelPosition::BackLeft,
|
|
|
|
ChannelPosition::BackRight,
|
|
|
|
ChannelPosition::FrontCenter,
|
|
|
|
ChannelPosition::LowFrequency,
|
|
|
|
].iter()
|
|
|
|
.take(num as usize)
|
|
|
|
.cloned()
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
)
|
2015-09-10 17:48:39 +00:00
|
|
|
} else {
|
|
|
|
None
|
2017-10-11 11:24:49 +00:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
2015-09-10 17:48:39 +00:00
|
|
|
|
|
|
|
let mut output = Vec::with_capacity(supported_formats.len() * supported_channels.len() *
|
2017-10-11 11:24:49 +00:00
|
|
|
samples_rates.len());
|
2015-09-10 17:48:39 +00:00
|
|
|
for &data_type in supported_formats.iter() {
|
|
|
|
for channels in supported_channels.iter() {
|
2017-10-20 19:18:40 +00:00
|
|
|
for &(min_rate, max_rate) in samples_rates.iter() {
|
|
|
|
output.push(SupportedFormat {
|
2017-10-11 11:24:49 +00:00
|
|
|
channels: channels.clone(),
|
2017-10-20 19:18:40 +00:00
|
|
|
min_samples_rate: SamplesRate(min_rate as u32),
|
|
|
|
max_samples_rate: SamplesRate(max_rate as u32),
|
2017-10-11 11:24:49 +00:00
|
|
|
data_type: data_type,
|
|
|
|
});
|
2015-09-10 17:48:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: RAII
|
|
|
|
alsa::snd_pcm_close(playback_handle);
|
|
|
|
Ok(output.into_iter())
|
|
|
|
}
|
2015-09-01 15:15:49 +00:00
|
|
|
}
|
2015-09-22 12:46:27 +00:00
|
|
|
|
|
|
|
#[inline]
|
2017-10-12 09:54:09 +00:00
|
|
|
pub fn name(&self) -> String {
|
2015-09-22 13:46:56 +00:00
|
|
|
self.0.clone()
|
2015-09-22 12:46:27 +00:00
|
|
|
}
|
2015-09-01 15:15:49 +00:00
|
|
|
}
|
|
|
|
|
2016-08-02 20:28:37 +00:00
|
|
|
pub struct EventLoop {
|
2017-10-18 18:24:05 +00:00
|
|
|
// Each newly-created voice gets a new ID from this counter. The counter is then incremented.
|
2017-10-23 14:41:38 +00:00
|
|
|
next_voice_id: AtomicUsize, // TODO: use AtomicU64 when stable?
|
2016-08-02 20:28:37 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// A trigger that uses a `pipe()` as backend. Signalled whenever a new command is ready, so
|
|
|
|
// that `poll()` can wake up and pick the changes.
|
2017-07-13 11:58:01 +00:00
|
|
|
pending_trigger: Trigger,
|
2016-08-02 20:28:37 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// This field is locked by the `run()` method.
|
|
|
|
// The mutex also ensures that only one thread at a time has `run()` running.
|
|
|
|
run_context: Mutex<RunContext>,
|
2016-08-02 20:28:37 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// Commands processed by the `run()` method that is currently running.
|
|
|
|
// TODO: use a lock-free container
|
|
|
|
commands: Mutex<Vec<Command>>,
|
2017-10-11 11:24:49 +00:00
|
|
|
}
|
2016-08-02 20:28:37 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
unsafe impl Send for EventLoop {
|
2016-08-02 20:28:37 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
unsafe impl Sync for EventLoop {
|
2016-08-02 20:28:37 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
enum Command {
|
|
|
|
NewVoice(VoiceInner),
|
2017-11-02 09:30:15 +00:00
|
|
|
PlayVoice(VoiceId),
|
|
|
|
PauseVoice(VoiceId),
|
2017-10-18 18:24:05 +00:00
|
|
|
DestroyVoice(VoiceId),
|
2016-10-02 11:18:27 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
struct RunContext {
|
|
|
|
// Descriptors to wait for. Always contains `pending_trigger.read_fd()` as first element.
|
|
|
|
descriptors: Vec<libc::pollfd>,
|
|
|
|
// List of voices that are written in `descriptors`.
|
|
|
|
voices: Vec<VoiceInner>,
|
2016-10-02 11:18:27 +00:00
|
|
|
}
|
|
|
|
|
2016-08-02 20:28:37 +00:00
|
|
|
struct VoiceInner {
|
2017-10-18 18:24:05 +00:00
|
|
|
// The id of the voice.
|
|
|
|
id: VoiceId,
|
2016-08-02 20:28:37 +00:00
|
|
|
|
|
|
|
// The ALSA channel.
|
2017-10-18 18:24:05 +00:00
|
|
|
channel: *mut alsa::snd_pcm_t,
|
2016-08-02 20:28:37 +00:00
|
|
|
|
|
|
|
// When converting between file descriptors and `snd_pcm_t`, this is the number of
|
|
|
|
// file descriptors that this `snd_pcm_t` uses.
|
|
|
|
num_descriptors: usize,
|
|
|
|
|
|
|
|
// Format of the samples.
|
|
|
|
sample_format: SampleFormat,
|
|
|
|
|
|
|
|
// Number of channels, ie. number of samples per frame.
|
2014-12-16 15:45:45 +00:00
|
|
|
num_channels: u16,
|
2016-08-02 20:28:37 +00:00
|
|
|
|
|
|
|
// Number of samples that can fit in the buffer.
|
2016-09-30 16:18:28 +00:00
|
|
|
buffer_len: usize,
|
2016-08-02 20:28:37 +00:00
|
|
|
|
|
|
|
// Minimum number of samples to put in the buffer.
|
|
|
|
period_len: usize,
|
|
|
|
|
2017-11-02 09:30:15 +00:00
|
|
|
// Whether or not the hardware supports pausing the stream.
|
|
|
|
can_pause: bool,
|
|
|
|
|
|
|
|
// Whether or not the sample stream is currently paused.
|
|
|
|
is_paused: bool,
|
2016-10-02 11:18:27 +00:00
|
|
|
|
|
|
|
// A file descriptor opened with `eventfd`.
|
|
|
|
// It is used to wait for resume signal.
|
2017-07-13 11:58:01 +00:00
|
|
|
resume_trigger: Trigger,
|
2014-12-16 15:45:45 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct VoiceId(usize);
|
2016-08-02 20:28:37 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
impl EventLoop {
|
2016-09-30 16:18:28 +00:00
|
|
|
#[inline]
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn new() -> EventLoop {
|
|
|
|
let pending_trigger = Trigger::new();
|
2016-09-30 16:18:28 +00:00
|
|
|
|
2017-11-01 09:15:17 +00:00
|
|
|
let initial_descriptors = vec![
|
|
|
|
libc::pollfd {
|
|
|
|
fd: pending_trigger.read_fd(),
|
|
|
|
events: libc::POLLIN,
|
|
|
|
revents: 0,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
let run_context = Mutex::new(RunContext {
|
2017-11-01 09:15:17 +00:00
|
|
|
descriptors: initial_descriptors,
|
2017-10-23 14:41:38 +00:00
|
|
|
voices: Vec::new(),
|
|
|
|
});
|
2016-09-30 16:18:28 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
EventLoop {
|
|
|
|
next_voice_id: AtomicUsize::new(0),
|
|
|
|
pending_trigger: pending_trigger,
|
|
|
|
run_context,
|
|
|
|
commands: Mutex::new(Vec::new()),
|
2016-09-30 16:18:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn run<F>(&self, mut callback: F) -> !
|
|
|
|
where F: FnMut(VoiceId, UnknownTypeBuffer)
|
|
|
|
{
|
|
|
|
self.run_inner(&mut callback)
|
|
|
|
}
|
2016-08-02 20:28:37 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
fn run_inner(&self, callback: &mut FnMut(VoiceId, UnknownTypeBuffer)) -> ! {
|
|
|
|
unsafe {
|
|
|
|
let mut run_context = self.run_context.lock().unwrap();
|
|
|
|
let run_context = &mut *run_context;
|
2016-10-02 11:18:27 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
loop {
|
|
|
|
{
|
|
|
|
let mut commands_lock = self.commands.lock().unwrap();
|
|
|
|
if !commands_lock.is_empty() {
|
|
|
|
for command in commands_lock.drain(..) {
|
|
|
|
match command {
|
|
|
|
Command::DestroyVoice(voice_id) => {
|
|
|
|
run_context.voices.retain(|v| v.id != voice_id);
|
|
|
|
},
|
2017-11-02 09:30:15 +00:00
|
|
|
Command::PlayVoice(voice_id) => {
|
|
|
|
if let Some(voice) = run_context.voices.iter_mut()
|
|
|
|
.find(|voice| voice.can_pause && voice.id == voice_id)
|
|
|
|
{
|
|
|
|
alsa::snd_pcm_pause(voice.channel, 0);
|
|
|
|
voice.is_paused = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Command::PauseVoice(voice_id) => {
|
|
|
|
if let Some(voice) = run_context.voices.iter_mut()
|
|
|
|
.find(|voice| voice.can_pause && voice.id == voice_id)
|
|
|
|
{
|
|
|
|
alsa::snd_pcm_pause(voice.channel, 1);
|
|
|
|
voice.is_paused = true;
|
|
|
|
}
|
|
|
|
},
|
2017-10-18 18:24:05 +00:00
|
|
|
Command::NewVoice(voice_inner) => {
|
|
|
|
run_context.voices.push(voice_inner);
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2016-08-02 20:28:37 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
run_context.descriptors = vec![
|
|
|
|
libc::pollfd {
|
|
|
|
fd: self.pending_trigger.read_fd(),
|
|
|
|
events: libc::POLLIN,
|
|
|
|
revents: 0,
|
2017-10-23 14:41:38 +00:00
|
|
|
},
|
2017-10-18 18:24:05 +00:00
|
|
|
];
|
|
|
|
for voice in run_context.voices.iter() {
|
|
|
|
run_context.descriptors.reserve(voice.num_descriptors);
|
|
|
|
let len = run_context.descriptors.len();
|
|
|
|
let filled = alsa::snd_pcm_poll_descriptors(voice.channel,
|
2017-10-23 14:41:38 +00:00
|
|
|
run_context
|
|
|
|
.descriptors
|
2017-10-18 18:24:05 +00:00
|
|
|
.as_mut_ptr()
|
|
|
|
.offset(len as isize),
|
|
|
|
voice.num_descriptors as
|
|
|
|
libc::c_uint);
|
|
|
|
debug_assert_eq!(filled, voice.num_descriptors as libc::c_int);
|
|
|
|
run_context.descriptors.set_len(len + voice.num_descriptors);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-02 20:28:37 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
let ret = libc::poll(run_context.descriptors.as_mut_ptr(),
|
|
|
|
run_context.descriptors.len() as libc::nfds_t,
|
|
|
|
-1 /* infinite */);
|
|
|
|
assert!(ret >= 0, "poll() failed");
|
2014-12-16 15:45:45 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
if ret == 0 {
|
|
|
|
continue;
|
|
|
|
}
|
2015-09-10 17:48:39 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// If the `pending_trigger` was signaled, we need to process the comands.
|
|
|
|
if run_context.descriptors[0].revents != 0 {
|
|
|
|
run_context.descriptors[0].revents = 0;
|
|
|
|
self.pending_trigger.clear_pipe();
|
|
|
|
}
|
2015-09-10 17:48:39 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// Iterate over each individual voice/descriptor.
|
|
|
|
let mut i_voice = 0;
|
|
|
|
let mut i_descriptor = 1;
|
|
|
|
while (i_descriptor as usize) < run_context.descriptors.len() {
|
|
|
|
let voice_inner = run_context.voices.get_mut(i_voice).unwrap();
|
|
|
|
|
|
|
|
// Check whether the event is `POLLOUT`. If not, `continue`.
|
|
|
|
{
|
|
|
|
let mut revent = mem::uninitialized();
|
|
|
|
|
|
|
|
{
|
|
|
|
let num_descriptors = voice_inner.num_descriptors as libc::c_uint;
|
2017-10-23 14:41:38 +00:00
|
|
|
let desc_ptr =
|
|
|
|
run_context.descriptors.as_mut_ptr().offset(i_descriptor);
|
|
|
|
let res = alsa::snd_pcm_poll_descriptors_revents(voice_inner.channel,
|
|
|
|
desc_ptr,
|
|
|
|
num_descriptors,
|
|
|
|
&mut revent);
|
|
|
|
check_errors(res).unwrap();
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (revent as libc::c_short & libc::POLLOUT) == 0 {
|
|
|
|
i_descriptor += voice_inner.num_descriptors as isize;
|
|
|
|
i_voice += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine the number of samples that are available to write.
|
|
|
|
let available = {
|
|
|
|
let available = alsa::snd_pcm_avail(voice_inner.channel); // TODO: what about snd_pcm_avail_update?
|
|
|
|
|
|
|
|
if available == -32 {
|
|
|
|
// buffer underrun
|
|
|
|
voice_inner.buffer_len
|
|
|
|
} else if available < 0 {
|
2017-10-23 14:41:38 +00:00
|
|
|
check_errors(available as libc::c_int)
|
|
|
|
.expect("buffer is not available");
|
2017-10-18 18:24:05 +00:00
|
|
|
unreachable!()
|
|
|
|
} else {
|
2017-10-23 14:41:38 +00:00
|
|
|
(available * voice_inner.num_channels as alsa::snd_pcm_sframes_t) as
|
|
|
|
usize
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if available < voice_inner.period_len {
|
|
|
|
i_descriptor += voice_inner.num_descriptors as isize;
|
|
|
|
i_voice += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let voice_id = voice_inner.id.clone();
|
|
|
|
|
|
|
|
// We're now sure that we're ready to write data.
|
|
|
|
let buffer = match voice_inner.sample_format {
|
|
|
|
SampleFormat::I16 => {
|
|
|
|
let buffer = Buffer {
|
|
|
|
voice_inner: voice_inner,
|
|
|
|
buffer: iter::repeat(mem::uninitialized())
|
|
|
|
.take(available)
|
|
|
|
.collect(),
|
|
|
|
};
|
|
|
|
|
|
|
|
UnknownTypeBuffer::I16(::Buffer { target: Some(buffer) })
|
|
|
|
},
|
|
|
|
SampleFormat::U16 => {
|
|
|
|
let buffer = Buffer {
|
|
|
|
voice_inner: voice_inner,
|
|
|
|
buffer: iter::repeat(mem::uninitialized())
|
|
|
|
.take(available)
|
|
|
|
.collect(),
|
|
|
|
};
|
|
|
|
|
|
|
|
UnknownTypeBuffer::U16(::Buffer { target: Some(buffer) })
|
|
|
|
},
|
|
|
|
SampleFormat::F32 => {
|
|
|
|
let buffer = Buffer {
|
|
|
|
voice_inner: voice_inner,
|
2017-10-23 14:41:38 +00:00
|
|
|
// Note that we don't use `mem::uninitialized` because of sNaN.
|
|
|
|
buffer: iter::repeat(0.0).take(available).collect(),
|
2017-10-18 18:24:05 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
UnknownTypeBuffer::F32(::Buffer { target: Some(buffer) })
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
callback(voice_id, buffer);
|
|
|
|
}
|
|
|
|
}
|
2015-09-10 17:48:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn build_voice(&self, endpoint: &Endpoint, format: &Format)
|
|
|
|
-> Result<VoiceId, CreationError> {
|
2014-12-16 15:45:45 +00:00
|
|
|
unsafe {
|
2016-01-15 20:16:34 +00:00
|
|
|
let name = ffi::CString::new(endpoint.0.clone()).expect("unable to clone endpoint");
|
2014-12-16 15:45:45 +00:00
|
|
|
|
|
|
|
let mut playback_handle = mem::uninitialized();
|
2015-09-22 13:46:56 +00:00
|
|
|
match alsa::snd_pcm_open(&mut playback_handle, name.as_ptr(),
|
2016-08-02 20:28:37 +00:00
|
|
|
alsa::SND_PCM_STREAM_PLAYBACK, 0)
|
2016-01-28 20:27:09 +00:00
|
|
|
{
|
2015-09-22 13:46:56 +00:00
|
|
|
-16 /* determined empirically */ => return Err(CreationError::DeviceNotAvailable),
|
2016-01-15 20:16:34 +00:00
|
|
|
e => check_errors(e).expect("Device unavailable")
|
2015-09-22 13:46:56 +00:00
|
|
|
}
|
2015-09-10 17:48:39 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
let data_type = if cfg!(target_endian = "big") {
|
|
|
|
match format.data_type {
|
|
|
|
SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_BE,
|
|
|
|
SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_BE,
|
|
|
|
SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_BE,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
match format.data_type {
|
|
|
|
SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_LE,
|
|
|
|
SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_LE,
|
|
|
|
SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_LE,
|
|
|
|
}
|
2015-09-22 13:20:11 +00:00
|
|
|
};
|
|
|
|
|
2015-09-10 17:48:39 +00:00
|
|
|
let hw_params = HwParams::alloc();
|
2017-10-11 11:24:49 +00:00
|
|
|
check_errors(alsa::snd_pcm_hw_params_any(playback_handle, hw_params.0))
|
|
|
|
.expect("Errors on playback handle");
|
|
|
|
check_errors(alsa::snd_pcm_hw_params_set_access(playback_handle,
|
|
|
|
hw_params.0,
|
|
|
|
alsa::SND_PCM_ACCESS_RW_INTERLEAVED))
|
|
|
|
.expect("handle not acessible");
|
|
|
|
check_errors(alsa::snd_pcm_hw_params_set_format(playback_handle,
|
|
|
|
hw_params.0,
|
|
|
|
data_type))
|
|
|
|
.expect("format could not be set");
|
|
|
|
check_errors(alsa::snd_pcm_hw_params_set_rate(playback_handle,
|
|
|
|
hw_params.0,
|
|
|
|
format.samples_rate.0 as libc::c_uint,
|
|
|
|
0))
|
|
|
|
.expect("sample rate could not be set");
|
|
|
|
check_errors(alsa::snd_pcm_hw_params_set_channels(playback_handle,
|
|
|
|
hw_params.0,
|
|
|
|
format.channels.len() as
|
|
|
|
libc::c_uint))
|
|
|
|
.expect("channel count could not be set");
|
|
|
|
let mut max_buffer_size = format.samples_rate.0 as alsa::snd_pcm_uframes_t /
|
|
|
|
format.channels.len() as alsa::snd_pcm_uframes_t /
|
|
|
|
5; // 200ms of buffer
|
|
|
|
check_errors(alsa::snd_pcm_hw_params_set_buffer_size_max(playback_handle,
|
|
|
|
hw_params.0,
|
|
|
|
&mut max_buffer_size))
|
|
|
|
.unwrap();
|
|
|
|
check_errors(alsa::snd_pcm_hw_params(playback_handle, hw_params.0))
|
|
|
|
.expect("hardware params could not be set");
|
|
|
|
|
2017-11-02 09:30:15 +00:00
|
|
|
let can_pause = alsa::snd_pcm_hw_params_can_pause(hw_params.0) == 1;
|
|
|
|
|
2017-10-11 11:24:49 +00:00
|
|
|
let mut sw_params = mem::uninitialized(); // TODO: RAII
|
2016-08-02 20:28:37 +00:00
|
|
|
check_errors(alsa::snd_pcm_sw_params_malloc(&mut sw_params)).unwrap();
|
|
|
|
check_errors(alsa::snd_pcm_sw_params_current(playback_handle, sw_params)).unwrap();
|
2017-10-11 11:24:49 +00:00
|
|
|
check_errors(alsa::snd_pcm_sw_params_set_start_threshold(playback_handle,
|
|
|
|
sw_params,
|
|
|
|
0))
|
|
|
|
.unwrap();
|
2014-12-16 15:45:45 +00:00
|
|
|
|
2015-09-27 11:00:17 +00:00
|
|
|
let (buffer_len, period_len) = {
|
|
|
|
let mut buffer = mem::uninitialized();
|
|
|
|
let mut period = mem::uninitialized();
|
2017-10-11 11:24:49 +00:00
|
|
|
check_errors(alsa::snd_pcm_get_params(playback_handle, &mut buffer, &mut period))
|
|
|
|
.expect("could not initialize buffer");
|
2015-09-27 11:00:17 +00:00
|
|
|
assert!(buffer != 0);
|
2017-10-11 11:24:49 +00:00
|
|
|
check_errors(alsa::snd_pcm_sw_params_set_avail_min(playback_handle,
|
|
|
|
sw_params,
|
|
|
|
period))
|
|
|
|
.unwrap();
|
2015-09-27 11:00:17 +00:00
|
|
|
let buffer = buffer as usize * format.channels.len();
|
|
|
|
let period = period as usize * format.channels.len();
|
|
|
|
(buffer, period)
|
2015-09-22 15:52:35 +00:00
|
|
|
};
|
|
|
|
|
2017-01-24 00:10:44 +00:00
|
|
|
check_errors(alsa::snd_pcm_sw_params(playback_handle, sw_params)).unwrap();
|
2017-10-11 11:24:49 +00:00
|
|
|
check_errors(alsa::snd_pcm_prepare(playback_handle))
|
|
|
|
.expect("could not get playback handle");
|
2017-01-24 00:10:44 +00:00
|
|
|
|
2016-08-02 20:28:37 +00:00
|
|
|
let num_descriptors = {
|
|
|
|
let num_descriptors = alsa::snd_pcm_poll_descriptors_count(playback_handle);
|
|
|
|
debug_assert!(num_descriptors >= 1);
|
|
|
|
num_descriptors as usize
|
|
|
|
};
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
let new_voice_id = VoiceId(self.next_voice_id.fetch_add(1, Ordering::Relaxed));
|
2017-10-23 14:41:38 +00:00
|
|
|
assert_ne!(new_voice_id.0, usize::max_value()); // check for overflows
|
2017-10-18 18:24:05 +00:00
|
|
|
|
|
|
|
let voice_inner = VoiceInner {
|
|
|
|
id: new_voice_id.clone(),
|
|
|
|
channel: playback_handle,
|
|
|
|
sample_format: format.data_type,
|
|
|
|
num_descriptors: num_descriptors,
|
|
|
|
num_channels: format.channels.len() as u16,
|
|
|
|
buffer_len: buffer_len,
|
|
|
|
period_len: period_len,
|
2017-11-02 09:30:15 +00:00
|
|
|
can_pause: can_pause,
|
|
|
|
is_paused: false,
|
2017-10-18 18:24:05 +00:00
|
|
|
resume_trigger: Trigger::new(),
|
|
|
|
};
|
|
|
|
|
2017-11-02 09:30:15 +00:00
|
|
|
self.push_command(Command::NewVoice(voice_inner));
|
2017-10-18 18:24:05 +00:00
|
|
|
Ok(new_voice_id)
|
2014-12-16 15:45:45 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-22 13:16:47 +00:00
|
|
|
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2017-11-02 09:30:15 +00:00
|
|
|
fn push_command(&self, command: Command) {
|
|
|
|
self.commands.lock().unwrap().push(command);
|
2017-10-18 18:24:05 +00:00
|
|
|
self.pending_trigger.wakeup();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2017-11-02 09:30:15 +00:00
|
|
|
pub fn destroy_voice(&self, voice_id: VoiceId) {
|
|
|
|
self.push_command(Command::DestroyVoice(voice_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn play(&self, voice_id: VoiceId) {
|
|
|
|
self.push_command(Command::PlayVoice(voice_id));
|
2014-12-22 13:16:47 +00:00
|
|
|
}
|
|
|
|
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2017-11-02 09:30:15 +00:00
|
|
|
pub fn pause(&self, voice_id: VoiceId) {
|
|
|
|
self.push_command(Command::PauseVoice(voice_id));
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Buffer<'a, T: 'a> {
|
|
|
|
voice_inner: &'a mut VoiceInner,
|
|
|
|
buffer: Vec<T>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Wrapper around `hw_params`.
|
|
|
|
struct HwParams(*mut alsa::snd_pcm_hw_params_t);
|
|
|
|
|
|
|
|
impl HwParams {
|
|
|
|
pub fn alloc() -> HwParams {
|
|
|
|
unsafe {
|
|
|
|
let mut hw_params = mem::uninitialized();
|
|
|
|
check_errors(alsa::snd_pcm_hw_params_malloc(&mut hw_params))
|
|
|
|
.expect("unable to get hardware parameters");
|
|
|
|
HwParams(hw_params)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for HwParams {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
alsa::snd_pcm_hw_params_free(self.0);
|
|
|
|
}
|
2014-12-22 13:16:47 +00:00
|
|
|
}
|
2014-12-16 15:45:45 +00:00
|
|
|
}
|
|
|
|
|
2016-08-02 20:28:37 +00:00
|
|
|
impl Drop for VoiceInner {
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2014-12-16 15:45:45 +00:00
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
2017-10-18 18:24:05 +00:00
|
|
|
alsa::snd_pcm_close(self.channel);
|
2014-12-16 15:45:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
impl<'a, T> Buffer<'a, T> {
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2017-10-12 09:54:09 +00:00
|
|
|
pub fn buffer(&mut self) -> &mut [T] {
|
2015-03-30 09:06:46 +00:00
|
|
|
&mut self.buffer
|
2014-12-16 15:45:45 +00:00
|
|
|
}
|
|
|
|
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2015-09-10 19:21:46 +00:00
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.buffer.len()
|
|
|
|
}
|
|
|
|
|
2014-12-16 15:45:45 +00:00
|
|
|
pub fn finish(self) {
|
2017-10-18 18:24:05 +00:00
|
|
|
let to_write = (self.buffer.len() / self.voice_inner.num_channels as usize) as
|
2017-10-11 11:24:49 +00:00
|
|
|
alsa::snd_pcm_uframes_t;
|
2014-12-16 15:45:45 +00:00
|
|
|
|
|
|
|
unsafe {
|
2015-09-22 18:20:17 +00:00
|
|
|
loop {
|
2017-10-23 14:41:38 +00:00
|
|
|
let result = alsa::snd_pcm_writei(self.voice_inner.channel,
|
|
|
|
self.buffer.as_ptr() as *const _,
|
|
|
|
to_write);
|
2015-09-22 18:20:17 +00:00
|
|
|
|
|
|
|
if result == -32 {
|
|
|
|
// buffer underrun
|
2017-10-18 18:24:05 +00:00
|
|
|
alsa::snd_pcm_prepare(self.voice_inner.channel);
|
2015-09-22 18:20:17 +00:00
|
|
|
} else if result < 0 {
|
2016-01-15 20:16:34 +00:00
|
|
|
check_errors(result as libc::c_int).expect("could not write pcm");
|
2015-09-22 18:20:17 +00:00
|
|
|
} else {
|
2015-09-22 19:01:26 +00:00
|
|
|
assert_eq!(result as alsa::snd_pcm_uframes_t, to_write);
|
2015-09-22 18:20:17 +00:00
|
|
|
break;
|
|
|
|
}
|
2014-12-16 15:45:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2014-12-16 15:45:45 +00:00
|
|
|
fn check_errors(err: libc::c_int) -> Result<(), String> {
|
2015-01-08 19:34:27 +00:00
|
|
|
use std::ffi;
|
2014-12-16 15:45:45 +00:00
|
|
|
|
|
|
|
if err < 0 {
|
|
|
|
unsafe {
|
2017-10-11 11:24:49 +00:00
|
|
|
let s = ffi::CStr::from_ptr(alsa::snd_strerror(err))
|
|
|
|
.to_bytes()
|
|
|
|
.to_vec();
|
2016-01-15 20:16:34 +00:00
|
|
|
let s = String::from_utf8(s).expect("Streaming error occured");
|
2015-04-04 07:06:46 +00:00
|
|
|
return Err(s);
|
2014-12-16 15:45:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|