From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 19 Jan 2023 12:44:26 +0000 (+0000) Subject: Continuing to check the high-volume limiter. Add configuration file settings for... X-Git-Tag: 4.2.1d0~41 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=72f1554cbbfa8a10d59641c25187b4a9ea77e187;p=thirdparty%2Fshairport-sync.git Continuing to check the high-volume limiter. Add configuration file settings for default and threshold volume settings and timeout. Include in diagnostics, set tentative defaults. --- diff --git a/player.c b/player.c index db7b8ae0..84f0a9e4 100644 --- a/player.c +++ b/player.c @@ -1704,33 +1704,31 @@ void statistics_item(const char *heading, const char *format, ...) { double suggested_volume(rtsp_conn_info *conn) { double response = config.airplay_volume; - if (conn) { - if (conn->own_airplay_volume_set != 0) { - response = conn->own_airplay_volume; - } else if (config.airplay_volume > config.high_threshold_airplay_volume) { - int64_t volume_validity_time = config.limit_to_high_volume_threshold_time_in_minutes; - // zero means never check the volume - if (volume_validity_time != 0) { - // If the volume is higher than the high volume threshold - // and enough time has gone past, suggest the default volume. - uint64_t time_now = get_absolute_time_in_ns(); - int64_t time_since_last_access_to_volume_info = - time_now - config.last_access_to_volume_info_time; - - volume_validity_time = volume_validity_time * 60; // to seconds - volume_validity_time = volume_validity_time * 1000000000; // to nanoseconds - - if ((config.airplay_volume > config.high_threshold_airplay_volume) && - ((config.last_access_to_volume_info_time == 0) || - (time_since_last_access_to_volume_info > volume_validity_time))) { - - debug(2, - "the current volume %.6f is higher than the high volume threshold %.6f, so the " - "default volume %.6f is suggested.", - config.airplay_volume, config.high_threshold_airplay_volume, - config.default_airplay_volume); - response = config.default_airplay_volume; - } + if ((conn != NULL) && (conn->own_airplay_volume_set != 0)) { + response = conn->own_airplay_volume; + } else if (config.airplay_volume > config.high_threshold_airplay_volume) { + int64_t volume_validity_time = config.limit_to_high_volume_threshold_time_in_minutes; + // zero means never check the volume + if (volume_validity_time != 0) { + // If the volume is higher than the high volume threshold + // and enough time has gone past, suggest the default volume. + uint64_t time_now = get_absolute_time_in_ns(); + int64_t time_since_last_access_to_volume_info = + time_now - config.last_access_to_volume_info_time; + + volume_validity_time = volume_validity_time * 60; // to seconds + volume_validity_time = volume_validity_time * 1000000000; // to nanoseconds + + if ((config.airplay_volume > config.high_threshold_airplay_volume) && + ((config.last_access_to_volume_info_time == 0) || + (time_since_last_access_to_volume_info > volume_validity_time))) { + + debug(2, + "the current volume %.6f is higher than the high volume threshold %.6f, so the " + "default volume %.6f is suggested.", + config.airplay_volume, config.high_threshold_airplay_volume, + config.default_airplay_volume); + response = config.default_airplay_volume; } } } diff --git a/rtsp.c b/rtsp.c index 83374deb..11f09ed0 100644 --- a/rtsp.c +++ b/rtsp.c @@ -757,19 +757,24 @@ void cleanup_threads(void) { free(conns[i]); conns[i] = NULL; } - if (conns[i] != NULL) + if (conns[i] != NULL) { + debug(1, "Airplay Volume for connection %d is %.6f.", conns[i]->connection_number, + suggested_volume(conns[i])); connection_count++; + } } debug_mutex_unlock(&conns_lock, 3); + if (old_connection_count != connection_count) { - if (connection_count == 0) + if (connection_count == 0) { debug(2, "No active connections."); - else if (connection_count == 1) + } else if (connection_count == 1) debug(2, "One active connection."); else debug(2, "%d active connections.", connection_count); old_connection_count = connection_count; } + debug(1, "Airplay Volume for new connections is %.6f.", suggested_volume(NULL)); } // park a null at the line ending, and return the next line pointer @@ -5477,6 +5482,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) { FD_SET(sockfd[i], &fds); ret = select(maxfd + 1, &fds, 0, 0, &tv); + if (ret < 0) { if (errno == EINTR) continue; diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 918ad377..5731fe08 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -43,9 +43,24 @@ general = // "standard" makes the volume change more quickly at lower volumes and slower at higher volumes. // "flat" makes the volume change at the same rate at all volumes. // volume_control_combined_hardware_priority = "no"; // when extending the volume range by combining the built-in software attenuator with the hardware mixer attenuator, set this to "yes" to reduce volume by using the hardware mixer first, then the built-in software attenuator. + +// default_airplay_volume = 24.0; // this is the suggested volume after a reset or after the high_volume_threshold has been exceed and the high_volume_idle_timeout_in_minutes has passed + +// The following settings are for dealing with potentially surprising high ("very loud") volume levels. +// When a new play session starts, it usually requests a suggested volume level from Shairport Sync. This is normally the volume level of the last session. +// This can cause unpleasant surprises if the last session was (a) very loud and (b) a long time ago. +// Thus, the user could be unpleasantly surprised by the volume level of the new session. + +// To deal with this, when the last session volume is "very loud", the following two settings will lower the suggested volume after a period of idleness: + +// high_threshold_airplay_volume = -16.0; // airplay volume greater or equal to this is "very loud" +// high_volume_idle_timeout_in_minutes = 0; // if the current volume is "very loud" and the device is not playing for more than this time, suggest the default volume for new connections instead of the current volume. +// Note 1: This timeout is set to 0 by default to disable this feature. Set it to some positive number, e.g. 180 to activate the feature. +// Note 2: Not all applications use the suggested volume: MacOS Music and Mac OS System Sounds use their own settings. + // run_this_when_volume_is_set = "/full/path/to/application/and/args"; // Run the specified application whenever the volume control is set or changed. // The desired AirPlay volume is appended to the end of the command line – leave a space if you want it treated as an extra argument. -// AirPlay volume goes from 0 to -30 and -144 means "mute". +// AirPlay volume goes from 0.0 to -30.0 and -144.0 means "mute". // audio_backend_latency_offset_in_seconds = 0.0; // This is added to the latency requested by the player to delay or advance the output by a fixed amount. // Use it, for example, to compensate for a fixed delay in the audio back end. diff --git a/shairport.c b/shairport.c index dd2edd4d..17ab9717 100644 --- a/shairport.c +++ b/shairport.c @@ -471,8 +471,8 @@ int parse_options(int argc, char **argv) { -16.0; // if the volume exceeds this, reset to the default volume if idle for the // limit_to_high_volume_threshold_time_in_minutes time config.limit_to_high_volume_threshold_time_in_minutes = - 3 * 60; // after this time in minutes, if the volume is higher, use the default_airplay_volume volume - // for new play sessions. + 0; // after this time in minutes, if the volume is higher, use the default_airplay_volume + // volume for new play sessions. config.fixedLatencyOffset = 11025; // this sounds like it works properly. config.diagnostic_drop_packet_fraction = 0.0; config.active_state_timeout = 10.0; @@ -845,6 +845,37 @@ int parse_options(int argc, char **argv) { config.volume_max_db_set = 1; } + /* Get the optional default_volume setting. */ + if (config_lookup_float(config.cfg, "general.default_airplay_volume", &dvalue)) { + // debug(1, "Default airplay volume setting of %f on the -30.0 to 0 scale", dvalue); + if ((dvalue >= -30.0) && (dvalue <= 0.0)) { + config.default_airplay_volume = dvalue; + } else { + warn("The default airplay volume setting must be between -30.0 and 0.0."); + } + } + + /* Get the optional high_volume_threshold setting. */ + if (config_lookup_float(config.cfg, "general.high_threshold_airplay_volume", &dvalue)) { + // debug(1, "High threshold airplay volume setting of %f on the -30.0 to 0 scale", dvalue); + if ((dvalue >= -30.0) && (dvalue <= 0.0)) { + config.high_threshold_airplay_volume = dvalue; + } else { + warn("The high threshold airplay volume setting must be between -30.0 and 0.0."); + } + } + + /* Get the optional high volume idle tiomeout setting. */ + if (config_lookup_float(config.cfg, "general.high_volume_idle_timeout_in_minutes", &dvalue)) { + // debug(1, "High high_volume_idle_timeout_in_minutes setting of %f", dvalue); + if (dvalue >= 0.0) { + config.limit_to_high_volume_threshold_time_in_minutes = dvalue; + } else { + warn("The high volume idle timeout in minutes setting must be 0.0 or greater. A setting " + "of 0.0 disables the high volume check."); + } + } + if (config_lookup_string(config.cfg, "general.run_this_when_volume_is_set", &str)) { config.cmd_set_volume = (char *)str; } @@ -2392,6 +2423,15 @@ int main(int argc, char **argv) { debug(1, "busy timeout time is %d.", config.timeout); debug(1, "drift tolerance is %f seconds.", config.tolerance); debug(1, "password is \"%s\".", strnull(config.password)); + debug(1, "default airplay volume is %.6f.", config.default_airplay_volume); + debug(1, "high threshold airplay volume is %.6f.", config.high_threshold_airplay_volume); + if (config.limit_to_high_volume_threshold_time_in_minutes == 0) + debug(1, "check for higher-than-threshold volume for new play session is disabled."); + else + debug(1, + "suggest default airplay volume for new play session instead of higher-than-threshold " + "airplay volume after %d minutes.", + config.limit_to_high_volume_threshold_time_in_minutes); debug(1, "ignore_volume_control is %d.", config.ignore_volume_control); if (config.volume_max_db_set) debug(1, "volume_max_db is %d.", config.volume_max_db);