1+ use color_eyre:: eyre:: { self , Context , eyre} ;
12use librespot_playback:: mixer:: { Mixer , MixerConfig } ;
23use log:: error;
3- use std:: error :: Error ;
4+ use std:: sync :: { Arc , Mutex , MutexGuard } ;
45
5- #[ derive( Clone ) ]
66pub struct AlsaMixer {
7- pub device : String ,
8- pub mixer : String ,
9- pub linear_scaling : bool ,
7+ pub mixer : Arc < Mutex < alsa:: Mixer > > ,
8+ pub config : MixerConfig ,
109}
1110
1211impl AlsaMixer {
13- fn set_volume_with_err ( & self , volume : u16 ) -> Result < ( ) , Box < dyn Error > > {
14- let mixer = alsa:: mixer:: Mixer :: new ( & self . device , false ) ?;
15-
16- let selem_id = alsa:: mixer:: SelemId :: new ( & self . mixer , 0 ) ;
17- let elem = mixer. find_selem ( & selem_id) . ok_or_else ( || {
18- format ! (
19- "Couldn't find selem with name '{}'." ,
20- selem_id. get_name( ) . unwrap_or( "unnamed" )
12+ fn get_selem < ' a > (
13+ & ' a self ,
14+ lock : & ' a MutexGuard < ' a , alsa:: Mixer > ,
15+ ) -> eyre:: Result < alsa:: mixer:: Selem < ' a > > {
16+ let selem_id = alsa:: mixer:: SelemId :: new ( & self . config . control , self . config . index ) ;
17+ let selem = lock. find_selem ( & selem_id) . ok_or_else ( || {
18+ eyre ! (
19+ "No control with name '{}' in alsa device '{}'" ,
20+ self . config. control,
21+ self . config. device,
2122 )
2223 } ) ?;
23-
24+ Ok ( selem)
25+ }
26+ fn set_volume_with_err ( & self , volume : u16 ) -> eyre:: Result < ( ) > {
27+ let lock = self . mixer . lock ( ) . expect ( "lock shouldn't be poisoned" ) ;
28+ let elem = self . get_selem ( & lock) ?;
2429 let ( min, max) = elem. get_playback_volume_range ( ) ;
2530
2631 let volume_steps = ( max - min) as f64 ;
27- let normalised_volume = if self . linear_scaling {
32+ let normalised_volume = if matches ! (
33+ self . config. volume_ctrl,
34+ librespot_playback:: config:: VolumeCtrl :: Linear
35+ ) {
2836 ( ( ( volume as f64 ) / ( u16:: MAX as f64 ) ) * volume_steps) as i64 + min
2937 } else {
3038 ( ( volume as f64 + 1.0 ) . log ( ( u16:: MAX as f64 ) + 1.0 ) * volume_steps) . floor ( ) as i64 + min
@@ -33,39 +41,41 @@ impl AlsaMixer {
3341 elem. set_playback_volume_all ( normalised_volume) ?;
3442 Ok ( ( ) )
3543 }
44+
45+ fn get_volume_with_err ( & self ) -> eyre:: Result < u16 > {
46+ let lock = self . mixer . lock ( ) . expect ( "lock shouldn't be poisoned" ) ;
47+ let elem = self . get_selem ( & lock) ?;
48+ let ( min, max) = elem. get_playback_volume_range ( ) ;
49+ let volume_steps = ( max - min) as f64 ;
50+ let vol = elem. get_playback_volume ( alsa:: mixer:: SelemChannelId :: mono ( ) ) ?;
51+ let normalized_volume = if matches ! (
52+ self . config. volume_ctrl,
53+ librespot_playback:: config:: VolumeCtrl :: Linear
54+ ) {
55+ ( ( vol - min) as f64 * u16:: MAX as f64 / volume_steps) . floor ( ) as u16
56+ } else {
57+ ( ( u16:: MAX as f64 + 1.0 ) . powf ( ( ( vol - min) as f64 ) / volume_steps) - 1.0 ) . floor ( ) as u16
58+ } ;
59+ Ok ( normalized_volume)
60+ }
3661}
3762
3863impl Mixer for AlsaMixer {
39- fn open ( _: MixerConfig ) -> AlsaMixer {
40- AlsaMixer {
41- device : "default" . to_string ( ) ,
42- mixer : "Master" . to_string ( ) ,
43- linear_scaling : false ,
44- }
64+ fn open ( config : MixerConfig ) -> Result < AlsaMixer , librespot_core:: Error > {
65+ let mixer = alsa:: Mixer :: new ( & config. device , false )
66+ . wrap_err ( "failed to open mixer" )
67+ . map_err ( librespot_core:: Error :: invalid_argument) ?;
68+ Ok ( AlsaMixer {
69+ mixer : Arc :: new ( Mutex :: new ( mixer) ) ,
70+ config,
71+ } )
4572 }
4673
4774 fn volume ( & self ) -> u16 {
48- let selem_id = alsa:: mixer:: SelemId :: new ( & self . mixer , 0 ) ;
49- let mixer = alsa:: mixer:: Mixer :: new ( & self . device , false ) . ok ( ) ;
50- let vol = mixer
51- . as_ref ( )
52- . and_then ( |mixer| mixer. find_selem ( & selem_id) )
53- . and_then ( |elem| {
54- let ( min, max) = elem. get_playback_volume_range ( ) ;
55- elem. get_playback_volume ( alsa:: mixer:: SelemChannelId :: mono ( ) )
56- . ok ( )
57- . map ( |volume| {
58- ( ( ( volume - min) as f64 / ( max - min) as f64 ) * ( u16:: MAX as f64 ) ) . floor ( )
59- as u16
60- } )
61- } ) ;
62- match vol {
63- Some ( vol) => vol,
64- None => {
65- error ! (
66- "Couldn't read volume from alsa device with name \" {}\" ." ,
67- self . device
68- ) ;
75+ match self . get_volume_with_err ( ) {
76+ Ok ( vol) => vol,
77+ Err ( err) => {
78+ error ! ( "failed to get volume from alsa device: {err:?}" ) ;
6979 0
7080 }
7181 }
@@ -74,7 +84,7 @@ impl Mixer for AlsaMixer {
7484 fn set_volume ( & self , volume : u16 ) {
7585 match self . set_volume_with_err ( volume) {
7686 Ok ( _) => ( ) ,
77- Err ( e ) => error ! ( "Couldn't set volume: {:?}" , e ) ,
87+ Err ( err ) => error ! ( "Couldn't set volume of alsa device : {err :?}" ) ,
7888 }
7989 }
8090}
0 commit comments