From baf51cf2a7a34736252b3b6adedddce9ea1a0c9e Mon Sep 17 00:00:00 2001 From: dasl- Date: Sun, 2 Jul 2023 09:27:53 -0400 Subject: [PATCH] Add logarithmic volume_control_profile mode --- common.c | 27 +++++++++++++++++++++++++++ common.h | 5 +++++ dbus-service.c | 7 +++++++ player.c | 3 +++ scripts/shairport-sync.conf | 1 + shairport.c | 6 ++++-- 6 files changed, 47 insertions(+), 2 deletions(-) diff --git a/common.c b/common.c index 2c902e3b..f9b83b8d 100644 --- a/common.c +++ b/common.c @@ -37,6 +37,7 @@ #include #include // PRIdPTR #include +#include #include #include #include @@ -1205,6 +1206,32 @@ double flat_vol2attn(double vol, long max_db, long min_db) { } return vol_setting; } + +double logarithmic_vol2attn(double vol, long max_db, long min_db) { + double vol_setting = min_db; // if all else fails, set this, for safety + + if ((vol <= 0.0) && (vol >= -30.0)) { + double vol_pct = 1 - (vol / -30.0); // This will be in the range [0, 1] + if (vol_pct <= 0) { + return min_db; + } + + vol_setting = 1000 * log10(vol_pct) / log10(2); // This will be in the range [-inf, 0] + if (vol_setting < min_db) { + return min_db; + } + if (vol_setting > max_db) { + return max_db; + } + return vol_setting; + } else if (vol != -144.0) { + debug(1, + "Logarithmic volume request value %f is out of range: should be from 0.0 to -30.0 or -144.0.", + vol); + } + return vol_setting; +} + // Given a volume (0 to -30) and high and low attenuations available in the mixer in dB, return an // attenuation depending on the volume and the function's transfer function // See http://tangentsoft.net/audio/atten.html for data on good attenuators. diff --git a/common.h b/common.h index e152db68..ceea2206 100644 --- a/common.h +++ b/common.h @@ -73,6 +73,7 @@ typedef enum { typedef enum { VCP_standard = 0, VCP_flat, + VCP_logarithmic, } volume_control_profile_type; typedef enum { @@ -390,6 +391,10 @@ uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode); // dB), return an attenuation depending on a linear interpolation along along the range double flat_vol2attn(double vol, long max_db, long min_db); +// given a volume (0 to -30) and high and low attenuations in dB*100 (e.g. 0 to -6000 for 0 to -60 +// dB), return an attenuation depending on a logarithmic interpolation along along the range +double logarithmic_vol2attn(double vol, long max_db, long min_db); + // given a volume (0 to -30) and high and low attenuations in dB*100 (e.g. 0 to -6000 for 0 to -60 // dB), return an attenuation depending on the transfer function double vol2attn(double vol, long max_db, long min_db); diff --git a/dbus-service.c b/dbus-service.c index 6a3b544f..4795896e 100644 --- a/dbus-service.c +++ b/dbus-service.c @@ -765,6 +765,8 @@ gboolean notify_volume_control_profile_callback(ShairportSync *skeleton, config.volume_control_profile = VCP_standard; else if (strcasecmp(th, "flat") == 0) config.volume_control_profile = VCP_flat; + else if (strcasecmp(th, "logarithmic") == 0) + config.volume_control_profile = VCP_logarithmic; else { warn("Unrecognised Volume Control Profile: \"%s\".", th); switch (config.volume_control_profile) { @@ -774,6 +776,9 @@ gboolean notify_volume_control_profile_callback(ShairportSync *skeleton, case VCP_flat: shairport_sync_set_volume_control_profile(skeleton, "flat"); break; + case VCP_logarithmic: + shairport_sync_set_volume_control_profile(skeleton, "logarithmic"); + break; default: debug(1, "This should never happen!"); shairport_sync_set_volume_control_profile(skeleton, "standard"); @@ -1065,6 +1070,8 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name if (config.volume_control_profile == VCP_standard) shairport_sync_set_volume_control_profile(SHAIRPORT_SYNC(shairportSyncSkeleton), "standard"); + else if (config.volume_control_profile == VCP_logarithmic) + shairport_sync_set_volume_control_profile(SHAIRPORT_SYNC(shairportSyncSkeleton), "logarithmic"); else shairport_sync_set_volume_control_profile(SHAIRPORT_SYNC(shairportSyncSkeleton), "flat"); diff --git a/player.c b/player.c index c1739ee1..3aa29943 100644 --- a/player.c +++ b/player.c @@ -3432,6 +3432,9 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c else if (config.volume_control_profile == VCP_flat) scaled_attenuation = flat_vol2attn(airplay_volume, max_db, min_db); // no cancellation points + else if (config.volume_control_profile == VCP_logarithmic) + scaled_attenuation = + logarithmic_vol2attn(airplay_volume, max_db, min_db); // no cancellation points else debug(1, "player_volume_without_notification: unrecognised volume control profile"); } diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 03cb4339..e2521985 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -42,6 +42,7 @@ general = // volume_control_profile = "standard" ; // use this advanced setting to specify how the airplay volume is transferred to the mixer volume. // "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. +// "logarithmic" makes the volume change logarithmically. // 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 diff --git a/shairport.c b/shairport.c index db5bf2c0..7d200313 100644 --- a/shairport.c +++ b/shairport.c @@ -905,9 +905,11 @@ int parse_options(int argc, char **argv) { config.volume_control_profile = VCP_standard; else if (strcasecmp(str, "flat") == 0) config.volume_control_profile = VCP_flat; + else if (strcasecmp(str, "logarithmic") == 0) + config.volume_control_profile = VCP_logarithmic; else - die("Invalid volume_control_profile choice \"%s\". It should be \"standard\" (default) " - "or \"flat\"", + die("Invalid volume_control_profile choice \"%s\". It should be \"standard\" (default), " + "\"logarithmic\", or \"flat\"", str); } -- 2.47.3