2016-05-19 14:41:06 +00:00
# include "lemonbuddy.hpp"
# include "bar.hpp"
# include "utils/math.hpp"
2016-05-31 03:58:58 +00:00
# include "utils/macros.hpp"
2016-05-19 14:41:06 +00:00
# include "modules/volume.hpp"
using namespace modules ;
2016-05-31 03:58:58 +00:00
VolumeModule : : VolumeModule ( const std : : string & name_ ) : EventModule ( name_ )
2016-05-19 14:41:06 +00:00
{
2016-06-09 23:09:54 +00:00
// Load configuration values {{{
auto master_mixer = config : : get < std : : string > ( name ( ) , " master_mixer " , " Master " ) ;
2016-05-19 14:41:06 +00:00
auto speaker_mixer = config : : get < std : : string > ( name ( ) , " speaker_mixer " , " " ) ;
auto headphone_mixer = config : : get < std : : string > ( name ( ) , " headphone_mixer " , " " ) ;
this - > headphone_ctrl_numid = config : : get < int > ( name ( ) , " headphone_control_numid " , - 1 ) ;
if ( ! headphone_mixer . empty ( ) & & this - > headphone_ctrl_numid = = - 1 )
throw ModuleError ( " [VolumeModule] Missing required property value for \" headphone_control_numid \" ... " ) ;
2016-06-09 23:09:54 +00:00
else if ( headphone_mixer . empty ( ) & & this - > headphone_ctrl_numid ! = - 1 )
2016-05-19 14:41:06 +00:00
throw ModuleError ( " [VolumeModule] Missing required property value for \" headphone_mixer \" ... " ) ;
if ( string : : lower ( speaker_mixer ) = = " master " )
throw ModuleError ( " [VolumeModule] The \" Master \" mixer is already processed internally. Specify another mixer or comment out the \" speaker_mixer \" parameter... " ) ;
if ( string : : lower ( headphone_mixer ) = = " master " )
throw ModuleError ( " [VolumeModule] The \" Master \" mixer is already processed internally. Specify another mixer or comment out the \" headphone_mixer \" parameter... " ) ;
2016-06-09 23:09:54 +00:00
// }}}
2016-05-19 14:41:06 +00:00
2016-06-09 23:09:54 +00:00
// Setup mixers {{{
2016-05-19 14:41:06 +00:00
auto create_mixer = [ ] ( std : : string mixer_name )
{
std : : unique_ptr < alsa : : Mixer > mixer ;
try {
mixer = std : : make_unique < alsa : : Mixer > ( mixer_name ) ;
} catch ( alsa : : MixerError & e ) {
2016-05-31 03:58:58 +00:00
log_error ( " Failed to open \" " + mixer_name + " \" mixer => " + ToStr ( e . what ( ) ) ) ;
2016-05-19 14:41:06 +00:00
mixer . reset ( ) ;
}
return mixer ;
} ;
2016-06-09 23:09:54 +00:00
this - > master_mixer = create_mixer ( master_mixer ) ;
2016-05-19 14:41:06 +00:00
if ( ! speaker_mixer . empty ( ) )
this - > speaker_mixer = create_mixer ( speaker_mixer ) ;
if ( ! headphone_mixer . empty ( ) )
this - > headphone_mixer = create_mixer ( headphone_mixer ) ;
if ( ! this - > master_mixer & & ! this - > speaker_mixer & & ! this - > headphone_mixer ) {
this - > stop ( ) ;
return ;
}
if ( this - > headphone_mixer & & this - > headphone_ctrl_numid > - 1 ) {
try {
this - > headphone_ctrl = std : : make_unique < alsa : : ControlInterface > ( this - > headphone_ctrl_numid ) ;
} catch ( alsa : : ControlInterfaceError & e ) {
2016-05-31 03:58:58 +00:00
log_error ( " Failed to open headphone control interface => " + ToStr ( e . what ( ) ) ) ;
2016-05-19 14:41:06 +00:00
this - > headphone_ctrl . reset ( ) ;
}
}
2016-06-09 23:09:54 +00:00
// }}}
2016-05-19 14:41:06 +00:00
2016-06-09 23:09:54 +00:00
// Add formats and elements {{{
2016-05-19 14:41:06 +00:00
this - > formatter - > add ( FORMAT_VOLUME , TAG_LABEL_VOLUME ,
{ TAG_RAMP_VOLUME , TAG_LABEL_VOLUME , TAG_BAR_VOLUME } ) ;
this - > formatter - > add ( FORMAT_MUTED , TAG_LABEL_MUTED ,
{ TAG_RAMP_VOLUME , TAG_LABEL_MUTED , TAG_BAR_VOLUME } ) ;
if ( this - > formatter - > has ( TAG_BAR_VOLUME ) )
this - > bar_volume = drawtypes : : get_config_bar ( name ( ) , get_tag_name ( TAG_BAR_VOLUME ) ) ;
if ( this - > formatter - > has ( TAG_RAMP_VOLUME ) )
this - > ramp_volume = drawtypes : : get_config_ramp ( name ( ) , get_tag_name ( TAG_RAMP_VOLUME ) ) ;
2016-06-01 14:26:44 +00:00
if ( this - > formatter - > has ( TAG_LABEL_VOLUME , FORMAT_VOLUME ) ) {
2016-05-19 14:41:06 +00:00
this - > label_volume = drawtypes : : get_optional_config_label ( name ( ) , get_tag_name ( TAG_LABEL_VOLUME ) , " %percentage% " ) ;
2016-06-01 14:26:44 +00:00
this - > label_volume_tokenized = this - > label_volume - > clone ( ) ;
}
if ( this - > formatter - > has ( TAG_LABEL_MUTED , FORMAT_MUTED ) ) {
2016-05-19 14:41:06 +00:00
this - > label_muted = drawtypes : : get_optional_config_label ( name ( ) , get_tag_name ( TAG_LABEL_MUTED ) , " %percentage% " ) ;
2016-06-01 14:26:44 +00:00
this - > label_muted_tokenized = this - > label_muted - > clone ( ) ;
}
2016-06-09 23:09:54 +00:00
// }}}
2016-05-19 14:41:06 +00:00
2016-06-09 23:09:54 +00:00
// Sign up for stdin events {{{
2016-05-19 14:41:06 +00:00
register_command_handler ( name ( ) ) ;
2016-06-09 23:09:54 +00:00
// }}}
2016-05-19 14:41:06 +00:00
}
VolumeModule : : ~ VolumeModule ( )
{
std : : lock_guard < concurrency : : SpinLock > lck ( this - > update_lock ) ;
this - > master_mixer . reset ( ) ;
this - > speaker_mixer . reset ( ) ;
this - > headphone_mixer . reset ( ) ;
this - > headphone_ctrl . reset ( ) ;
}
bool VolumeModule : : has_event ( )
{
bool has_event = false ;
2016-06-09 23:09:54 +00:00
if ( this - > has_changed ( ) )
has_event = true ;
2016-05-19 14:41:06 +00:00
try {
2016-06-09 23:09:54 +00:00
if ( ! has_event & & this - > master_mixer )
2016-05-19 14:41:06 +00:00
has_event | = this - > master_mixer - > wait ( 25 ) ;
2016-06-09 23:09:54 +00:00
if ( ! has_event & & this - > speaker_mixer )
2016-05-19 14:41:06 +00:00
has_event | = this - > speaker_mixer - > wait ( 25 ) ;
2016-06-09 23:09:54 +00:00
if ( ! has_event & & this - > headphone_mixer )
2016-05-19 14:41:06 +00:00
has_event | = this - > headphone_mixer - > wait ( 25 ) ;
2016-06-09 23:09:54 +00:00
if ( ! has_event & & this - > headphone_ctrl )
2016-05-19 14:41:06 +00:00
has_event | = this - > headphone_ctrl - > wait ( 25 ) ;
} catch ( alsa : : Exception & e ) {
log_error ( e . what ( ) ) ;
}
return has_event ;
}
bool VolumeModule : : update ( )
{
2016-06-09 23:09:54 +00:00
// Consume any other pending events
this - > has_changed = false ;
if ( this - > master_mixer )
this - > master_mixer - > process_events ( ) ;
if ( this - > speaker_mixer )
this - > speaker_mixer - > process_events ( ) ;
if ( this - > headphone_mixer )
this - > headphone_mixer - > process_events ( ) ;
if ( this - > headphone_ctrl )
this - > headphone_ctrl - > wait ( 0 ) ;
int volume = 100 ;
2016-05-19 14:41:06 +00:00
bool muted = false ;
if ( this - > master_mixer ) {
2016-06-09 23:09:54 +00:00
volume * = this - > master_mixer - > get_volume ( ) / 100.0f ;
2016-05-19 14:41:06 +00:00
muted | = this - > master_mixer - > is_muted ( ) ;
}
2016-06-09 23:09:54 +00:00
if ( this - > headphone_mixer & & this - > headphone_ctrl & & this - > headphone_ctrl - > test_device_plugged ( ) ) {
2016-05-19 14:41:06 +00:00
volume * = this - > headphone_mixer - > get_volume ( ) / 100.0f ;
muted | = this - > headphone_mixer - > is_muted ( ) ;
} else if ( this - > speaker_mixer ) {
volume * = this - > speaker_mixer - > get_volume ( ) / 100.0f ;
muted | = this - > speaker_mixer - > is_muted ( ) ;
}
this - > volume = volume ;
this - > muted = muted ;
this - > label_volume_tokenized - > text = this - > label_volume - > text ;
2016-06-09 23:09:54 +00:00
this - > label_volume_tokenized - > replace_token ( " %percentage% " , std : : to_string ( this - > volume ( ) ) + " % " ) ;
2016-05-19 14:41:06 +00:00
this - > label_muted_tokenized - > text = this - > label_muted - > text ;
2016-06-09 23:09:54 +00:00
this - > label_muted_tokenized - > replace_token ( " %percentage% " , std : : to_string ( this - > volume ( ) ) + " % " ) ;
2016-05-19 14:41:06 +00:00
return true ;
}
2016-06-09 23:09:54 +00:00
std : : string VolumeModule : : get_format ( )
{
return this - > muted ( ) = = true ? FORMAT_MUTED : FORMAT_VOLUME ;
2016-05-19 14:41:06 +00:00
}
std : : string VolumeModule : : get_output ( )
{
this - > builder - > cmd ( Cmd : : LEFT_CLICK , EVENT_TOGGLE_MUTE ) ;
2016-06-09 23:09:54 +00:00
if ( ! this - > muted ( ) ) {
if ( this - > volume ( ) < 100 )
this - > builder - > cmd ( Cmd : : SCROLL_UP , EVENT_VOLUME_UP ) ;
if ( this - > volume ( ) > 0 )
this - > builder - > cmd ( Cmd : : SCROLL_DOWN , EVENT_VOLUME_DOWN ) ;
}
2016-05-19 14:41:06 +00:00
this - > builder - > node ( this - > Module : : get_output ( ) ) ;
return this - > builder - > flush ( ) ;
}
bool VolumeModule : : build ( Builder * builder , const std : : string & tag )
{
if ( tag = = TAG_BAR_VOLUME )
builder - > node ( this - > bar_volume , volume ) ;
else if ( tag = = TAG_RAMP_VOLUME )
builder - > node ( this - > ramp_volume , volume ) ;
else if ( tag = = TAG_LABEL_VOLUME )
builder - > node ( this - > label_volume_tokenized ) ;
else if ( tag = = TAG_LABEL_MUTED )
builder - > node ( this - > label_muted_tokenized ) ;
else
2016-06-09 23:09:54 +00:00
return false ;
2016-05-19 14:41:06 +00:00
2016-06-09 23:09:54 +00:00
return true ;
2016-05-19 14:41:06 +00:00
}
bool VolumeModule : : handle_command ( const std : : string & cmd )
{
2016-06-09 23:09:54 +00:00
if ( cmd . length ( ) < std : : strlen ( EVENT_PREFIX ) )
return false ;
if ( std : : strncmp ( cmd . c_str ( ) , EVENT_PREFIX , 3 ) ! = 0 )
2016-05-19 14:41:06 +00:00
return false ;
2016-06-09 23:09:54 +00:00
std : : lock_guard < concurrency : : SpinLock > lck ( this - > update_lock ) ;
2016-05-19 14:41:06 +00:00
alsa : : Mixer * master_mixer = nullptr ;
alsa : : Mixer * other_mixer = nullptr ;
2016-06-09 23:09:54 +00:00
if ( this - > master_mixer )
master_mixer = this - > master_mixer . get ( ) ;
2016-05-19 14:41:06 +00:00
2016-06-09 23:09:54 +00:00
if ( master_mixer = = nullptr )
return false ;
2016-05-19 14:41:06 +00:00
2016-06-09 23:09:54 +00:00
if ( this - > headphone_mixer & & this - > headphone_ctrl & & this - > headphone_ctrl - > test_device_plugged ( ) )
2016-05-19 14:41:06 +00:00
other_mixer = this - > headphone_mixer . get ( ) ;
else if ( this - > speaker_mixer )
other_mixer = this - > speaker_mixer . get ( ) ;
2016-06-09 23:09:54 +00:00
// Toggle mute state
if ( std : : strncmp ( cmd . c_str ( ) , EVENT_TOGGLE_MUTE , std : : strlen ( EVENT_TOGGLE_MUTE ) ) = = 0 ) {
master_mixer - > set_mute ( this - > muted ( ) ) ;
if ( other_mixer ! = nullptr )
other_mixer - > set_mute ( this - > muted ( ) ) ;
// Increase volume
} else if ( std : : strncmp ( cmd . c_str ( ) , EVENT_VOLUME_UP , std : : strlen ( EVENT_VOLUME_UP ) ) = = 0 ) {
master_mixer - > set_volume ( math : : cap < float > ( master_mixer - > get_volume ( ) + 5 , 0 , 100 ) ) ;
if ( other_mixer ! = nullptr )
other_mixer - > set_volume ( math : : cap < float > ( other_mixer - > get_volume ( ) + 5 , 0 , 100 ) ) ;
// Decrease volume
} else if ( std : : strncmp ( cmd . c_str ( ) , EVENT_VOLUME_DOWN , std : : strlen ( EVENT_VOLUME_DOWN ) ) = = 0 ) {
master_mixer - > set_volume ( math : : cap < float > ( master_mixer - > get_volume ( ) - 5 , 0 , 100 ) ) ;
if ( other_mixer ! = nullptr )
other_mixer - > set_volume ( math : : cap < float > ( other_mixer - > get_volume ( ) - 5 , 0 , 100 ) ) ;
2016-05-19 14:41:06 +00:00
} else {
return false ;
}
2016-06-09 23:09:54 +00:00
this - > has_changed = true ;
2016-05-19 14:41:06 +00:00
return true ;
}