From c2ed8467654b5ad7a1ca72f695985813e4b88b48 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 16 Dec 2014 16:45:45 +0100 Subject: [PATCH] Add basic implementation for alsa --- src/alsa/mod.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 ++- 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/alsa/mod.rs diff --git a/src/alsa/mod.rs b/src/alsa/mod.rs new file mode 100644 index 0000000..4dddd88 --- /dev/null +++ b/src/alsa/mod.rs @@ -0,0 +1,107 @@ +extern crate "alsa-sys" as alsa; +extern crate libc; + +use std::{mem, ptr}; + +pub struct Channel { + channel: *mut alsa::snd_pcm_t, + num_channels: u16, +} + +pub struct Buffer<'a, T> { + channel: &'a mut Channel, + buffer: Vec, +} + +impl Channel { + pub fn new() -> Channel { + unsafe { + let name = "default".to_c_str(); + + let mut playback_handle = mem::uninitialized(); + check_errors(alsa::snd_pcm_open(&mut playback_handle, name.as_ptr(), alsa::SND_PCM_STREAM_PLAYBACK, alsa::SND_PCM_NONBLOCK)).unwrap(); + + let mut hw_params = mem::uninitialized(); + check_errors(alsa::snd_pcm_hw_params_malloc(&mut hw_params)).unwrap(); + check_errors(alsa::snd_pcm_hw_params_any(playback_handle, hw_params)).unwrap(); + check_errors(alsa::snd_pcm_hw_params_set_access(playback_handle, hw_params, alsa::SND_PCM_ACCESS_RW_INTERLEAVED)).unwrap(); + check_errors(alsa::snd_pcm_hw_params_set_format(playback_handle, hw_params, alsa::SND_PCM_FORMAT_S16_LE)).unwrap(); // TODO: check endianess + check_errors(alsa::snd_pcm_hw_params_set_rate(playback_handle, hw_params, 44100, 0)).unwrap(); + check_errors(alsa::snd_pcm_hw_params_set_channels(playback_handle, hw_params, 2)).unwrap(); + check_errors(alsa::snd_pcm_hw_params(playback_handle, hw_params)).unwrap(); + alsa::snd_pcm_hw_params_free(hw_params); + + check_errors(alsa::snd_pcm_prepare(playback_handle)).unwrap(); + + Channel { + channel: playback_handle, + num_channels: 2, + } + } + } + + pub fn get_channels(&self) -> ::ChannelsCount { + self.num_channels + } + + pub fn get_samples_rate(&self) -> ::SamplesRate { + ::SamplesRate(44100) + } + + pub fn get_samples_format(&self) -> ::SampleFormat { + ::SampleFormat::U16 + } + + pub fn append_data<'a, T>(&'a mut self, max_elements: uint) -> Buffer<'a, T> where T: Clone { + let available = unsafe { alsa::snd_pcm_avail(self.channel) }; + let available = available * self.num_channels as alsa::snd_pcm_sframes_t; + + let elements = ::std::cmp::min(available as uint, max_elements); + + Buffer { + channel: self, + buffer: Vec::from_elem(elements, unsafe { mem::uninitialized() }) + } + } +} + +impl Drop for Channel { + fn drop(&mut self) { + unsafe { + alsa::snd_pcm_close(self.channel); + } + } +} + +impl<'a, T> Buffer<'a, T> { + pub fn get_buffer<'b>(&'b mut self) -> &'b mut [T] { + self.buffer.as_mut_slice() + } + + pub fn finish(self) { + let written = (self.buffer.len() / self.channel.num_channels as uint) as alsa::snd_pcm_uframes_t; + + unsafe { + let result = alsa::snd_pcm_writei(self.channel.channel, + self.buffer.as_ptr() as *const libc::c_void, + written); + + if result < 0 { + check_errors(result as libc::c_int).unwrap(); + } + } + } +} + +fn check_errors(err: libc::c_int) -> Result<(), String> { + use std::c_str::CString; + + if err < 0 { + unsafe { + let s = CString::new(alsa::snd_strerror(err), false); + return Err(s.to_string()); + } + } + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 6dc394b..30b30f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,13 @@ #![feature(unsafe_destructor)] -#[cfg(all(not(windows)))] +#[cfg(all(not(windows), not(unix)))] use this_platform_is_not_supported; mod conversions; +#[cfg(unix)] +#[path="alsa/mod.rs"] +pub mod cpal_impl; #[cfg(windows)] #[path="wasapi/mod.rs"] pub mod cpal_impl;