]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Add logarithmic volume_control_profile mode
authordasl- <david.leibovic@gmail.com>
Sun, 2 Jul 2023 13:27:53 +0000 (09:27 -0400)
committerdasl- <david.leibovic@gmail.com>
Sun, 2 Jul 2023 13:27:53 +0000 (09:27 -0400)
common.c
common.h
dbus-service.c
player.c
scripts/shairport-sync.conf
shairport.c

index 2c902e3bc8775b7b0bfa1b95710383191fb0137a..f9b83b8d76d7c47857cb9b1345c654c48d112d9c 100644 (file)
--- a/common.c
+++ b/common.c
@@ -37,6 +37,7 @@
 #include <fcntl.h>
 #include <inttypes.h> // PRIdPTR
 #include <libgen.h>
+#include <math.h>
 #include <memory.h>
 #include <poll.h>
 #include <popt.h>
@@ -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.
index e152db687e03aa8793627a67d03bc531c3ecf532..ceea2206df2b4ce12a01155dc9cba01843f43b95 100644 (file)
--- 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);
index 6a3b544f8b8b92b97c87dfa9972093f3a50af111..4795896e5f71ba126c04128aa25a4091479a9dba 100644 (file)
@@ -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");
 
index c1739ee163a01ec7592f2d139abaee96395ae3b7..3aa299430484b45480da8e6a789d3ff3b1922f9f 100644 (file)
--- 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");
     }
index 03cb4339a3a767b5455a7bdad83bbea9653852b0..e252198592be845ddca959dffc2180a1ffd8c489 100644 (file)
@@ -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
index db5bf2c077d1280a64fa57eee147d4fa3f58ddeb..7d20031337a9cd0b902d5de2631c0210bed6ffb6 100644 (file)
@@ -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);
       }