use std::ffi::{CStr, CString};
use std::{ptr, mem, fmt, ops};
use libc::{c_long, c_int, c_uint, c_short, pollfd};
use crate::poll;
use crate::alsa;
use super::Round;
use super::error::*;
const SELEM_ID_SIZE: usize = 64;
#[derive(Debug)]
pub struct Mixer(*mut alsa::snd_mixer_t);
unsafe impl Send for Mixer {}
impl Mixer {
pub fn new(name: &str, nonblock: bool) -> Result<Mixer> {
let mut mixer = Mixer::open(nonblock)?;
mixer.attach(&CString::new(name).unwrap())?;
Selem::register(&mut mixer)?;
mixer.load()?;
Ok(mixer)
}
pub fn find_selem(&self, id: &SelemId) -> Option<Selem> {
let selem = unsafe { alsa::snd_mixer_find_selem(self.0, id.as_ptr()) };
if selem.is_null() { None }
else { Some(Selem(Elem {handle: selem, _mixer: self})) }
}
pub fn open(nonblock: bool) -> Result<Mixer> {
let mut r = ptr::null_mut();
let flags = if nonblock { 1 } else { 0 }; acheck!(snd_mixer_open(&mut r, flags)).map(|_| Mixer(r))
}
pub fn attach(&mut self, name: &CStr) -> Result<()> {
acheck!(snd_mixer_attach(self.0, name.as_ptr())).map(|_| ())
}
pub fn load(&mut self) -> Result<()> {
acheck!(snd_mixer_load(self.0)).map(|_| ())
}
pub fn iter(&self) -> Iter {
Iter {
last_handle: ptr::null_mut(),
mixer: self
}
}
pub fn handle_events(&self) -> Result<u32> {
acheck!(snd_mixer_handle_events(self.0)).map(|x| x as u32)
}
pub fn wait(&self, timeout_ms: Option<u32>) -> Result<()> {
acheck!(snd_mixer_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|_| ()) }
}
impl Drop for Mixer {
fn drop(&mut self) {
unsafe { alsa::snd_mixer_close(self.0) };
}
}
impl poll::Descriptors for Mixer {
fn count(&self) -> usize {
unsafe { alsa::snd_mixer_poll_descriptors_count(self.0) as usize }
}
fn fill(&self, p: &mut [pollfd]) -> Result<usize> {
let z = unsafe { alsa::snd_mixer_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) };
from_code("snd_mixer_poll_descriptors", z).map(|_| z as usize)
}
fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> {
let mut r = 0;
let z = unsafe { alsa::snd_mixer_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) };
from_code("snd_mixer_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MilliBel(pub i64);
impl MilliBel {
pub fn to_db(self) -> f32 { (self.0 as f32) / 100.0 }
pub fn from_db(db: f32) -> Self { MilliBel((db * 100.0) as i64) }
}
impl ops::Deref for MilliBel {
type Target = i64;
fn deref(&self) -> &i64 { &self.0 }
}
impl ops::Add for MilliBel {
type Output = MilliBel;
fn add(self, rhs: Self) -> Self { MilliBel(self.0 + rhs.0) }
}
impl ops::AddAssign for MilliBel {
fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 }
}
impl ops::Sub for MilliBel {
type Output = MilliBel;
fn sub(self, rhs: Self) -> Self { MilliBel(self.0 - rhs.0) }
}
impl ops::SubAssign for MilliBel {
fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0 }
}
#[derive(Copy, Clone, Debug)]
pub struct Elem<'a>{
handle: *mut alsa::snd_mixer_elem_t,
_mixer: &'a Mixer
}
#[derive(Copy, Clone)]
pub struct Iter<'a>{
last_handle: *mut alsa::snd_mixer_elem_t,
mixer: &'a Mixer
}
impl<'a> Iterator for Iter<'a> {
type Item = Elem<'a>;
fn next(&mut self) -> Option<Elem<'a>> {
let elem = if self.last_handle.is_null() {
unsafe { alsa::snd_mixer_first_elem(self.mixer.0) }
} else {
unsafe { alsa::snd_mixer_elem_next(self.last_handle) }
};
if elem.is_null() {
None
} else {
self.last_handle = elem;
Some(Elem { handle: elem, _mixer: self.mixer})
}
}
}
pub struct SelemId([u8; SELEM_ID_SIZE]);
impl SelemId {
pub fn new(name: &str, index: u32) -> SelemId {
let mut s = SelemId::empty();
s.set_name(&CString::new(name).unwrap());
s.set_index(index);
s
}
pub fn empty() -> SelemId {
assert!(unsafe { alsa::snd_mixer_selem_id_sizeof() } as usize <= SELEM_ID_SIZE);
SelemId(unsafe { mem::zeroed() })
}
#[inline]
fn as_ptr(&self) -> *mut alsa::snd_mixer_selem_id_t {
self.0.as_ptr() as *const _ as *mut alsa::snd_mixer_selem_id_t
}
pub fn get_name(&self) -> Result<&str> {
let c = unsafe { alsa::snd_mixer_selem_id_get_name(self.as_ptr()) };
from_const("snd_mixer_selem_id_get_name", c)
}
pub fn get_index(&self) -> u32 {
unsafe { alsa::snd_mixer_selem_id_get_index(self.as_ptr()) }
}
pub fn set_name(&mut self, name: &CStr) {
unsafe { alsa::snd_mixer_selem_id_set_name(self.as_ptr(), name.as_ptr()) };
}
pub fn set_index(&mut self, index: u32) {
unsafe { alsa::snd_mixer_selem_id_set_index(self.as_ptr(), index) };
}
}
pub struct Selem<'a>(Elem<'a>);
impl<'a> Selem<'a> {
pub fn new(elem: Elem<'a>) -> Option<Selem<'a>> {
if unsafe { alsa::snd_mixer_elem_get_type(elem.handle) } == alsa::SND_MIXER_ELEM_SIMPLE
{ Some(Selem(elem)) } else { None }
}
pub fn register(mixer: &mut Mixer) -> Result<()> {
acheck!(snd_mixer_selem_register(mixer.0, ptr::null_mut(), ptr::null_mut())).map(|_| ())
}
pub fn get_id(&self) -> SelemId {
let id = SelemId::empty();
unsafe { alsa::snd_mixer_selem_get_id(self.handle, id.as_ptr()) };
id
}
pub fn has_capture_volume(&self) -> bool {
unsafe { alsa::snd_mixer_selem_has_capture_volume(self.handle) > 0 }
}
pub fn has_capture_switch(&self) -> bool {
unsafe { alsa::snd_mixer_selem_has_capture_switch(self.handle) > 0 }
}
pub fn has_playback_volume(&self) -> bool {
unsafe { alsa::snd_mixer_selem_has_playback_volume(self.handle) > 0 }
}
pub fn has_playback_switch(&self) -> bool {
unsafe { alsa::snd_mixer_selem_has_playback_switch(self.handle) > 0 }
}
pub fn can_capture(&self) -> bool {
self.has_capture_volume() || self.has_capture_switch()
}
pub fn can_playback(&self) -> bool {
self.has_playback_volume() || self.has_playback_switch()
}
pub fn has_volume(&self) -> bool {
self.has_capture_volume() || self.has_playback_volume()
}
pub fn get_capture_volume_range(&self) -> (i64, i64) {
let mut min: c_long = 0;
let mut max: c_long = 0;
unsafe { alsa::snd_mixer_selem_get_capture_volume_range(self.handle, &mut min, &mut max) };
(min as i64, max as i64)
}
pub fn get_capture_db_range(&self) -> (MilliBel, MilliBel) {
let mut min: c_long = 0;
let mut max: c_long = 0;
unsafe { alsa::snd_mixer_selem_get_capture_dB_range(self.handle, &mut min, &mut max) };
(MilliBel(min as i64), MilliBel(max as i64))
}
pub fn get_playback_volume_range(&self) -> (i64, i64) {
let mut min: c_long = 0;
let mut max: c_long = 0;
unsafe { alsa::snd_mixer_selem_get_playback_volume_range(self.handle, &mut min, &mut max) };
(min as i64, max as i64)
}
pub fn get_playback_db_range(&self) -> (MilliBel, MilliBel) {
let mut min: c_long = 0;
let mut max: c_long = 0;
unsafe { alsa::snd_mixer_selem_get_playback_dB_range(self.handle, &mut min, &mut max) };
(MilliBel(min as i64), MilliBel(max as i64))
}
pub fn is_capture_mono(&self) -> bool {
unsafe { alsa::snd_mixer_selem_is_capture_mono(self.handle) == 1 }
}
pub fn is_playback_mono(&self) -> bool {
unsafe { alsa::snd_mixer_selem_is_playback_mono(self.handle) == 1 }
}
pub fn has_capture_channel(&self, channel: SelemChannelId) -> bool {
unsafe { alsa::snd_mixer_selem_has_capture_channel(self.handle, channel as i32) > 0 }
}
pub fn has_playback_channel(&self, channel: SelemChannelId) -> bool {
unsafe { alsa::snd_mixer_selem_has_playback_channel(self.handle, channel as i32) > 0 }
}
pub fn channel_name(channel: SelemChannelId) -> Result<&'static str> {
let c = unsafe { alsa::snd_mixer_selem_channel_name(channel as i32) };
from_const("snd_mixer_selem_channel_name", c)
}
pub fn get_playback_volume(&self, channel: SelemChannelId) -> Result<i64> {
let mut value: c_long = 0;
acheck!(snd_mixer_selem_get_playback_volume(self.handle, channel as i32, &mut value)).and_then(|_| Ok(value as i64))
}
pub fn get_playback_vol_db(&self, channel: SelemChannelId) -> Result<MilliBel> {
self.get_playback_volume(channel)
.and_then(|volume| self.ask_playback_vol_db(volume))
}
pub fn ask_playback_vol_db(&self, volume: i64) -> Result<MilliBel> {
let mut decibel_value: c_long = 0;
acheck!(snd_mixer_selem_ask_playback_vol_dB(self.handle, volume as c_long, &mut decibel_value))
.map(|_| MilliBel(decibel_value as i64))
}
pub fn ask_playback_db_vol(&self, db: MilliBel, dir: Round) -> Result<i64> {
let mut raw_volume: c_long = 0;
acheck!(snd_mixer_selem_ask_playback_dB_vol(self.handle, db.0 as c_long, dir as c_int, &mut raw_volume))
.map(|_| raw_volume as i64)
}
pub fn get_capture_volume(&self, channel: SelemChannelId) -> Result<i64> {
let mut value: c_long = 0;
acheck!(snd_mixer_selem_get_capture_volume(self.handle, channel as i32, &mut value)).map(|_| value as i64)
}
pub fn get_capture_vol_db(&self, channel: SelemChannelId) -> Result<MilliBel> {
self.get_capture_volume(channel)
.and_then(|volume| self.ask_capture_vol_db(volume))
}
pub fn ask_capture_vol_db(&self, volume: i64) -> Result<MilliBel> {
let mut decibel_value: c_long = 0;
acheck!(snd_mixer_selem_ask_capture_vol_dB (self.handle, volume as c_long, &mut decibel_value))
.map(|_| MilliBel(decibel_value as i64))
}
pub fn ask_capture_db_vol(&self, db: MilliBel, dir: Round) -> Result<i64> {
let mut raw_volume: c_long = 0;
acheck!(snd_mixer_selem_ask_capture_dB_vol(self.handle, db.0 as c_long, dir as c_int, &mut raw_volume))
.map(|_| raw_volume as i64)
}
pub fn set_playback_volume(&self, channel: SelemChannelId, value: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_volume(self.handle, channel as i32, value as c_long)).map(|_| ())
}
pub fn set_playback_volume_range(&self, min: i64, max: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_volume_range(self.handle, min as c_long, max as c_long)).map(|_| ())
}
pub fn set_playback_volume_all(&self, value: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_volume_all(self.handle, value as c_long)).map(|_| ())
}
pub fn set_playback_db(&self, channel: SelemChannelId, value: MilliBel, dir: Round) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_dB(self.handle, channel as i32, *value as c_long, dir as c_int)).map(|_| ())
}
pub fn set_capture_db(&self, channel: SelemChannelId, value: MilliBel, dir: Round) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_dB(self.handle, channel as i32, *value as c_long, dir as c_int)).map(|_| ())
}
pub fn set_playback_db_all(&self, value: MilliBel, dir: Round) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_dB_all(self.handle, *value as c_long, dir as c_int)).map(|_| ())
}
pub fn set_capture_db_all(&self, value: MilliBel, dir: Round) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_dB_all(self.handle, *value as c_long, dir as c_int)).map(|_| ())
}
pub fn set_capture_volume(&self, channel: SelemChannelId, value: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_volume(self.handle, channel as i32, value as c_long)).map(|_| ())
}
pub fn set_capture_volume_range(&self, min: i64, max: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_volume_range(self.handle, min as c_long, max as c_long)).map(|_| ())
}
pub fn set_capture_volume_all(&self, value: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_volume_all(self.handle, value as c_long)).map(|_| ())
}
pub fn set_playback_switch(&self, channel: SelemChannelId, value: i32) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_switch(self.handle, channel as i32, value)).map(|_| ())
}
pub fn set_playback_switch_all(&self, value: i32) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_switch_all(self.handle, value)).map(|_| ())
}
pub fn set_capture_switch(&self, channel: SelemChannelId, value: i32) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_switch(self.handle, channel as i32, value)).map(|_| ())
}
pub fn set_capture_switch_all(&self, value: i32) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_switch_all(self.handle, value)).map(|_| ())
}
pub fn get_playback_switch(&self, channel: SelemChannelId) -> Result<i32> {
let mut value: i32 = 0;
acheck!(snd_mixer_selem_get_playback_switch(self.handle, channel as i32, &mut value)).map(|_| value)
}
pub fn get_capture_switch(&self, channel: SelemChannelId) -> Result<i32> {
let mut value: i32 = 0;
acheck!(snd_mixer_selem_get_capture_switch(self.handle, channel as i32, &mut value)).map(|_| value)
}
pub fn is_enumerated(&self) -> bool {
unsafe { alsa::snd_mixer_selem_is_enumerated(self.handle) == 1 }
}
pub fn is_enum_playback(&self) -> bool {
unsafe { alsa::snd_mixer_selem_is_enum_playback(self.handle) == 1 }
}
pub fn is_enum_capture(&self) -> bool {
unsafe { alsa::snd_mixer_selem_is_enum_capture(self.handle) == 1 }
}
pub fn get_enum_items(&self) -> Result<u32> {
acheck!(snd_mixer_selem_get_enum_items(self.handle)).map(|v| v as u32)
}
pub fn get_enum_item_name(&self, idx: u32) -> Result<String> {
let mut temp = [0 as ::libc::c_char; 128];
acheck!(snd_mixer_selem_get_enum_item_name(self.handle, idx, temp.len()-1, temp.as_mut_ptr()))
.and_then(|_| from_const("snd_mixer_selem_get_enum_item_name", temp.as_ptr()))
.map(|v| v.into())
}
pub fn iter_enum(&self) -> Result<IterEnum> {
Ok(IterEnum(self, 0, self.get_enum_items()?))
}
pub fn get_enum_item(&self, channel: SelemChannelId) -> Result<u32> {
let mut temp = 0;
acheck!(snd_mixer_selem_get_enum_item(self.handle, channel as i32, &mut temp))
.map(|_| temp)
}
pub fn set_enum_item(&self, channel: SelemChannelId, idx: u32) -> Result<()> {
acheck!(snd_mixer_selem_set_enum_item(self.handle, channel as i32, idx))
.map(|_| ())
}
}
impl<'a> ops::Deref for Selem<'a> {
type Target = Elem<'a>;
fn deref(&self) -> &Elem<'a> {
&self.0
}
}
pub struct IterEnum<'a>(&'a Selem<'a>, u32, u32);
impl<'a> Iterator for IterEnum<'a> {
type Item = Result<String>;
fn next(&mut self) -> Option<Self::Item> {
if self.1 >= self.2 { None }
else { self.1 += 1; Some(self.0.get_enum_item_name(self.1-1)) }
}
}
alsa_enum!(
SelemChannelId, ALL_SELEM_CHANNEL_ID[11],
Unknown = SND_MIXER_SCHN_UNKNOWN,
FrontLeft = SND_MIXER_SCHN_FRONT_LEFT,
FrontRight = SND_MIXER_SCHN_FRONT_RIGHT,
RearLeft = SND_MIXER_SCHN_REAR_LEFT,
RearRight = SND_MIXER_SCHN_REAR_RIGHT,
FrontCenter = SND_MIXER_SCHN_FRONT_CENTER,
Woofer = SND_MIXER_SCHN_WOOFER,
SideLeft = SND_MIXER_SCHN_SIDE_LEFT,
SideRight = SND_MIXER_SCHN_SIDE_RIGHT,
RearCenter = SND_MIXER_SCHN_REAR_CENTER,
Last = SND_MIXER_SCHN_LAST,
);
impl SelemChannelId {
pub fn mono() -> SelemChannelId { SelemChannelId::FrontLeft }
}
impl fmt::Display for SelemChannelId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", Selem::channel_name(*self).unwrap())
}
}
#[test]
fn print_mixer_of_cards() {
use super::card;
for card in card::Iter::new().map(|c| c.unwrap()) {
println!("Card #{}: {} ({})", card.get_index(), card.get_name().unwrap(), card.get_longname().unwrap());
let mixer = Mixer::new(&format!("hw:{}", card.get_index()), false).unwrap();
for selem in mixer.iter().filter_map(|e| Selem::new(e)) {
let sid = selem.get_id();
println!("\tMixer element {},{}:", sid.get_name().unwrap(), sid.get_index());
if selem.has_volume() {
print!("\t Volume limits: ");
if selem.has_capture_volume() {
let (vmin, vmax) = selem.get_capture_volume_range();
let (mbmin, mbmax) = selem.get_capture_db_range();
print!("Capture = {} - {}", vmin, vmax);
print!(" ({} dB - {} dB)", mbmin.to_db(), mbmax.to_db());
}
if selem.has_playback_volume() {
let (vmin, vmax) = selem.get_playback_volume_range();
let (mbmin, mbmax) = selem.get_playback_db_range();
print!("Playback = {} - {}", vmin, vmax);
print!(" ({} dB - {} dB)", mbmin.to_db(), mbmax.to_db());
}
println!();
}
if selem.is_enumerated() {
print!("\t Valid values: ");
for v in selem.iter_enum().unwrap() { print!("{}, ", v.unwrap()) };
print!("\n\t Current values: ");
for v in SelemChannelId::all().iter().filter_map(|&v| selem.get_enum_item(v).ok()) {
print!("{}, ", selem.get_enum_item_name(v).unwrap());
}
println!();
}
if selem.can_capture() {
print!("\t Capture channels: ");
if selem.is_capture_mono() {
print!("Mono");
} else {
for channel in SelemChannelId::all() {
if selem.has_capture_channel(*channel) { print!("{}, ", channel) };
}
}
println!();
print!("\t Capture volumes: ");
for channel in SelemChannelId::all() {
if selem.has_capture_channel(*channel) { print!("{}: {} ({} dB), ", channel,
match selem.get_capture_volume(*channel) {Ok(v) => format!("{}", v), Err(_) => "n/a".to_string()},
match selem.get_capture_vol_db(*channel) {Ok(v) => format!("{}", v.to_db()), Err(_) => "n/a".to_string()}
);}
}
println!();
}
if selem.can_playback() {
print!("\t Playback channels: ");
if selem.is_playback_mono() {
print!("Mono");
} else {
for channel in SelemChannelId::all() {
if selem.has_playback_channel(*channel) { print!("{}, ", channel) };
}
}
println!();
if selem.has_playback_volume() {
print!("\t Playback volumes: ");
for channel in SelemChannelId::all() {
if selem.has_playback_channel(*channel) { print!("{}: {} / {}dB, ",
channel,
match selem.get_playback_volume(*channel) {Ok(v) => format!("{}", v), Err(_) => "n/a".to_string()},
match selem.get_playback_vol_db(*channel) {Ok(v) => format!("{}", v.to_db()), Err(_) => "n/a".to_string()}
);}
}
println!();
}
}
}
}
}
#[test]
#[ignore]
fn get_and_set_playback_volume() {
let mixer = Mixer::new("hw:1", false).unwrap();
let selem = mixer.find_selem(&SelemId::new("Master", 0)).unwrap();
let (rmin, rmax) = selem.get_playback_volume_range();
let mut channel = SelemChannelId::mono();
for c in SelemChannelId::all().iter() {
if selem.has_playback_channel(*c) { channel = *c; break }
}
println!("Testing on {} with limits {}-{} on channel {}", selem.get_id().get_name().unwrap(), rmin, rmax, channel);
let old: i64 = selem.get_playback_volume(channel).unwrap();
let new: i64 = rmax / 2;
assert_ne!(new, old);
println!("Changing volume of {} from {} to {}", channel, old, new);
selem.set_playback_volume(channel, new).unwrap();
let mut result: i64 = selem.get_playback_volume(channel).unwrap();
assert_eq!(new, result);
selem.set_playback_volume(channel, old).unwrap();
result = selem.get_playback_volume(channel).unwrap();
assert_eq!(old, result);
}
#[test]
#[ignore]
fn get_and_set_capture_volume() {
let mixer = Mixer::new("hw:1", false).unwrap();
let selem = mixer.find_selem(&SelemId::new("Capture", 0)).unwrap();
let (rmin, rmax) = selem.get_capture_volume_range();
let mut channel = SelemChannelId::mono();
for c in SelemChannelId::all().iter() {
if selem.has_playback_channel(*c) { channel = *c; break }
}
println!("Testing on {} with limits {}-{} on channel {}", selem.get_id().get_name().unwrap(), rmin, rmax, channel);
let old: i64 = selem.get_capture_volume(channel).unwrap();
let new: i64 = rmax / 2;
assert_ne!(new, old);
println!("Changing volume of {} from {} to {}", channel, old, new);
selem.set_capture_volume(channel, new).unwrap();
let mut result: i64 = selem.get_capture_volume(channel).unwrap();
assert_eq!(new, result);
selem.set_capture_volume(channel, old).unwrap();
result = selem.get_capture_volume(channel).unwrap();
assert_eq!(old, result);
}
#[test]
fn print_sizeof() {
let selemid = unsafe { alsa::snd_mixer_selem_id_sizeof() } as usize;
assert!(selemid <= SELEM_ID_SIZE);
println!("Selem id: {}", selemid);
}