From: Mike Brady Date: Mon, 29 Jan 2018 22:28:22 +0000 (+0000) Subject: Add the option to use a flat volume control profile. X-Git-Tag: 3.2d29~96 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f83b86ec2323fda033f26fbf97740ffc7da549e1;p=thirdparty%2Fshairport-sync.git Add the option to use a flat volume control profile. --- diff --git a/audio_alsa.c b/audio_alsa.c index 3315ed97..977e2517 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -1,6 +1,7 @@ /* * libalsa output driver. This file is part of Shairport. * Copyright (c) Muffinman, Skaman 2013 + * Copyright (c) Mike Brady 2014 -- 2018 * All rights reserved. * * Permission is hereby granted, free of charge, to any person diff --git a/common.c b/common.c index f2d714d9..dd12d6cd 100644 --- a/common.c +++ b/common.c @@ -664,6 +664,19 @@ uint32_t uatoi(const char *nptr) { return r; } + +double flat_vol2attn(double vol, long max_db, long min_db) { + double vol_setting = min_db; + + if ((vol <= 0.0) && (vol >= -30.0)) { + vol_setting = ((max_db - min_db)*(30.0+vol)/30)+min_db; + debug(2,"Linear Volume Setting: %f in range %ld to %ld.",vol_setting,min_db,max_db); + } else if (vol != -144.0) { + debug(1, "Linear 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. @@ -725,6 +738,7 @@ double vol2attn(double vol, long max_db, long min_db) { vol_setting = min_db; // for safety, return the lowest setting... } // debug(1,"returning an attenuation of %f.",vol_setting); + debug(2,"Standard profile Volume Setting for Airplay vol %f: %f in range %ld to %ld.",vol,vol_setting,min_db,max_db); return vol_setting; } diff --git a/common.h b/common.h index 86ea7dc1..e36aae9a 100644 --- a/common.h +++ b/common.h @@ -61,6 +61,11 @@ enum playback_mode_type { ST_right_only, } playback_mode_type; +enum volume_control_profile_type { + VCP_standard = 0, + VCP_flat, +} volume_control_profile_type; + enum decoders_supported_type { decoder_hammerton = 0, decoder_apple_alac, @@ -157,6 +162,7 @@ typedef struct { uint32_t volume_range_db; // the range, in dB, from max dB to min dB. Zero means use the mixer's // native range. enum sps_format_t output_format; + enum volume_control_profile_type volume_control_profile; int output_rate; #ifdef CONFIG_CONVOLUTION @@ -219,6 +225,10 @@ char *base64_enc(uint8_t *input, int length); #define RSA_MODE_KEY (1) uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode); +// 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 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 the transfer function double vol2attn(double vol, long max_db, long min_db); diff --git a/player.c b/player.c index 459a90f6..790ced47 100644 --- a/player.c +++ b/player.c @@ -2416,8 +2416,12 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c config.output->mute(0); // unmute mute if it's there if (config.ignore_volume_control == 1) scaled_attenuation = max_db; - else - scaled_attenuation = vol2attn(airplay_volume, max_db, min_db); + else if (config.volume_control_profile == VCP_standard) + scaled_attenuation = vol2attn(airplay_volume, max_db, min_db); + else if (config.volume_control_profile == VCP_flat) + scaled_attenuation = flat_vol2attn(airplay_volume, max_db, min_db); + else debug(1,"Unrecognised volume control profile"); + if (hw_range_db) { // if there is a hardware mixer if (scaled_attenuation <= hw_max_db) { diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 896ed1f9..f65ee917 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -24,6 +24,9 @@ general = // ignore_volume_control = "no"; // set this to "yes" if you want the volume to be at 100% no matter what the source's volume control is set to. // volume_range_db = 60 ; // use this advanced setting to set the range, in dB, you want between the maximum volume and the minimum volume. Range is 30 to 150 dB. Leave it commented out to use mixer's native range. // volume_max_db = 0.0 ; // use this advanced setting, which must have a decimal point in it, to set the maximum volume, in dB, you wish to use. +// 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 than logarithmically at lower values and slower at higher values. +// "flat" makes the volume change logarithmically at all values. // The setting is for the hardware mixer, if chosen, or the software mixer otherwise. The value must be in the mixer's range (0.0 to -96.2 for the software mixer). // Leave it commented out to use mixer's maximum volume. // run_this_when_volume_is_set = "/full/path/to/application/and/args"; // Run the specified application whenever the volume control is set or changed. diff --git a/shairport.c b/shairport.c index 99df5cc1..6193857d 100644 --- a/shairport.c +++ b/shairport.c @@ -622,6 +622,16 @@ int parse_options(int argc, char **argv) { "\"reverse stereo\", \"both left\", \"both right\""); } + /* Get the volume control profile setting -- "standard" or "flat" */ + if (config_lookup_string(config.cfg, "general.volume_control_profile", &str)) { + if (strcasecmp(str, "standard") == 0) + config.volume_control_profile = VCP_standard; + else if (strcasecmp(str, "flat") == 0) + config.volume_control_profile = VCP_flat; + else + die("Invalid volume_control_profile choice \"%s\". It should be \"standard\" (default) or \"flat\""); + } + /* Get the interface to listen on, if specified Default is all interfaces */ /* we keep the interface name and the index */