librespot/playback/src/mixer/alsamixer.rs

180 lines
5.8 KiB
Rust
Raw Normal View History

2018-03-20 15:32:43 +00:00
use super::AudioFilter;
use super::{Mixer, MixerConfig};
2018-09-17 15:28:54 +00:00
use std;
2018-09-01 00:42:50 +00:00
use std::error::Error;
2018-03-20 15:32:43 +00:00
use alsa;
2018-09-17 15:28:54 +00:00
#[derive(Clone)]
struct AlsaMixerVolumeParams {
min: i64,
max: i64,
range: f64,
min_db: alsa::mixer::MilliBel,
max_db: alsa::mixer::MilliBel,
}
2018-03-20 15:32:43 +00:00
#[derive(Clone)]
pub struct AlsaMixer {
config: MixerConfig,
2018-09-17 15:28:54 +00:00
params: AlsaMixerVolumeParams,
2018-03-20 15:32:43 +00:00
}
2018-09-01 00:42:50 +00:00
impl AlsaMixer {
2018-09-17 15:28:54 +00:00
fn pvol<T>(&self, vol: T, min: T, max: T) -> f64
where
T: std::ops::Sub + Copy,
f64: std::convert::From<<T as std::ops::Sub>::Output>,
{
f64::from(vol - min) / f64::from(max - min)
}
fn init_mixer(mut config: MixerConfig) -> Result<AlsaMixer, Box<Error>> {
let mixer = alsa::mixer::Mixer::new(&config.card, false)?;
let sid = alsa::mixer::SelemId::new(&config.mixer, config.index);
let selem = mixer.find_selem(&sid).expect("Couldn't find SelemId");
let (min, max) = selem.get_playback_volume_range();
let (min_db, max_db) = selem.get_playback_db_range();
let hw_mix = selem
.get_playback_vol_db(alsa::mixer::SelemChannelId::mono())
.is_ok();
2018-09-17 15:28:54 +00:00
info!(
"Alsa min: {} ({:?}[dB]) -- max: {} ({:?}[dB]) HW: {:?}",
min, min_db, max, max_db, hw_mix
2018-09-17 15:28:54 +00:00
);
if config.mapped_volume && (max_db - min_db <= alsa::mixer::MilliBel(24)) {
warn!(
"Switching to linear volume mapping, control range: {:?}",
max_db - min_db
);
config.mapped_volume = false;
} else if !config.mapped_volume {
info!("Using Alsa linear volume");
2018-09-17 15:28:54 +00:00
}
Ok(AlsaMixer {
config: config,
params: AlsaMixerVolumeParams {
min: min,
max: max,
range: (max - min) as f64,
min_db: min_db,
max_db: max_db,
},
})
}
fn map_volume(&self, set_volume: Option<u16>) -> Result<(u16), Box<Error>> {
2018-10-16 14:46:26 +00:00
let mixer = alsa::mixer::Mixer::new(&self.config.card, false)?;
let sid = alsa::mixer::SelemId::new(&*self.config.mixer, self.config.index);
2018-09-01 00:42:50 +00:00
2018-09-17 15:28:54 +00:00
let selem = mixer.find_selem(&sid).expect("Couldn't find SelemId");
let cur_vol = selem
.get_playback_volume(alsa::mixer::SelemChannelId::mono())
.expect("Couldn't get current volume");
let cur_vol_db = selem
.get_playback_vol_db(alsa::mixer::SelemChannelId::mono())
.unwrap_or(alsa::mixer::MilliBel(9999));
2018-09-01 00:42:50 +00:00
2018-10-16 14:46:26 +00:00
let new_vol: u16;
info!("Current alsa volume: {}[i64] {:?}", cur_vol, cur_vol_db);
2018-09-01 00:42:50 +00:00
if let Some(vol) = set_volume {
if self.config.mapped_volume {
info!("Using mapped_volume");
//TODO Check if min is not mute!
let vol_db = ((self.pvol(vol, 0x0000, 0xFFFF)).log10() * 6000.0).floor() as i64
+ self.params.max;
selem
.set_playback_volume_all(vol_db)
.expect("Couldn't set alsa dB volume");
info!(
"Mapping volume [{:.3}%] {:?} [u16] ->> Alsa [{:.3}%] {:?} [dB]",
self.pvol(vol, 0x0000, 0xFFFF) * 100.0,
vol,
self.pvol(
vol_db as f64,
self.params.min_db.to_db() as f64,
self.params.max_db.to_db() as f64
) * 100.0,
vol_db as f64 / 100.0,
);
2018-09-17 15:28:54 +00:00
} else {
info!("Using linear vol");
let alsa_volume =
((vol as f64 / 0xFFFF as f64) * self.params.range) as i64 + self.params.min;
selem
.set_playback_volume_all(alsa_volume)
.expect("Couldn't set alsa raw volume");
info!(
"Mapping volume [{:.3}%] {:?} [u16] ->> Alsa [{:.3}%] {:?} [i64]",
self.pvol(vol, 0x0000, 0xFFFF) * 100.0,
vol,
self.pvol(
alsa_volume as f64,
self.params.min as f64,
self.params.max as f64
) * 100.0,
alsa_volume
);
2018-09-17 15:28:54 +00:00
};
new_vol = vol; // Meh
2018-09-01 00:42:50 +00:00
} else {
2018-09-17 15:28:54 +00:00
new_vol =
(((cur_vol - self.params.min) as f64 / self.params.range) * 0xFFFF as f64) as u16;
info!(
"Mapping volume [{:.3}%] {:?} [u16] <<- Alsa [{:.3}%] {:?} [i64]",
2018-09-17 15:28:54 +00:00
self.pvol(new_vol, 0x0000, 0xFFFF),
new_vol,
self.pvol(
cur_vol as f64,
self.params.min as f64,
self.params.max as f64
),
cur_vol
);
2018-09-01 00:42:50 +00:00
}
Ok(new_vol)
}
}
2018-03-21 21:18:37 +00:00
2018-03-20 15:32:43 +00:00
impl Mixer for AlsaMixer {
fn open(config: Option<MixerConfig>) -> AlsaMixer {
let config = config.unwrap_or_default();
2018-09-01 00:42:50 +00:00
info!(
"Setting up new mixer: card:{} mixer:{} index:{}",
config.card, config.mixer, config.index
2018-09-01 00:42:50 +00:00
);
2018-09-17 15:28:54 +00:00
AlsaMixer::init_mixer(config).expect("Error setting up mixer!")
2018-03-20 15:32:43 +00:00
}
2018-10-16 14:46:26 +00:00
fn start(&self) {}
2018-03-20 15:32:43 +00:00
2018-10-16 14:46:26 +00:00
fn stop(&self) {}
2018-03-20 15:32:43 +00:00
fn volume(&self) -> u16 {
2018-10-16 14:46:26 +00:00
match self.map_volume(None) {
Ok(vol) => vol,
Err(e) => {
error!("Error getting volume for <{}>, {:?}", self.config.card, e);
0
}
2018-09-01 00:42:50 +00:00
}
2018-03-20 15:32:43 +00:00
}
fn set_volume(&self, volume: u16) {
2018-10-16 14:46:26 +00:00
match self.map_volume(Some(volume)) {
Ok(_) => (),
Err(e) => error!("Error setting volume for <{}>, {:?}", self.config.card, e),
2018-09-01 00:42:50 +00:00
}
2018-03-20 15:32:43 +00:00
}
fn get_audio_filter(&self) -> Option<Box<dyn AudioFilter + Send>> {
2018-03-20 15:32:43 +00:00
None
}
}