The old method always returned _RUNNING on some machines. This new method seems to produce the expected behaviour. Note: -32 is probably -EPIPE, but the appropriate constant was not available at this time.
387 lines
14 KiB
Rust
387 lines
14 KiB
Rust
extern crate alsa_sys as alsa;
|
|
extern crate libc;
|
|
|
|
pub use self::enumerate::{EndpointsIterator, get_default_endpoint};
|
|
|
|
use ChannelPosition;
|
|
use CreationError;
|
|
use Format;
|
|
use FormatsEnumerationError;
|
|
use SampleFormat;
|
|
use SamplesRate;
|
|
|
|
use std::{ffi, cmp, iter, mem, ptr};
|
|
use std::vec::IntoIter as VecIntoIter;
|
|
use std::sync::Mutex;
|
|
|
|
pub type SupportedFormatsIterator = VecIntoIter<Format>;
|
|
|
|
mod enumerate;
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct Endpoint(String);
|
|
|
|
impl Endpoint {
|
|
pub fn get_supported_formats_list(&self)
|
|
-> Result<SupportedFormatsIterator, FormatsEnumerationError>
|
|
{
|
|
unsafe {
|
|
let mut playback_handle = mem::uninitialized();
|
|
let device_name = ffi::CString::new(self.0.clone()).expect("Unable to get device name");
|
|
|
|
match alsa::snd_pcm_open(&mut playback_handle, device_name.as_ptr() as *const _,
|
|
alsa::SND_PCM_STREAM_PLAYBACK, alsa::SND_PCM_NONBLOCK)
|
|
{
|
|
-2 |
|
|
-16 /* determined empirically */ => return Err(FormatsEnumerationError::DeviceNotAvailable),
|
|
e => check_errors(e).expect("device not available")
|
|
}
|
|
|
|
let hw_params = HwParams::alloc();
|
|
match check_errors(alsa::snd_pcm_hw_params_any(playback_handle, hw_params.0)) {
|
|
Err(_) => return Ok(Vec::new().into_iter()),
|
|
Ok(_) => ()
|
|
};
|
|
|
|
// TODO: check endianess
|
|
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,
|
|
/*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,*/
|
|
(SampleFormat::F32, alsa::SND_PCM_FORMAT_FLOAT_LE),
|
|
/*SND_PCM_FORMAT_FLOAT_BE,
|
|
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,
|
|
SND_PCM_FORMAT_U18_3BE,*/
|
|
];
|
|
|
|
let mut supported_formats = Vec::new();
|
|
for &(sample_format, alsa_format) in FORMATS.iter() {
|
|
if alsa::snd_pcm_hw_params_test_format(playback_handle, hw_params.0, alsa_format) == 0 {
|
|
supported_formats.push(sample_format);
|
|
}
|
|
}
|
|
|
|
let mut min_rate = mem::uninitialized();
|
|
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");
|
|
let mut max_rate = mem::uninitialized();
|
|
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");
|
|
|
|
let samples_rates = if min_rate == max_rate {
|
|
vec![min_rate]
|
|
/*} else if alsa::snd_pcm_hw_params_test_rate(playback_handle, hw_params.0, min_rate + 1, 0) == 0 {
|
|
(min_rate .. max_rate + 1).collect()*/ // TODO: code is correct but returns lots of stuff
|
|
} else {
|
|
const RATES: [libc::c_uint; 13] = [
|
|
5512,
|
|
8000,
|
|
11025,
|
|
16000,
|
|
22050,
|
|
32000,
|
|
44100,
|
|
48000,
|
|
64000,
|
|
88200,
|
|
96000,
|
|
176400,
|
|
192000,
|
|
];
|
|
|
|
let mut rates = Vec::new();
|
|
for &rate in RATES.iter() {
|
|
if alsa::snd_pcm_hw_params_test_rate(playback_handle, hw_params.0, rate, 0) == 0 {
|
|
rates.push(rate);
|
|
}
|
|
}
|
|
|
|
/*if rates.len() == 0 {
|
|
(min_rate .. max_rate + 1).collect()
|
|
} else {*/
|
|
rates // TODO: code is correct but returns lots of stuff
|
|
//}
|
|
};
|
|
|
|
let mut min_channels = mem::uninitialized();
|
|
check_errors(alsa::snd_pcm_hw_params_get_channels_min(hw_params.0, &mut min_channels)).expect("unable to get minimum supported channel count");
|
|
let mut max_channels = mem::uninitialized();
|
|
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<_>>())
|
|
} else {
|
|
None
|
|
}
|
|
}).collect::<Vec<_>>();
|
|
|
|
let mut output = Vec::with_capacity(supported_formats.len() * supported_channels.len() *
|
|
samples_rates.len());
|
|
for &data_type in supported_formats.iter() {
|
|
for channels in supported_channels.iter() {
|
|
for &rate in samples_rates.iter() {
|
|
output.push(Format {
|
|
channels: channels.clone(),
|
|
samples_rate: SamplesRate(rate as u32),
|
|
data_type: data_type,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: RAII
|
|
alsa::snd_pcm_close(playback_handle);
|
|
Ok(output.into_iter())
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn get_name(&self) -> String {
|
|
self.0.clone()
|
|
}
|
|
}
|
|
|
|
pub struct Voice {
|
|
channel: Mutex<*mut alsa::snd_pcm_t>,
|
|
num_channels: u16,
|
|
buffer_len: usize, // number of samples that can fit in the buffer
|
|
period_len: usize, // minimum number of samples to put in the buffer
|
|
}
|
|
|
|
pub struct Buffer<'a, T> {
|
|
channel: &'a mut Voice,
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Voice {
|
|
pub fn new(endpoint: &Endpoint, format: &Format) -> Result<Voice, CreationError> {
|
|
unsafe {
|
|
let name = ffi::CString::new(endpoint.0.clone()).expect("unable to clone endpoint");
|
|
|
|
let mut playback_handle = mem::uninitialized();
|
|
match alsa::snd_pcm_open(&mut playback_handle, name.as_ptr(),
|
|
alsa::SND_PCM_STREAM_PLAYBACK, alsa::SND_PCM_NONBLOCK)
|
|
{
|
|
-16 /* determined empirically */ => return Err(CreationError::DeviceNotAvailable),
|
|
e => check_errors(e).expect("Device unavailable")
|
|
}
|
|
|
|
// TODO: check endianess
|
|
let data_type = 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,
|
|
};
|
|
|
|
let hw_params = HwParams::alloc();
|
|
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");
|
|
check_errors(alsa::snd_pcm_hw_params(playback_handle, hw_params.0)).expect("hardware params could not be set");
|
|
|
|
check_errors(alsa::snd_pcm_prepare(playback_handle)).expect("could not get playback handle");
|
|
|
|
let (buffer_len, period_len) = {
|
|
let mut buffer = mem::uninitialized();
|
|
let mut period = mem::uninitialized();
|
|
check_errors(alsa::snd_pcm_get_params(playback_handle, &mut buffer, &mut period)).expect("could not initialize buffer");
|
|
assert!(buffer != 0);
|
|
let buffer = buffer as usize * format.channels.len();
|
|
let period = period as usize * format.channels.len();
|
|
(buffer, period)
|
|
};
|
|
|
|
Ok(Voice {
|
|
channel: Mutex::new(playback_handle),
|
|
num_channels: format.channels.len() as u16,
|
|
buffer_len: buffer_len,
|
|
period_len: period_len,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub fn append_data<'a, T>(&'a mut self, max_elements: usize) -> Buffer<'a, T> where T: Clone {
|
|
let available = {
|
|
let channel = self.channel.lock().expect("could not lock channel");
|
|
let available = unsafe { alsa::snd_pcm_avail(*channel) };
|
|
|
|
if available == -32 {
|
|
// buffer underrun
|
|
self.buffer_len
|
|
} else if available < 0 {
|
|
check_errors(available as libc::c_int).expect("buffer is not available");
|
|
unreachable!()
|
|
} else {
|
|
(available * self.num_channels as alsa::snd_pcm_sframes_t) as usize
|
|
}
|
|
};
|
|
|
|
let elements = cmp::min(available, max_elements);
|
|
|
|
Buffer {
|
|
channel: self,
|
|
buffer: iter::repeat(unsafe { mem::uninitialized() }).take(elements).collect(),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn play(&mut self) {
|
|
// already playing
|
|
//unimplemented!()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn pause(&mut self) {
|
|
unimplemented!()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn get_period(&self) -> usize {
|
|
self.period_len
|
|
}
|
|
|
|
pub fn get_pending_samples(&self) -> usize {
|
|
let available = {
|
|
let channel = self.channel.lock().expect("could not lock channel");
|
|
let available = unsafe { alsa::snd_pcm_avail(*channel) };
|
|
|
|
if available == -32 {
|
|
self.buffer_len as alsa::snd_pcm_sframes_t // buffer underrun
|
|
} else if available < 0 {
|
|
check_errors(available as libc::c_int).expect("could not write to buffer");
|
|
unreachable!()
|
|
} else {
|
|
available * self.num_channels as alsa::snd_pcm_sframes_t
|
|
}
|
|
};
|
|
|
|
self.buffer_len - available as usize
|
|
}
|
|
|
|
pub fn underflowed(&self) -> bool {
|
|
let channel = self.channel.lock().expect("channel underflow");
|
|
|
|
let available = unsafe { alsa::snd_pcm_avail(*channel) };
|
|
available == -32
|
|
}
|
|
}
|
|
|
|
unsafe impl Send for Voice {}
|
|
unsafe impl Sync for Voice {}
|
|
|
|
impl Drop for Voice {
|
|
#[inline]
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
alsa::snd_pcm_close(*self.channel.lock().expect("drop for voice"));
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T> Buffer<'a, T> {
|
|
#[inline]
|
|
pub fn get_buffer<'b>(&'b mut self) -> &'b mut [T] {
|
|
&mut self.buffer
|
|
}
|
|
|
|
#[inline]
|
|
pub fn len(&self) -> usize {
|
|
self.buffer.len()
|
|
}
|
|
|
|
pub fn finish(self) {
|
|
let to_write = (self.buffer.len() / self.channel.num_channels as usize)
|
|
as alsa::snd_pcm_uframes_t;
|
|
let channel = self.channel.channel.lock().expect("Buffer channel lock failed");
|
|
|
|
unsafe {
|
|
loop {
|
|
let result = alsa::snd_pcm_writei(*channel,
|
|
self.buffer.as_ptr() as *const libc::c_void,
|
|
to_write);
|
|
|
|
if result == -32 {
|
|
// buffer underrun
|
|
alsa::snd_pcm_prepare(*channel);
|
|
} else if result < 0 {
|
|
check_errors(result as libc::c_int).expect("could not write pcm");
|
|
} else {
|
|
assert_eq!(result as alsa::snd_pcm_uframes_t, to_write);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn check_errors(err: libc::c_int) -> Result<(), String> {
|
|
use std::ffi;
|
|
|
|
if err < 0 {
|
|
unsafe {
|
|
let s = ffi::CStr::from_ptr(alsa::snd_strerror(err)).to_bytes().to_vec();
|
|
let s = String::from_utf8(s).expect("Streaming error occured");
|
|
return Err(s);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|