librespot_playback/mixer/
mod.rs1use crate::config::VolumeCtrl;
2use librespot_core::Error;
3use std::sync::Arc;
4
5pub mod mappings;
6use self::mappings::MappedCtrl;
7
8pub struct NoOpVolume;
9
10pub trait Mixer: Send + Sync {
11 fn open(config: MixerConfig) -> Result<Self, Error>
12 where
13 Self: Sized;
14
15 fn volume(&self) -> u16;
16 fn set_volume(&self, volume: u16);
17
18 fn get_soft_volume(&self) -> Box<dyn VolumeGetter + Send> {
19 Box::new(NoOpVolume)
20 }
21}
22
23pub trait VolumeGetter {
24 fn attenuation_factor(&self) -> f64;
25}
26
27impl VolumeGetter for NoOpVolume {
28 #[inline]
29 fn attenuation_factor(&self) -> f64 {
30 1.0
31 }
32}
33
34pub mod softmixer;
35use self::softmixer::SoftMixer;
36
37#[cfg(feature = "alsa-backend")]
38pub mod alsamixer;
39#[cfg(feature = "alsa-backend")]
40use self::alsamixer::AlsaMixer;
41
42#[derive(Debug, Clone)]
43pub struct MixerConfig {
44 pub device: String,
45 pub control: String,
46 pub index: u32,
47 pub volume_ctrl: VolumeCtrl,
48}
49
50impl Default for MixerConfig {
51 fn default() -> MixerConfig {
52 MixerConfig {
53 device: String::from("default"),
54 control: String::from("PCM"),
55 index: 0,
56 volume_ctrl: VolumeCtrl::default(),
57 }
58 }
59}
60
61pub type MixerFn = fn(MixerConfig) -> Result<Arc<dyn Mixer>, Error>;
62
63fn mk_sink<M: Mixer + 'static>(config: MixerConfig) -> Result<Arc<dyn Mixer>, Error> {
64 Ok(Arc::new(M::open(config)?))
65}
66
67pub const MIXERS: &[(&str, MixerFn)] = &[
68 (SoftMixer::NAME, mk_sink::<SoftMixer>), #[cfg(feature = "alsa-backend")]
70 (AlsaMixer::NAME, mk_sink::<AlsaMixer>),
71];
72
73pub fn find(name: Option<&str>) -> Option<MixerFn> {
74 if let Some(name) = name {
75 MIXERS
76 .iter()
77 .find(|mixer| name == mixer.0)
78 .map(|mixer| mixer.1)
79 } else {
80 MIXERS.first().map(|mixer| mixer.1)
81 }
82}