alsamixer.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. use super::AudioFilter;
  2. use super::{Mixer, MixerConfig};
  3. use std;
  4. use std::error::Error;
  5. use alsa;
  6. #[derive(Clone)]
  7. struct AlsaMixerVolumeParams {
  8. min: i64,
  9. max: i64,
  10. range: f64,
  11. min_db: alsa::mixer::MilliBel,
  12. max_db: alsa::mixer::MilliBel,
  13. }
  14. #[derive(Clone)]
  15. pub struct AlsaMixer {
  16. config: MixerConfig,
  17. params: AlsaMixerVolumeParams,
  18. }
  19. impl AlsaMixer {
  20. fn pvol<T>(&self, vol: T, min: T, max: T) -> f64
  21. where
  22. T: std::ops::Sub + Copy,
  23. f64: std::convert::From<<T as std::ops::Sub>::Output>,
  24. {
  25. f64::from(vol - min) / f64::from(max - min)
  26. }
  27. fn init_mixer(mut config: MixerConfig) -> Result<AlsaMixer, Box<Error>> {
  28. let mixer = alsa::mixer::Mixer::new(&config.card, false)?;
  29. let sid = alsa::mixer::SelemId::new(&config.mixer, config.index);
  30. let selem = mixer.find_selem(&sid).expect("Couldn't find SelemId");
  31. let (min, max) = selem.get_playback_volume_range();
  32. let (min_db, max_db) = selem.get_playback_db_range();
  33. let hw_mix = selem
  34. .get_playback_vol_db(alsa::mixer::SelemChannelId::mono())
  35. .is_ok();
  36. info!(
  37. "Alsa min: {} ({:?}[dB]) -- max: {} ({:?}[dB]) HW: {:?}",
  38. min, min_db, max, max_db, hw_mix
  39. );
  40. if config.mapped_volume && (max_db - min_db <= alsa::mixer::MilliBel(24)) {
  41. warn!(
  42. "Switching to linear volume mapping, control range: {:?}",
  43. max_db - min_db
  44. );
  45. config.mapped_volume = false;
  46. } else if !config.mapped_volume {
  47. info!("Using Alsa linear volume");
  48. }
  49. Ok(AlsaMixer {
  50. config: config,
  51. params: AlsaMixerVolumeParams {
  52. min: min,
  53. max: max,
  54. range: (max - min) as f64,
  55. min_db: min_db,
  56. max_db: max_db,
  57. },
  58. })
  59. }
  60. fn map_volume(&self, set_volume: Option<u16>) -> Result<(u16), Box<Error>> {
  61. let mixer = alsa::mixer::Mixer::new(&self.config.card, false)?;
  62. let sid = alsa::mixer::SelemId::new(&*self.config.mixer, self.config.index);
  63. let selem = mixer.find_selem(&sid).expect("Couldn't find SelemId");
  64. let cur_vol = selem
  65. .get_playback_volume(alsa::mixer::SelemChannelId::mono())
  66. .expect("Couldn't get current volume");
  67. let cur_vol_db = selem
  68. .get_playback_vol_db(alsa::mixer::SelemChannelId::mono())
  69. .unwrap_or(alsa::mixer::MilliBel(9999));
  70. let new_vol: u16;
  71. info!("Current alsa volume: {}[i64] {:?}", cur_vol, cur_vol_db);
  72. if let Some(vol) = set_volume {
  73. if self.config.mapped_volume {
  74. info!("Using mapped_volume");
  75. //TODO Check if min is not mute!
  76. let vol_db = ((self.pvol(vol, 0x0000, 0xFFFF)).log10() * 6000.0).floor() as i64
  77. + self.params.max;
  78. selem
  79. .set_playback_volume_all(vol_db)
  80. .expect("Couldn't set alsa dB volume");
  81. info!(
  82. "Mapping volume [{:.3}%] {:?} [u16] ->> Alsa [{:.3}%] {:?} [dB]",
  83. self.pvol(vol, 0x0000, 0xFFFF) * 100.0,
  84. vol,
  85. self.pvol(
  86. vol_db as f64,
  87. self.params.min_db.to_db() as f64,
  88. self.params.max_db.to_db() as f64
  89. ) * 100.0,
  90. vol_db as f64 / 100.0,
  91. );
  92. } else {
  93. info!("Using linear vol");
  94. let alsa_volume =
  95. ((vol as f64 / 0xFFFF as f64) * self.params.range) as i64 + self.params.min;
  96. selem
  97. .set_playback_volume_all(alsa_volume)
  98. .expect("Couldn't set alsa raw volume");
  99. info!(
  100. "Mapping volume [{:.3}%] {:?} [u16] ->> Alsa [{:.3}%] {:?} [i64]",
  101. self.pvol(vol, 0x0000, 0xFFFF) * 100.0,
  102. vol,
  103. self.pvol(
  104. alsa_volume as f64,
  105. self.params.min as f64,
  106. self.params.max as f64
  107. ) * 100.0,
  108. alsa_volume
  109. );
  110. };
  111. new_vol = vol; // Meh
  112. } else {
  113. new_vol =
  114. (((cur_vol - self.params.min) as f64 / self.params.range) * 0xFFFF as f64) as u16;
  115. info!(
  116. "Mapping volume [{:.3}%] {:?} [u16] <<- Alsa [{:.3}%] {:?} [i64]",
  117. self.pvol(new_vol, 0x0000, 0xFFFF),
  118. new_vol,
  119. self.pvol(
  120. cur_vol as f64,
  121. self.params.min as f64,
  122. self.params.max as f64
  123. ),
  124. cur_vol
  125. );
  126. }
  127. Ok(new_vol)
  128. }
  129. }
  130. impl Mixer for AlsaMixer {
  131. fn open(config: Option<MixerConfig>) -> AlsaMixer {
  132. let config = config.unwrap_or_default();
  133. info!(
  134. "Setting up new mixer: card:{} mixer:{} index:{}",
  135. config.card, config.mixer, config.index
  136. );
  137. AlsaMixer::init_mixer(config).expect("Error setting up mixer!")
  138. }
  139. fn start(&self) {}
  140. fn stop(&self) {}
  141. fn volume(&self) -> u16 {
  142. match self.map_volume(None) {
  143. Ok(vol) => vol,
  144. Err(e) => {
  145. error!("Error getting volume for <{}>, {:?}", self.config.card, e);
  146. 0
  147. }
  148. }
  149. }
  150. fn set_volume(&self, volume: u16) {
  151. match self.map_volume(Some(volume)) {
  152. Ok(_) => (),
  153. Err(e) => error!("Error setting volume for <{}>, {:?}", self.config.card, e),
  154. }
  155. }
  156. fn get_audio_filter(&self) -> Option<Box<dyn AudioFilter + Send>> {
  157. None
  158. }
  159. }