From: Mike Brady Date: Thu, 31 Jan 2019 09:01:35 +0000 (+0000) Subject: Rebuild the hardware mute code -- clear up the logic. Needs to check if disconnected... X-Git-Tag: 3.3RC0~66^2~30 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=555bb97a39757025ae2ce1e696a3ad1500d45cd9;p=thirdparty%2Fshairport-sync.git Rebuild the hardware mute code -- clear up the logic. Needs to check if disconnected though. --- diff --git a/audio_alsa.c b/audio_alsa.c index ca636708..9be90890 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -51,7 +51,6 @@ static int play(void *buf, int samples); static void stop(void); static void flush(void); int delay(long *the_delay); -void do_mute(int request); int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played); void *alsa_buffer_monitor_thread_code(void *arg); @@ -61,7 +60,7 @@ void do_volume(double vol); static void parameters(audio_parameters *info); int mute(int do_mute); // returns true if it actually is allowed to use the mute static double set_volume; -static int output_method_signalled = 0; +static int output_method_signalled = 0; // for reporting whether it's using mmap or not audio_output audio_alsa = { .name = "alsa", @@ -75,25 +74,40 @@ audio_output audio_alsa = { .delay = &delay, .play = &play, .rate_info = &get_rate_information, - .mute = NULL, // a function will be provided if it can, and is allowed to, do hardware mute - .volume = NULL, // a function will be provided if it can do hardware volume + .mute = NULL, // a function will be provided if it can, and is allowed to, + // do hardware mute + .volume = NULL, // a function will be provided if it can do hardware volume .parameters = NULL}; // a function will be provided if it can do hardware volume static pthread_mutex_t alsa_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_t alsa_buffer_monitor_thread; +// for deciding when to activate mute +// there are two sources of requests to mute -- the backend itself, e.g. when it +// is flushing +// and the player, e.g. when volume goes down to -144, i.e. mute. + +// we may not be allowed to use hardware mute, so we must reflect that too. + +int mute_requested_externally = 0; +int mute_requested_internally = 0; + // for tracking how long the output device has stalled -uint64_t stall_monitor_start_time; // zero if not initialised / not started / zeroed by flush -long stall_monitor_frame_count; // set to delay at start of time, incremented by any writes -uint64_t stall_monitor_error_threshold; // if the time is longer than this, it's an error +uint64_t stall_monitor_start_time; // zero if not initialised / not started / + // zeroed by flush +long stall_monitor_frame_count; // set to delay at start of time, incremented by + // any writes +uint64_t stall_monitor_error_threshold; // if the time is longer than this, it's + // an error static snd_output_t *output = NULL; static unsigned int desired_sample_rate; static enum sps_format_t sample_format; int frame_size; // in bytes for interleaved stereo -int alsa_device_initialised; // boolean to ensure the initialisation is only done once +int alsa_device_initialised; // boolean to ensure the initialisation is only + // done once snd_pcm_t *alsa_handle = NULL; static snd_pcm_hw_params_t *alsa_params = NULL; static snd_pcm_sw_params_t *alsa_swparams = NULL; @@ -114,13 +128,13 @@ static int has_softvol = 0; int64_t dither_random_number_store = 0; -static int volume_set_request = 0; // set when an external request is made to set the volume. -int mute_request_pending = 0; // set when an external request is made to mute or unmute. -int overriding_mute_state_requested = 0; // 1 = mute; 0 = unmute requested -int mixer_volume_setting_gives_mute = - 0; // set when it is discovered that particular mixer volume setting causes a mute. -long alsa_mix_mute; // setting the volume to this value mutes output, if - // mixer_volume_setting_gives_mute is true +static int volume_set_request = 0; // set when an external request is made to set the volume. + +int mixer_volume_setting_gives_mute = 0; // set when it is discovered that + // particular mixer volume setting + // causes a mute. +long alsa_mix_mute; // setting the volume to this value mutes output, if + // mixer_volume_setting_gives_mute is true int volume_based_mute_is_active = 0; // set when muting is being done by a setting the volume to a magic value @@ -223,8 +237,9 @@ void actual_close_alsa_device() { if (alsa_handle) { int derr; if ((derr = snd_pcm_hw_free(alsa_handle))) - debug(1, "Error %d (\"%s\") freeing the output device hardware while closing it.", derr, - snd_strerror(derr)); + debug(1, "Error %d (\"%s\") freeing the output device hardware while " + "closing it.", + derr, snd_strerror(derr)); if ((derr = snd_pcm_close(alsa_handle))) debug(1, "Error %d (\"%s\") closing the output device.", derr, snd_strerror(derr)); @@ -241,7 +256,8 @@ int actual_open_alsa_device(void) { /* const snd_pcm_uframes_t requested_buffer_headroom = minimal_buffer_headroom + 2048; // we ask for this much headroom in the - // hardware buffer, but we'll accept less + // hardware buffer, but we'll accept + less */ int ret, dir = 0; @@ -250,17 +266,20 @@ int actual_open_alsa_device(void) { snd_pcm_uframes_t actual_buffer_length; snd_pcm_access_t access; - // ensure no calls are made to the alsa device enquiring about the buffer length if + // ensure no calls are made to the alsa device enquiring about the buffer + // length if // synchronisation is disabled. if (config.no_sync != 0) audio_alsa.delay = NULL; - // ensure no calls are made to the alsa device enquiring about the buffer length if + // ensure no calls are made to the alsa device enquiring about the buffer + // length if // synchronisation is disabled. if (config.no_sync != 0) audio_alsa.delay = NULL; - // ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + // ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, + // SND_PCM_NONBLOCK); ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0); if (ret < 0) return ret; @@ -369,7 +388,8 @@ int actual_open_alsa_device(void) { snd_pcm_uframes_t actual_period_size; snd_pcm_hw_params_get_period_size(alsa_params, &actual_period_size, &dir); if (actual_period_size != period_size_requested) - inform("Actual period size set to a different value than requested. Requested: %lu, actual " + inform("Actual period size set to a different value than requested. " + "Requested: %lu, actual " "setting: %lu", period_size_requested, actual_period_size); } @@ -386,7 +406,8 @@ int actual_open_alsa_device(void) { snd_pcm_uframes_t actual_buffer_size; snd_pcm_hw_params_get_buffer_size(alsa_params, &actual_buffer_size); if (actual_buffer_size != buffer_size_requested) - inform("Actual period size set to a different value than requested. Requested: %lu, actual " + inform("Actual period size set to a different value than requested. " + "Requested: %lu, actual " "setting: %lu", buffer_size_requested, actual_buffer_size); } @@ -405,7 +426,8 @@ int actual_open_alsa_device(void) { snd_pcm_uframes_t actual_period_size; snd_pcm_hw_params_get_period_size(alsa_params, &actual_period_size, &dir); if (actual_period_size != period_size_requested) - inform("Actual period size set to a different value than requested. Requested: %lu, actual " + inform("Actual period size set to a different value than requested. " + "Requested: %lu, actual " "setting: %lu", period_size_requested, actual_period_size); } @@ -414,7 +436,8 @@ int actual_open_alsa_device(void) { snd_pcm_uframes_t actual_buffer_size; snd_pcm_hw_params_get_buffer_size(alsa_params, &actual_buffer_size); if (actual_buffer_size != buffer_size_requested) - inform("Actual period size set to a different value than requested. Requested: %lu, actual " + inform("Actual period size set to a different value than requested. " + "Requested: %lu, actual " "setting: %lu", buffer_size_requested, actual_buffer_size); } @@ -433,8 +456,9 @@ int actual_open_alsa_device(void) { ret = snd_pcm_sw_params_current(alsa_handle, alsa_swparams); if (ret < 0) { - warn("audio_alsa: Unable to get current sw parameters for device \"%s\": %s.", alsa_out_dev, - snd_strerror(ret)); + warn("audio_alsa: Unable to get current sw parameters for device \"%s\": " + "%s.", + alsa_out_dev, snd_strerror(ret)); return ret; } @@ -476,9 +500,9 @@ int actual_open_alsa_device(void) { buffer_size); } */ - debug(1, - "The alsa buffer is smaller (%lu bytes) than the desired backend buffer " - "length (%ld) you have chosen.", + debug(1, "The alsa buffer is smaller (%lu bytes) than the desired backend " + "buffer " + "length (%ld) you have chosen.", actual_buffer_length, config.audio_backend_buffer_desired_length); } @@ -499,7 +523,10 @@ int actual_open_alsa_device(void) { // ret = snd_pcm_hw_params_any(alsa_handle, alsa_params); // if (ret < 0) { - // die("audio_alsa: Cannpot get configuration for device \"%s\": no + // die("audio_alsa: Cannpot get configuration for + // device + //\"%s\": + // no // configurations //" // "available", @@ -622,105 +649,116 @@ int open_alsa_device(void) { } int do_alsa_device_init_if_needed() { - debug(1,"do_alsa_device_init_if_needed()"); + debug(1, "do_alsa_device_init_if_needed()"); int response = 0; // do any alsa device initialisation (general case) if needed // at present, this is only needed if a hardware mixer is being used // if there's a hardware mixer, it needs to be initialised before first use if (alsa_device_initialised == 0) { - alsa_device_initialised = 1; - if (hardware_mixer) { - int oldState; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable - - if (alsa_mix_dev == NULL) - alsa_mix_dev = alsa_out_dev; - - // Now, start trying to initialise the alsa device with the settings obtained - pthread_cleanup_debug_mutex_lock(&alsa_mutex, 1000, 1); - if (open_mixer() == 1) { - if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) < - 0) - debug(1, "Can't read mixer's [linear] min and max volumes."); - else { - if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb, - &alsa_mix_maxdb) == 0) { - - audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff - audio_alsa.parameters = ¶meters; // likewise the parameters stuff - if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) { - // For instance, the Raspberry Pi does this - debug(1, "Lowest dB value is a mute"); - mixer_volume_setting_gives_mute = 1; - alsa_mix_mute = SND_CTL_TLV_DB_GAIN_MUTE; // this may not be necessary -- it's always - // going to be SND_CTL_TLV_DB_GAIN_MUTE, right? - // debug(1, "Try minimum volume + 1 as lowest true attenuation value"); - if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv + 1, - &alsa_mix_mindb) != 0) - debug(1, "Can't get dB value corresponding to a minimum volume + 1."); - } - debug(1, "Hardware mixer has dB volume from %f to %f.", (1.0 * alsa_mix_mindb) / 100.0, - (1.0 * alsa_mix_maxdb) / 100.0); - } else { - // use the linear scale and do the db conversion ourselves - warn("The hardware mixer specified -- \"%s\" -- does not have " - "a dB volume scale.", - alsa_mix_ctrl); - - if (snd_ctl_open(&ctl, alsa_mix_dev, 0) < 0) { - warn("Cannot open control \"%s\"", alsa_mix_dev); - response = -1; - } - if (snd_ctl_elem_id_malloc(&elem_id) < 0) { - debug(1, "Cannot allocate memory for control \"%s\"", alsa_mix_dev); - elem_id = NULL; - response = -2; + alsa_device_initialised = 1; + if (hardware_mixer) { + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable + + if (alsa_mix_dev == NULL) + alsa_mix_dev = alsa_out_dev; + + // Now, start trying to initialise the alsa device with the settings + // obtained + pthread_cleanup_debug_mutex_lock(&alsa_mutex, 1000, 1); + if (open_mixer() == 1) { + if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, + &alsa_mix_maxv) < 0) + debug(1, "Can't read mixer's [linear] min and max volumes."); + else { + if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb, + &alsa_mix_maxdb) == 0) { + + audio_alsa.volume = &volume; // insert the volume function now we + // know it can do dB stuff + audio_alsa.parameters = ¶meters; // likewise the parameters stuff + if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) { + // For instance, the Raspberry Pi does this + debug(1, "Lowest dB value is a mute"); + mixer_volume_setting_gives_mute = 1; + alsa_mix_mute = SND_CTL_TLV_DB_GAIN_MUTE; // this may not be + // necessary -- it's + // always + // going to be SND_CTL_TLV_DB_GAIN_MUTE, right? + // debug(1, "Try minimum volume + 1 as lowest true attenuation + // value"); + if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv + 1, + &alsa_mix_mindb) != 0) + debug(1, "Can't get dB value corresponding to a minimum volume " + "+ 1."); + } + debug(1, "Hardware mixer has dB volume from %f to %f.", (1.0 * alsa_mix_mindb) / 100.0, + (1.0 * alsa_mix_maxdb) / 100.0); } else { - snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_id_set_name(elem_id, alsa_mix_ctrl); - - if (snd_ctl_get_dB_range(ctl, elem_id, &alsa_mix_mindb, &alsa_mix_maxdb) == 0) { - debug(1, "alsa: hardware mixer \"%s\" selected, with dB volume from %f to %f.", - alsa_mix_ctrl, (1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0); - has_softvol = 1; - audio_alsa.volume = - &volume; // insert the volume function now we know it can do dB stuff - audio_alsa.parameters = ¶meters; // likewise the parameters stuff + // use the linear scale and do the db conversion ourselves + warn("The hardware mixer specified -- \"%s\" -- does not have " + "a dB volume scale.", + alsa_mix_ctrl); + + if (snd_ctl_open(&ctl, alsa_mix_dev, 0) < 0) { + warn("Cannot open control \"%s\"", alsa_mix_dev); + response = -1; + } + if (snd_ctl_elem_id_malloc(&elem_id) < 0) { + debug(1, "Cannot allocate memory for control \"%s\"", alsa_mix_dev); + elem_id = NULL; + response = -2; } else { - debug(1, "Cannot get the dB range from the volume control \"%s\"", alsa_mix_ctrl); + snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER); + snd_ctl_elem_id_set_name(elem_id, alsa_mix_ctrl); + + if (snd_ctl_get_dB_range(ctl, elem_id, &alsa_mix_mindb, &alsa_mix_maxdb) == 0) { + debug(1, "alsa: hardware mixer \"%s\" selected, with dB volume " + "from %f to %f.", + alsa_mix_ctrl, (1.0 * alsa_mix_mindb) / 100.0, + (1.0 * alsa_mix_maxdb) / 100.0); + has_softvol = 1; + audio_alsa.volume = &volume; // insert the volume function now + // we know it can do dB stuff + audio_alsa.parameters = ¶meters; // likewise the parameters stuff + } else { + debug(1, "Cannot get the dB range from the volume control \"%s\"", alsa_mix_ctrl); + } } + /* + debug(1, "Min and max volumes are %d and + %d.",alsa_mix_minv,alsa_mix_maxv); + alsa_mix_maxdb = 0; + if ((alsa_mix_maxv!=0) && (alsa_mix_minv!=0)) + alsa_mix_mindb = + -20*100*(log10(alsa_mix_maxv*1.0)-log10(alsa_mix_minv*1.0)); + else if (alsa_mix_maxv!=0) + alsa_mix_mindb = -20*100*log10(alsa_mix_maxv*1.0); + audio_alsa.volume = &linear_volume; // insert the linear volume + function + audio_alsa.parameters = ¶meters; // likewise the parameters + stuff + debug(1,"Max and min dB calculated are %d and + %d.",alsa_mix_maxdb,alsa_mix_mindb); + */ } - /* - debug(1, "Min and max volumes are %d and - %d.",alsa_mix_minv,alsa_mix_maxv); - alsa_mix_maxdb = 0; - if ((alsa_mix_maxv!=0) && (alsa_mix_minv!=0)) - alsa_mix_mindb = - -20*100*(log10(alsa_mix_maxv*1.0)-log10(alsa_mix_minv*1.0)); - else if (alsa_mix_maxv!=0) - alsa_mix_mindb = -20*100*log10(alsa_mix_maxv*1.0); - audio_alsa.volume = &linear_volume; // insert the linear volume function - audio_alsa.parameters = ¶meters; // likewise the parameters stuff - debug(1,"Max and min dB calculated are %d and - %d.",alsa_mix_maxdb,alsa_mix_mindb); - */ } + if (((config.alsa_use_hardware_mute == 1) && + (snd_mixer_selem_has_playback_switch(alsa_mix_elem))) || + mixer_volume_setting_gives_mute) { + audio_alsa.mute = &mute; // insert the mute function now we know it + // can do muting stuff + // debug(1, "Has mixer and mute ability we will use."); + } else { + // debug(1, "Has mixer but not using hardware mute."); + } + close_mixer(); } - if (((config.alsa_use_hardware_mute == 1) && - (snd_mixer_selem_has_playback_switch(alsa_mix_elem))) || - mixer_volume_setting_gives_mute) { - audio_alsa.mute = &mute; // insert the mute function now we know it can do muting stuff - // debug(1, "Has mixer and mute ability we will use."); - } else { - // debug(1, "Has mixer but not using hardware mute."); - } - close_mixer(); - } - debug_mutex_unlock(&alsa_mutex, 3); // release the mutex + debug_mutex_unlock(&alsa_mutex, 3); // release the mutex - pthread_cleanup_pop(0); - pthread_setcancelstate(oldState, NULL); - } + pthread_cleanup_pop(0); + pthread_setcancelstate(oldState, NULL); + } } return response; } @@ -766,7 +804,8 @@ static int init(int argc, char **argv) { // get settings from settings file first, allow them to be overridden by // command line options - // do the "general" audio options. Note, these options are in the "general" stanza! + // do the "general" audio options. Note, these options are in the "general" + // stanza! parse_general_audio_options(); if (config.cfg != NULL) { @@ -803,7 +842,8 @@ static int init(int argc, char **argv) { else if (strcasecmp(str, "yes") == 0) config.no_sync = 1; else { - warn("Invalid disable_synchronization option choice \"%s\". It should be \"yes\" or " + warn("Invalid disable_synchronization option choice \"%s\". It should " + "be \"yes\" or " "\"no\". It is set to \"no\"."); config.no_sync = 0; } @@ -818,7 +858,8 @@ static int init(int argc, char **argv) { else if (strcasecmp(str, "yes") == 0) config.alsa_use_hardware_mute = 1; else { - warn("Invalid mute_using_playback_switch option choice \"%s\". It should be \"yes\" or " + warn("Invalid mute_using_playback_switch option choice \"%s\". It " + "should be \"yes\" or " "\"no\". It is set to \"no\"."); config.alsa_use_hardware_mute = 0; } @@ -831,7 +872,8 @@ static int init(int argc, char **argv) { else if (strcasecmp(str, "yes") == 0) config.alsa_use_hardware_mute = 1; else { - warn("Invalid use_hardware_mute_if_available option choice \"%s\". It should be \"yes\" or " + warn("Invalid use_hardware_mute_if_available option choice \"%s\". It " + "should be \"yes\" or " "\"no\". It is set to \"no\"."); config.alsa_use_hardware_mute = 0; } @@ -854,7 +896,8 @@ static int init(int argc, char **argv) { else if (strcasecmp(str, "S8") == 0) config.output_format = SPS_FORMAT_S8; else { - warn("Invalid output format \"%s\". It should be \"U8\", \"S8\", \"S16\", \"S24\", " + warn("Invalid output format \"%s\". It should be \"U8\", \"S8\", " + "\"S16\", \"S24\", " "\"S24_3LE\", \"S24_3BE\" or " "\"S32\". It is set to \"S16\".", str); @@ -873,7 +916,8 @@ static int init(int argc, char **argv) { config.output_rate = value; break; default: - warn("Invalid output rate \"%d\". It should be a multiple of 44,100 up to 352,800. It is " + warn("Invalid output rate \"%d\". It should be a multiple of 44,100 up " + "to 352,800. It is " "set to 44,100", value); config.output_rate = 44100; @@ -887,7 +931,8 @@ static int init(int argc, char **argv) { else if (strcasecmp(str, "yes") == 0) config.no_mmap = 0; else { - warn("Invalid use_mmap_if_available option choice \"%s\". It should be \"yes\" or \"no\". " + warn("Invalid use_mmap_if_available option choice \"%s\". It should be " + "\"yes\" or \"no\". " "It is set to \"yes\"."); config.no_mmap = 0; } @@ -977,10 +1022,11 @@ static int init(int argc, char **argv) { } debug(1, "alsa: output device name is \"%s\".", alsa_out_dev); - + alsa_mix_handle = NULL; // indicates alsa handle isn't open - // so, now, if the option to keep the DAC running has been selected, start a thread to monitor the + // so, now, if the option to keep the DAC running has been selected, start a + // thread to monitor the // length of the queue // if the queue gets too short, stuff it with silence @@ -1005,8 +1051,39 @@ static void deinit(void) { pthread_setcancelstate(oldState, NULL); } +int set_mute_state() { + debug(1, "set_mute_state"); + int response = 1; // some problem expected, e.g. no mixer or not allowed to use it + if (config.alsa_use_hardware_mute == 1) { + int mute = 0; + if ((mute_requested_externally != 0) || (mute_requested_internally != 0)) + mute = 1; + if (open_mixer() == 1) { + response = 0; // okay if actually using the mute facility + if (mute == 1) { + debug(1, "Hardware mute switched on"); + if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) + snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 0); + else { + volume_based_mute_is_active = 1; + do_snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, alsa_mix_mute); + } + } else { + debug(1, "Hardware mute switched off"); + if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) + snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 1); + else { + volume_based_mute_is_active = 0; + do_snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, set_volume); + } + } + close_mixer(); + } + } + return response; +} static void start(int i_sample_rate, int i_sample_format) { - debug(1,"audio_alsa start called."); + debug(1, "audio_alsa start called."); if (i_sample_rate == 0) desired_sample_rate = 44100; // default else @@ -1023,7 +1100,7 @@ static void start(int i_sample_rate, int i_sample_format) { stall_monitor_start_time = 0; stall_monitor_frame_count = 0; if (alsa_device_initialised == 0) { - debug(1,"alsa: start() calling do_alsa_device_init_if_needed."); + debug(1, "alsa: start() calling do_alsa_device_init_if_needed."); do_alsa_device_init_if_needed(); } } @@ -1065,10 +1142,9 @@ int delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay) { if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) || ((time_now_ns - stall_monitor_start_time) > stall_monitor_error_threshold)) { - debug(2, - "DAC seems to have stalled with time_now_ns: %" PRIX64 - ", update_timestamp_ns: %" PRIX64 ", stall_monitor_start_time %" PRIX64 - ", stall_monitor_error_threshold %" PRIX64 ".", + debug(2, "DAC seems to have stalled with time_now_ns: %" PRIX64 + ", update_timestamp_ns: %" PRIX64 ", stall_monitor_start_time %" PRIX64 + ", stall_monitor_error_threshold %" PRIX64 ".", time_now_ns, update_timestamp_ns, stall_monitor_start_time, stall_monitor_error_threshold); ret = sps_extra_code_output_stalled; @@ -1088,12 +1164,14 @@ int delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay) { *delay = *delay - frames_played_since_last_interrupt_sized; } - } else { // not running, thus no delay information, thus can't check for stall + } else { // not running, thus no delay information, thus can't check for + // stall *delay = 0; stall_monitor_start_time = 0; // zero if not initialised / not started / zeroed by flush stall_monitor_frame_count = 0; // set to delay at start of time, incremented by any writes - // not running, thus no delay information, thus can't check for frame rates + // not running, thus no delay information, thus can't check for frame + // rates frame_index = 0; // we'll be starting over... measurement_data_is_valid = 0; } @@ -1104,13 +1182,15 @@ int delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay) { } int delay(long *the_delay) { - // returns 0 if the device is in a valid state -- SND_PCM_STATE_RUNNING or SND_PCM_STATE_PREPARED + // returns 0 if the device is in a valid state -- SND_PCM_STATE_RUNNING or + // SND_PCM_STATE_PREPARED // or SND_PCM_STATE_DRAINING // and returns the actual delay if running or 0 if prepared in *the_delay // otherwise return an error code // the error code could be a Unix errno code or a snderror code, or - // the sps_extra_code_output_stalled or the sps_extra_code_output_state_cannot_make_ready codes + // the sps_extra_code_output_stalled or the + // sps_extra_code_output_state_cannot_make_ready codes int ret = 0; *the_delay = 0; if (alsa_handle == NULL) @@ -1212,7 +1292,8 @@ int do_play(void *buf, int samples) { while ((ret = snd_pcm_resume(alsa_handle)) == -EAGAIN) { sleep(1); /* wait until the suspend flag is released */ if (ret < 0) { - warn("alsa: can't recover from SND_PCM_STATE_SUSPENDED state, snd_pcm_prepare() " + warn("alsa: can't recover from SND_PCM_STATE_SUSPENDED state, " + "snd_pcm_prepare() " "failed: %s.", snd_strerror(ret)); } @@ -1221,8 +1302,9 @@ int do_play(void *buf, int samples) { } } } else { - debug(1, "alsa: device status returns fault status %d and SND_PCM_STATE_* %d for play.", ret, - state); + debug(1, "alsa: device status returns fault status %d and SND_PCM_STATE_* " + "%d for play.", + ret, state); frame_index = 0; measurement_data_is_valid = 0; } @@ -1234,15 +1316,21 @@ int do_play(void *buf, int samples) { int do_open() { int ret = 0; if (alsa_backend_state != abm_disconnected) - debug(1, "alsa: do_open() -- opening the output device when it is already connected"); + debug(1, "alsa: do_open() -- opening the output device when it is already " + "connected"); if (alsa_handle == NULL) { // debug(1,"alsa: do_open() -- opening the output device"); ret = open_alsa_device(); if (ret == 0) { + mute_requested_internally = 0; if (audio_alsa.volume) do_volume(set_volume); - if (audio_alsa.mute) - do_mute(0); + if (audio_alsa.mute) { + debug(1, "do_open() set_mute_state"); + set_mute_state(); // the mute_requested_externally flag will have been + // set accordingly + // do_mute(0); // complete unmute + } alsa_backend_state = abm_connected; // only do this if it really opened it. } } else { @@ -1253,7 +1341,8 @@ int do_open() { int do_close() { if (alsa_backend_state == abm_disconnected) - debug(1, "alsa: do_close() -- closing the output device when it is already disconnected"); + debug(1, "alsa: do_close() -- closing the output device when it is already " + "disconnected"); int derr = 0; if (alsa_handle) { // debug(1,"alsa: do_close() -- closing the output device"); @@ -1277,7 +1366,8 @@ int do_close() { int play(void *buf, int samples) { // play() will change the state of the alsa_backend_mode to abm_playing - // also, if the present alsa_backend_state is abm_disconnected, then first the DAC must be + // also, if the present alsa_backend_state is abm_disconnected, then first the + // DAC must be // connected // debug(3,"audio_alsa play called."); @@ -1289,13 +1379,17 @@ int play(void *buf, int samples) { ret = do_open(); if (ret == 0) debug(2, "alsa: play() -- opened output device"); - } + } if (ret == 0) { if (alsa_backend_state != abm_playing) { debug(2, "alsa: play() -- alsa_backend_state => abm_playing"); alsa_backend_state = abm_playing; - do_mute(0); + mute_requested_internally = 0; // stop requesting a mute for backend's own + // reasons, which might have been a flush + debug(1, "play() set_mute_state"); + set_mute_state(); // try to action the request and return a status + // do_mute(0); // unmute for backend's reason } ret = do_play(buf, samples); } @@ -1308,7 +1402,10 @@ int play(void *buf, int samples) { static void flush(void) { // debug(2,"audio_alsa flush called."); pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1); - do_mute(1); + mute_requested_internally = 1; // request a mute for backend's reasons + debug(1, "flush() set_mute_state"); + set_mute_state(); + // do_mute(1); // mute for backend's own reasons if (alsa_backend_state != abm_disconnected) { // must be playing or connected... if (config.keep_dac_busy != 0) { debug(2, "alsa: flush() -- alsa_backend_state => abm_connected."); @@ -1334,7 +1431,8 @@ static void parameters(audio_parameters *info) { info->maximum_volume_dB = alsa_mix_maxdb; } -void do_volume(double vol) { // caller is assumed to have the alsa_mutex when using this function +void do_volume(double vol) { // caller is assumed to have the alsa_mutex when + // using this function debug(3, "Setting volume db to %f.", vol); int oldState; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable @@ -1386,7 +1484,8 @@ static void linear_volume(double vol) { if (hardware_mixer && alsa_mix_handle) { double linear_volume = pow(10, vol); // debug(1,"Linear volume is %f.",linear_volume); - long int_vol = alsa_mix_minv + (alsa_mix_maxv - alsa_mix_minv) * linear_volume; + long int_vol = alsa_mix_minv + (alsa_mix_maxv - alsa_mix_minv) * +linear_volume; // debug(1,"Setting volume to %ld, for volume input of %f.",int_vol,vol); if (alsa_mix_handle) { if (snd_mixer_selem_set_playback_volume_all(alsa_mix_elem, int_vol) != 0) @@ -1397,71 +1496,17 @@ static void linear_volume(double vol) { } */ -int mute(int mute_state_requested) { - debug(1,"alsa: mute(%d)",mute_state_requested); - int response = 0; - if (config.alsa_use_hardware_mute == 1) - response = 1; // return true if actually using the mute facility - // debug(1,"External Mute Request: %d",mute_state_requested); - pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1); - mute_request_pending = 1; - overriding_mute_state_requested = mute_state_requested; - do_mute(mute_state_requested); - debug_mutex_unlock(&alsa_mutex, 3); - pthread_cleanup_pop(0); // release the mutex - return response; +int mute(int mute_state_requested) { // these would be for external reasons, not + // because of the + // state of the backend. + debug(1, "alsa: mute(%d)", mute_state_requested); + mute_requested_externally = mute_state_requested; // request a mute for external reasons + debug(1, "mute(%d) set_mute_state", mute_state_requested); + return set_mute_state(); } - -void do_mute(int mute_state_requested) { - debug(1, "alsa: do_mute(%d).", mute_state_requested); - int oldState; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable - - // if a mute is requested now, then - // if an external mute request is in place, leave everything muted - // otherwise, if an external mute request is pending, action it - // otherwise, action the do_mute request - - int local_mute_state_requested = - overriding_mute_state_requested; // go with whatever was asked by the external "mute" call - - // The mute state requested will be actioned unless mute_request_pending is set - // If it is set, then that the pending request will be actioned. - // If the hardware isn't there, or we are not allowed to use it, nothing will be done - // The caller must have the alsa mutex - - if (config.alsa_use_hardware_mute == 1) { - if (mute_request_pending == 0) - local_mute_state_requested = mute_state_requested; - if (open_mixer() == 1) { - if (local_mute_state_requested) { - // debug(1,"Playback Switch mute actually done"); - if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) - snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 0); - else { - // debug(1,"Activating volume-based mute."); - volume_based_mute_is_active = 1; - do_snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, alsa_mix_mute); - } - } else if (overriding_mute_state_requested == 0) { - // debug(1,"Playback Switch unmute actually done"); - if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) - snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 1); - else { - // debug(1,"Deactivating volume-based mute."); - volume_based_mute_is_active = 0; - do_snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, set_volume); - } - } - close_mixer(); - } - } - mute_request_pending = 0; - pthread_setcancelstate(oldState, NULL); -} - /* -void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void *arg) { +void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void +*arg) { debug(1, "alsa: alsa_buffer_monitor_thread_cleanup_function called."); } */ @@ -1469,7 +1514,8 @@ void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void *a void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { while (1) { if ((config.keep_dac_busy != 0) && (alsa_device_initialised == 0)) { - debug(1,"alsa: alsa_buffer_monitor_thread_code() calling do_alsa_device_init_if_needed."); + debug(1, "alsa: alsa_buffer_monitor_thread_code() calling " + "do_alsa_device_init_if_needed."); do_alsa_device_init_if_needed(); } int sleep_time_ms = (int)(config.audio_backend_silence_scan_interval * 1000); @@ -1478,18 +1524,23 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { if ((alsa_backend_state == abm_disconnected) && (config.keep_dac_busy != 0)) { // open the dac and move to abm_connected mode if (do_open() == 0) - debug(2, "alsa: alsa_buffer_monitor_thread_code() -- output device opened; alsa_backend_state => abm_connected"); + debug(2, "alsa: alsa_buffer_monitor_thread_code() -- output device opened; " + "alsa_backend_state => abm_connected"); } else if ((alsa_backend_state == abm_connected) && (config.keep_dac_busy == 0)) { stall_monitor_start_time = 0; frame_index = 0; measurement_data_is_valid = 0; - debug(2, "alsa: alsa_buffer_monitor_thread_code() -- closing the output device"); + debug(2, "alsa: alsa_buffer_monitor_thread_code() -- closing the output " + "device"); do_close(); - debug(2, "alsa: alsa_buffer_monitor_thread_code() -- alsa_backend_state => abm_disconnected"); + debug(2, "alsa: alsa_buffer_monitor_thread_code() -- alsa_backend_state " + "=> abm_disconnected"); } // now, if the backend is not in the abm_disconnected state - // and config.keep_dac_busy is true (at the present, this has to be the case to be in the - // abm_connected state in the first place...) then do the silence-filling thing, if needed + // and config.keep_dac_busy is true (at the present, this has to be the case + // to be in the + // abm_connected state in the first place...) then do the silence-filling + // thing, if needed if ((alsa_backend_state != abm_disconnected) && (config.keep_dac_busy != 0)) { int reply; long buffer_size = 0; @@ -1511,15 +1562,18 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { sleep_time_in_fp = sleep_time_in_fp << 32; sleep_time_in_fp = sleep_time_in_fp / 1000; // debug(1,"alsa: sleep_time: %d ms or 0x%" PRIx64 " in fp - // form.",sleep_time_ms,sleep_time_in_fp); int frames_of_silence = (desired_sample_rate * + // form.",sleep_time_ms,sleep_time_in_fp); int frames_of_silence = + // (desired_sample_rate * // sleep_time_ms * 2) / 1000; int frames_of_silence = 1024; size_t size_of_silence_buffer = frames_of_silence * frame_size; - // debug(1, "alsa: alsa_buffer_monitor_thread_code -- silence buffer length: %u bytes.", + // debug(1, "alsa: alsa_buffer_monitor_thread_code -- silence buffer + // length: %u bytes.", // size_of_silence_buffer); void *silence = malloc(size_of_silence_buffer); if (silence == NULL) { - debug(1, "alsa: alsa_buffer_monitor_thread_code -- failed to allocate memory for a " + debug(1, "alsa: alsa_buffer_monitor_thread_code -- failed to " + "allocate memory for a " "silent frame buffer."); } else { pthread_cleanup_push(malloc_cleanup, silence); @@ -1527,10 +1581,12 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { if ((hardware_mixer == 0) && (config.ignore_volume_control == 0) && (config.airplay_volume != 0.0)) use_dither = 1; - dither_random_number_store = generate_zero_frames( - silence, frames_of_silence, config.output_format, use_dither, // i.e. with dither - dither_random_number_store); - // debug(1,"Play %d frames of silence with most_recent_write_time of %" PRIx64 ".", + dither_random_number_store = + generate_zero_frames(silence, frames_of_silence, config.output_format, + use_dither, // i.e. with dither + dither_random_number_store); + // debug(1,"Play %d frames of silence with most_recent_write_time of + // %" PRIx64 ".", // frames_of_silence,most_recent_write_time); do_play(silence, frames_of_silence); pthread_cleanup_pop(1); diff --git a/player.c b/player.c index 642b6e2c..eb2be192 100644 --- a/player.c +++ b/player.c @@ -285,18 +285,16 @@ static int alac_decode(short *dest, int *destlen, uint8_t *buf, int len, rtsp_co } if (outsize > toutsize) { - debug(2, - "Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- " - "truncated, but buffer overflow possible! Encrypted = %d.", + debug(2, "Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- " + "truncated, but buffer overflow possible! Encrypted = %d.", outsize, toutsize, conn->stream.encrypted); reply = -1; // output packet is the wrong size } *destlen = outsize / conn->input_bytes_per_frame; if ((outsize % conn->input_bytes_per_frame) != 0) - debug(1, - "Number of audio frames (%d) does not correspond exactly to the number of bytes (%d) " - "and the audio frame size (%d).", + debug(1, "Number of audio frames (%d) does not correspond exactly to the number of bytes (%d) " + "and the audio frame size (%d).", *destlen, outsize, conn->input_bytes_per_frame); return reply; } @@ -456,10 +454,9 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in if ((conn->flush_rtp_timestamp != 0) && (actual_timestamp != conn->flush_rtp_timestamp) && (modulo_32_offset(actual_timestamp, conn->flush_rtp_timestamp) < conn->input_rate * 10)) { // if it's less than 10 seconds - debug(3, - "Dropping flushed packet in player_put_packet, seqno %u, timestamp %" PRIu32 - ", flushing to " - "timestamp: %" PRIu32 ".", + debug(3, "Dropping flushed packet in player_put_packet, seqno %u, timestamp %" PRIu32 + ", flushing to " + "timestamp: %" PRIu32 ".", seqno, actual_timestamp, conn->flush_rtp_timestamp); conn->initial_reference_time = 0; conn->initial_reference_timestamp = 0; @@ -852,10 +849,9 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { (curframe->given_timestamp != conn->flush_rtp_timestamp) && (modulo_32_offset(curframe->given_timestamp, conn->flush_rtp_timestamp) < conn->input_rate * 10)) { // if it's less than ten seconds - debug(3, - "Dropping flushed packet in buffer_get_frame, seqno %u, timestamp %" PRIu32 - ", flushing to " - "timestamp: %" PRIu32 ".", + debug(3, "Dropping flushed packet in buffer_get_frame, seqno %u, timestamp %" PRIu32 + ", flushing to " + "timestamp: %" PRIu32 ".", curframe->sequence_number, curframe->given_timestamp, conn->flush_rtp_timestamp); curframe->ready = 0; curframe->resend_level = 0; @@ -938,7 +934,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { frame_to_local_time(conn->first_packet_timestamp + conn->latency + (uint32_t)(config.audio_backend_latency_offset * conn->input_rate), // this will go modulo 2^32 - &should_be_time, conn); + &should_be_time, + conn); conn->first_packet_time_to_play = should_be_time; @@ -959,7 +956,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { frame_to_local_time(conn->first_packet_timestamp + conn->latency + (uint32_t)(config.audio_backend_latency_offset * conn->input_rate), // this should go modulo 2^32 - &should_be_time, conn); + &should_be_time, + conn); conn->first_packet_time_to_play = should_be_time; @@ -1030,9 +1028,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { // this might happen if a big clock adjustment was made at just the wrong // time. - debug(1, - "Run a bit past the exact start time by %" PRId64 - " frames with a DAC delay of %ld frames.", + debug(1, "Run a bit past the exact start time by %" PRId64 + " frames with a DAC delay of %ld frames.", -exact_frame_gap, dac_delay); if (config.output->flush) config.output->flush(); @@ -1190,7 +1187,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { (uint32_t)(config.audio_backend_latency_offset * conn->input_rate) - (uint32_t)(config.audio_backend_buffer_desired_length * conn->input_rate), // this will go modulo 2^32 - &time_to_play, conn); + &time_to_play, + conn); if (local_time_now >= time_to_play) { do_wait = 0; @@ -1670,17 +1668,17 @@ void *player_thread_func(void *arg) { // if ((input_rate!=config.output_rate) || (input_bit_depth!=output_bit_depth)) { // debug(1,"Define tbuf of length // %d.",output_bytes_per_frame*(max_frames_per_packet*output_sample_ratio+max_frame_size_change)); - conn->tbuf = malloc( - sizeof(int32_t) * 2 * - (conn->max_frames_per_packet * conn->output_sample_ratio + conn->max_frame_size_change)); + conn->tbuf = + malloc(sizeof(int32_t) * 2 * (conn->max_frames_per_packet * conn->output_sample_ratio + + conn->max_frame_size_change)); if (conn->tbuf == NULL) die("Failed to allocate memory for the transition buffer."); // initialise this, because soxr stuffing might be chosen later - conn->sbuf = malloc( - sizeof(int32_t) * 2 * - (conn->max_frames_per_packet * conn->output_sample_ratio + conn->max_frame_size_change)); + conn->sbuf = + malloc(sizeof(int32_t) * 2 * (conn->max_frames_per_packet * conn->output_sample_ratio + + conn->max_frame_size_change)); if (conn->sbuf == NULL) die("Failed to allocate memory for the sbuf buffer."); @@ -1968,9 +1966,8 @@ void *player_thread_func(void *arg) { SUCCESSOR(conn->last_seqno_read); // int32_t from seq_t, i.e. uint16_t, so okay. if (inframe->sequence_number != conn->last_seqno_read) { // seq_t, ei.e. uint16_t and int32_t, so okay - debug(2, - "Player: packets out of sequence: expected: %u, got: %u, with ab_read: %u " - "and ab_write: %u.", + debug(2, "Player: packets out of sequence: expected: %u, got: %u, with ab_read: %u " + "and ab_write: %u.", conn->last_seqno_read, inframe->sequence_number, conn->ab_read, conn->ab_write); conn->last_seqno_read = inframe->sequence_number; // reset warning... } @@ -2195,7 +2192,7 @@ void *player_thread_func(void *arg) { #ifdef CONFIG_CONVOLUTION || config.convolution #endif - ) { + ) { int32_t *tbuf32 = (int32_t *)conn->tbuf; float fbuf_l[inbuflength]; float fbuf_r[inbuflength]; @@ -2279,9 +2276,8 @@ void *player_thread_func(void *arg) { debug(1, "play_samples==0 skipping it (1)."); else { if (conn->software_mute_enabled) { - generate_zero_frames( - conn->outbuf, play_samples, - config.output_format, conn->enable_dither, conn->previous_random_number); + generate_zero_frames(conn->outbuf, play_samples, config.output_format, + conn->enable_dither, conn->previous_random_number); } config.output->play(conn->outbuf, play_samples); } @@ -2320,9 +2316,8 @@ void *player_thread_func(void *arg) { debug(1, "NULL outbuf to play -- skipping it."); else { if (conn->software_mute_enabled) { - generate_zero_frames( - conn->outbuf, play_samples, - config.output_format, conn->enable_dither, conn->previous_random_number); + generate_zero_frames(conn->outbuf, play_samples, config.output_format, + conn->enable_dither, conn->previous_random_number); } config.output->play(conn->outbuf, play_samples); // remove the (short*)! } @@ -2445,41 +2440,42 @@ void *player_thread_func(void *arg) { if ((config.output->delay)) { if (config.no_sync == 0) { - inform( - "%*.2f," /* Sync error in milliseconds */ - "%*.1f," /* net correction in ppm */ - "%*.1f," /* corrections in ppm */ - "%*d," /* total packets */ - "%*" PRIu64 "," /* missing packets */ - "%*" PRIu64 "," /* late packets */ - "%*" PRIu64 "," /* too late packets */ - "%*" PRIu64 "," /* resend requests */ - "%*" PRId64 "," /* min DAC queue size */ - "%*" PRId32 "," /* min buffer occupancy */ - "%*" PRId32 "," /* max buffer occupancy */ - "%*.2f," /* source nominal frame rate */ - "%*.2f," /* source actual (average) frame rate */ - "%*.2f," /* output frame rate */ - "%*.2f," /* source clock drift */ - "%*d," /* source clock drift sample count */ - "%*.2f", /* rough calculated correction in ppm */ - 10, 1000 * moving_average_sync_error / config.output_rate, 10, - moving_average_correction * 1000000 / (352 * conn->output_sample_ratio), 10, - moving_average_insertions_plus_deletions * 1000000 / - (352 * conn->output_sample_ratio), - 12, play_number, 7, conn->missing_packets, 7, conn->late_packets, 7, - conn->too_late_packets, 7, conn->resend_requests, 7, minimum_dac_queue_size, - 5, minimum_buffer_occupancy, 5, maximum_buffer_occupancy, 11, - conn->remote_frame_rate, 11, conn->input_frame_rate, 11, conn->frame_rate, 10, - (conn->local_to_remote_time_gradient - 1.0) * 1000000, 6, - conn->local_to_remote_time_gradient_sample_count, 10, - (conn->frame_rate > 0.0) - ? ((conn->frame_rate - conn->remote_frame_rate * - conn->output_sample_ratio * - conn->local_to_remote_time_gradient) * - 1000000) / - conn->frame_rate - : 0.0); + inform("%*.2f," /* Sync error in milliseconds */ + "%*.1f," /* net correction in ppm */ + "%*.1f," /* corrections in ppm */ + "%*d," /* total packets */ + "%*" PRIu64 "," /* missing packets */ + "%*" PRIu64 "," /* late packets */ + "%*" PRIu64 "," /* too late packets */ + "%*" PRIu64 "," /* resend requests */ + "%*" PRId64 "," /* min DAC queue size */ + "%*" PRId32 "," /* min buffer occupancy */ + "%*" PRId32 "," /* max buffer occupancy */ + "%*.2f," /* source nominal frame rate */ + "%*.2f," /* source actual (average) frame rate */ + "%*.2f," /* output frame rate */ + "%*.2f," /* source clock drift */ + "%*d," /* source clock drift sample count */ + "%*.2f", /* rough calculated correction in ppm */ + 10, + 1000 * moving_average_sync_error / config.output_rate, 10, + moving_average_correction * 1000000 / (352 * conn->output_sample_ratio), + 10, moving_average_insertions_plus_deletions * 1000000 / + (352 * conn->output_sample_ratio), + 12, play_number, 7, conn->missing_packets, 7, conn->late_packets, 7, + conn->too_late_packets, 7, conn->resend_requests, 7, + minimum_dac_queue_size, 5, minimum_buffer_occupancy, 5, + maximum_buffer_occupancy, 11, conn->remote_frame_rate, 11, + conn->input_frame_rate, 11, conn->frame_rate, 10, + (conn->local_to_remote_time_gradient - 1.0) * 1000000, 6, + conn->local_to_remote_time_gradient_sample_count, 10, + (conn->frame_rate > 0.0) + ? ((conn->frame_rate - + conn->remote_frame_rate * conn->output_sample_ratio * + conn->local_to_remote_time_gradient) * + 1000000) / + conn->frame_rate + : 0.0); } else { inform("%*.2f," /* Sync error in milliseconds */ "%*d," /* total packets */ @@ -2494,9 +2490,10 @@ void *player_thread_func(void *arg) { "%*.2f," /* source actual (average) frame rate */ "%*.2f," /* source clock drift */ "%*d", /* source clock drift sample count */ - 10, 1000 * moving_average_sync_error / config.output_rate, 12, play_number, - 7, conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, - 7, conn->resend_requests, 7, minimum_dac_queue_size, 5, + 10, + 1000 * moving_average_sync_error / config.output_rate, 12, play_number, 7, + conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, 7, + conn->resend_requests, 7, minimum_dac_queue_size, 5, minimum_buffer_occupancy, 5, maximum_buffer_occupancy, 11, conn->remote_frame_rate, 11, conn->input_frame_rate, 10, (conn->local_to_remote_time_gradient - 1.0) * 1000000, 6, @@ -2515,9 +2512,10 @@ void *player_thread_func(void *arg) { "%*.2f," /* source actual (average) frame rate */ "%*.2f," /* source clock drift */ "%*d", /* source clock drift sample count */ - 10, 1000 * moving_average_sync_error / config.output_rate, 12, play_number, - 7, conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, - 7, conn->resend_requests, 5, minimum_buffer_occupancy, 5, + 10, + 1000 * moving_average_sync_error / config.output_rate, 12, play_number, 7, + conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, 7, + conn->resend_requests, 5, minimum_buffer_occupancy, 5, maximum_buffer_occupancy, 11, conn->remote_frame_rate, 11, conn->input_frame_rate, 10, (conn->local_to_remote_time_gradient - 1.0) * 1000000, 6, @@ -2544,14 +2542,16 @@ void *player_thread_func(void *arg) { } void player_volume_without_notification(double airplay_volume, rtsp_conn_info *conn) { - debug(1,"player_volume_without_notification %f",airplay_volume); -//first, see if we are hw only, sw only, both with hw attenuation on the top or both with sw attenuation on top + debug(1, "player_volume_without_notification %f", airplay_volume); + // first, see if we are hw only, sw only, both with hw attenuation on the top or both with sw + // attenuation on top + + enum volume_mode_type { vol_sw_only, vol_hw_only, vol_both } volume_mode; - enum volume_mode_type {vol_sw_only, vol_hw_only, vol_both} volume_mode; - - // take account of whether there is a hardware mixer, if a max volume has been specified and if a range has been specified + // take account of whether there is a hardware mixer, if a max volume has been specified and if a + // range has been specified // the range might imply that both hw and software mixers are needed, so calculate this - + int32_t hw_max_db, hw_min_db; int32_t sw_max_db = 0, sw_min_db = -9630; if (config.output->parameters) { @@ -2569,9 +2569,9 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c sw_max_db = (config.volume_max_db * 100) - hw_min_db; } else { warn("The maximum output level is outside the range of the hardware mixer -- ignored"); - } + } } - + // here, we have set limits on the hw_max_db and the sw_max_db // but we haven't actually decided whether we need both hw and software attenuation // only if a range is specified could we need both @@ -2583,9 +2583,9 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c int32_t desired_sw_range = desired_range_db - (hw_max_db - hw_min_db); if ((sw_max_db - desired_sw_range) < sw_min_db) warn("The range requested is too large to accommodate -- ignored."); - else + else sw_min_db = (sw_max_db - desired_sw_range); - } + } } } else { // debug(1,"has no hardware mixer"); @@ -2593,112 +2593,116 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c if (config.volume_max_db_set) { if (((config.volume_max_db * 100) <= sw_max_db) && ((config.volume_max_db * 100) >= sw_min_db)) - sw_max_db = (int32_t)config.volume_max_db * 100; + sw_max_db = (int32_t)config.volume_max_db * 100; } if (config.volume_range_db) { - // see if the range requested exceeds the software range available - int32_t desired_range_db = (int32_t)trunc(config.volume_range_db * 100); - if ((desired_range_db) > (sw_max_db - sw_min_db)) - warn("The range requested is too large to accommodate -- ignored."); - else - sw_min_db = (sw_max_db - desired_range_db); - } + // see if the range requested exceeds the software range available + int32_t desired_range_db = (int32_t)trunc(config.volume_range_db * 100); + if ((desired_range_db) > (sw_max_db - sw_min_db)) + warn("The range requested is too large to accommodate -- ignored."); + else + sw_min_db = (sw_max_db - desired_range_db); + } } - - // here, we know whether it's hw volume control only, sw only or both, and we have the hw and sw limits. + + // here, we know whether it's hw volume control only, sw only or both, and we have the hw and sw + // limits. // if it's both, we haven't decided whether hw or sw should be on top // we have to consider the settings ignore_volume_control and mute. - + if (config.ignore_volume_control == 0) { if (airplay_volume == -144.0) { - - if ((config.output->mute) && (config.output->mute(1) != 0)) - debug(1,"hardware mute is enabled."); + + if ((config.output->mute) && (config.output->mute(1) == 0)) + debug(1, "player: hardware mute is enabled."); else { conn->software_mute_enabled = 1; - debug(1,"software mute is enabled."); - + debug(1, "player: software mute is enabled."); } } else { - int32_t max_db,min_db; + int32_t max_db, min_db; switch (volume_mode) { - case vol_hw_only: - max_db = hw_max_db; - min_db = hw_min_db; - break; - case vol_sw_only: - max_db = sw_max_db; - min_db = sw_min_db; - break; - case vol_both: - debug(1,"dB range passed is hw: %d, sw: %d, total: %d", hw_max_db - hw_min_db, sw_max_db - sw_min_db, (hw_max_db - hw_min_db) + (sw_max_db - sw_min_db)); - max_db = (hw_max_db - hw_min_db) + (sw_max_db - sw_min_db); // this should be the range requested - min_db = 0; - break; - default: - debug(1,"error in pv -- not in a volume mode"); - break; + case vol_hw_only: + max_db = hw_max_db; + min_db = hw_min_db; + break; + case vol_sw_only: + max_db = sw_max_db; + min_db = sw_min_db; + break; + case vol_both: + debug(1, "dB range passed is hw: %d, sw: %d, total: %d", hw_max_db - hw_min_db, + sw_max_db - sw_min_db, (hw_max_db - hw_min_db) + (sw_max_db - sw_min_db)); + max_db = + (hw_max_db - hw_min_db) + (sw_max_db - sw_min_db); // this should be the range requested + min_db = 0; + break; + default: + debug(1, "error in pv -- not in a volume mode"); + break; } double scaled_attenuation = 0.0; if (config.volume_control_profile == VCP_standard) scaled_attenuation = vol2attn(airplay_volume, max_db, min_db); // no cancellation points else if (config.volume_control_profile == VCP_flat) - scaled_attenuation = flat_vol2attn(airplay_volume, max_db, min_db); // no cancellation points + scaled_attenuation = + flat_vol2attn(airplay_volume, max_db, min_db); // no cancellation points else debug(1, "Unrecognised volume control profile"); - + // so here we have the scaled attenuation. If it's for hw or sw only, it's straightforward. double hardware_attenuation = 0.0; double software_attenuation = 0.0; switch (volume_mode) { - case vol_hw_only: - hardware_attenuation = scaled_attenuation; - break; - case vol_sw_only: - software_attenuation = scaled_attenuation; - break; - case vol_both: - // here, we now the attenuation required, so we have to apportion it to the sw and hw mixers - // if we give the hw priority, that means when lowering the volume, set the hw volume to its lowest - // before using the sw attenuation. - // similarly, if we give the sw priority, that means when lowering the volume, set the sw volume to its lowest - // before using the hw attenuation. - // one imagines that hw priority is likely to be much better - if (config.volume_range_hw_priority) { - // hw priority - if ((sw_max_db - sw_min_db) > scaled_attenuation) { - software_attenuation = sw_min_db + scaled_attenuation; - hardware_attenuation = hw_min_db; - } else { - software_attenuation = sw_max_db; - hardware_attenuation = hw_min_db + scaled_attenuation - (sw_max_db - sw_min_db); - } - } else { - // sw priority - if ((hw_max_db - hw_min_db) > scaled_attenuation) { - hardware_attenuation = hw_min_db + scaled_attenuation; - software_attenuation = sw_min_db; - } else { - hardware_attenuation = hw_max_db; - software_attenuation = sw_min_db + scaled_attenuation - (hw_max_db - hw_min_db); - } + case vol_hw_only: + hardware_attenuation = scaled_attenuation; + break; + case vol_sw_only: + software_attenuation = scaled_attenuation; + break; + case vol_both: + // here, we now the attenuation required, so we have to apportion it to the sw and hw mixers + // if we give the hw priority, that means when lowering the volume, set the hw volume to its + // lowest + // before using the sw attenuation. + // similarly, if we give the sw priority, that means when lowering the volume, set the sw + // volume to its lowest + // before using the hw attenuation. + // one imagines that hw priority is likely to be much better + if (config.volume_range_hw_priority) { + // hw priority + if ((sw_max_db - sw_min_db) > scaled_attenuation) { + software_attenuation = sw_min_db + scaled_attenuation; + hardware_attenuation = hw_min_db; + } else { + software_attenuation = sw_max_db; + hardware_attenuation = hw_min_db + scaled_attenuation - (sw_max_db - sw_min_db); + } + } else { + // sw priority + if ((hw_max_db - hw_min_db) > scaled_attenuation) { + hardware_attenuation = hw_min_db + scaled_attenuation; + software_attenuation = sw_min_db; + } else { + hardware_attenuation = hw_max_db; + software_attenuation = sw_min_db + scaled_attenuation - (hw_max_db - hw_min_db); } - break; - default: - debug(1,"error in pv -- not in a volume mode"); - break; + } + break; + default: + debug(1, "error in pv -- not in a volume mode"); + break; } - - + if (((volume_mode == vol_hw_only) || (volume_mode == vol_both)) && (config.output->volume)) { config.output->volume(hardware_attenuation); // otherwise set the output to the lowest value // debug(1,"Hardware attenuation set to %f for airplay volume of // %f.",hardware_attenuation,airplay_volume); conn->fix_volume = 0x10000; } - + if ((volume_mode == vol_sw_only) || (volume_mode == vol_both)) { double temp_fix_volume = 65536.0 * pow(10, software_attenuation / 2000); // debug(1,"Software attenuation set to %f, i.e %f out of 65,536, for airplay volume of @@ -2711,7 +2715,6 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c loudness_set_volume(software_attenuation / 100); } - if (config.logOutputLevel) { inform("Output Level set to: %.2f dB.", scaled_attenuation / 100.0); } @@ -2733,8 +2736,10 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c if (config.output->mute) config.output->mute(0); conn->software_mute_enabled = 0; - - debug(1,"pv: volume mode is %d, software_attenuation: %f, hardware_attenuation: %f, muting is disabled.", volume_mode, software_attenuation, hardware_attenuation); + + debug(1, "pv: volume mode is %d, software_attenuation: %f, hardware_attenuation: %f, muting " + "is disabled.", + volume_mode, software_attenuation, hardware_attenuation); } } config.airplay_volume = airplay_volume;