From 4c70223f5a5e50ffca321b6ebc1875777acfd3bd Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Thu, 5 Nov 2015 18:03:58 +0000 Subject: [PATCH 1/6] Work in progress, doesn't compile --- audio.h | 12 +++--- audio_alsa.c | 35 ++++++----------- audio_ao.c | 3 +- audio_dummy.c | 3 +- audio_pipe.c | 3 +- audio_pulse.c | 3 +- audio_sndio.c | 3 +- audio_stdout.c | 3 +- common.h | 2 +- player.c | 104 +++++++++++++++++++++++++++++-------------------- 10 files changed, 93 insertions(+), 78 deletions(-) diff --git a/audio.h b/audio.h index 4738a071..359f3402 100644 --- a/audio.h +++ b/audio.h @@ -5,13 +5,9 @@ #include typedef struct { - double airplay_volume; double current_volume_dB; - int minimum_volume_dB; - int maximum_volume_dB; - int has_true_mute; - int is_muted; - int valid; + int32_t minimum_volume_dB; + int32_t maximum_volume_dB; } audio_parameters; typedef struct { @@ -42,6 +38,10 @@ typedef struct { // may be NULL, in which case soft volume parameters are used void (*parameters)(audio_parameters *info); + + // may be NULL, in which case software muting is used. + void (*mute)(int do_mute); + } audio_output; audio_output *audio_get_output(char *name); diff --git a/audio_alsa.c b/audio_alsa.c index f9851f18..4a11260d 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -46,6 +46,7 @@ static uint32_t delay(void); static void volume(double vol); static void linear_volume(double vol); static void parameters(audio_parameters *info); +static void mute(int do_mute); static int has_mute = 0; static double set_volume; @@ -234,13 +235,6 @@ static int init(int argc, char **argv) { } 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); - if (config.volume_range_db) { - long suggested_alsa_min_db = alsa_mix_maxdb - (long)trunc(config.volume_range_db*100); - if (suggested_alsa_min_db > alsa_mix_mindb) - alsa_mix_mindb = suggested_alsa_min_db; - else - inform("The volume_range_db setting, %f is greater than the native range of the mixer %f, so it is ignored.",config.volume_range_db,(alsa_mix_maxdb-alsa_mix_mindb)/100.0); - } } else { // use the linear scale and do the db conversion ourselves debug(1, "note: the hardware mixer specified -- \"%s\" -- does not have a dB volume scale, so it can't be used.",alsa_mix_ctrl); @@ -447,35 +441,30 @@ static void stop(void) { } static void parameters(audio_parameters *info) { - info->has_true_mute = has_mute; - info->is_muted = ((has_mute) && (set_volume == -144.0)); info->minimum_volume_dB = alsa_mix_mindb; info->maximum_volume_dB = alsa_mix_maxdb; - info->airplay_volume = set_volume; - info->current_volume_dB = vol2attn(set_volume, alsa_mix_maxdb, alsa_mix_mindb); } static void volume(double vol) { - // debug(1,"Volume called %f.",vol); - set_volume = vol; - double vol_setting = vol2attn(vol, alsa_mix_maxdb, alsa_mix_mindb); // debug(1,"Setting volume db to %f, for volume input of %f.",vol_setting/100,vol); - if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol_setting, -1) != 0) + if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, -1) != 0) die("Failed to set playback dB volume"); - if (has_mute) - snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, (vol != -144.0)); } static void linear_volume(double vol) { - set_volume = vol; - double vol_setting = vol2attn(vol, 0, alsa_mix_mindb)/2000; - // debug(1,"Adjusted volume is %f.",vol_setting); - double linear_volume = pow(10, vol_setting); + 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; // debug(1,"Setting volume to %ld, for volume input of %f.",int_vol,vol); if (snd_mixer_selem_set_playback_volume_all(alsa_mix_elem, int_vol) != 0) die("Failed to set playback volume"); - if (has_mute) - snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, (vol != -144.0)); +} + +static void mute(int do_mute) { + if (has_mute) { + if (do_mute) + snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 0); + else + snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 1); + } } diff --git a/audio_ao.c b/audio_ao.c index a6f9cf25..6cd98eab 100644 --- a/audio_ao.c +++ b/audio_ao.c @@ -153,4 +153,5 @@ audio_output audio_ao = {.name = "ao", .delay = NULL, .play = &play, .volume = NULL, - .parameters = NULL}; + .parameters = NULL, + .mute = NULL}; diff --git a/audio_dummy.c b/audio_dummy.c index 9742c58c..a544031b 100644 --- a/audio_dummy.c +++ b/audio_dummy.c @@ -60,4 +60,5 @@ audio_output audio_dummy = {.name = "dummy", .delay = NULL, .play = &play, .volume = NULL, - .parameters = NULL}; + .parameters = NULL, + .mute = NULL}; diff --git a/audio_pipe.c b/audio_pipe.c index d38cd8f8..e18b4e83 100644 --- a/audio_pipe.c +++ b/audio_pipe.c @@ -133,4 +133,5 @@ audio_output audio_pipe = {.name = "pipe", .delay = NULL, .play = &play, .volume = NULL, - .parameters = NULL}; + .parameters = NULL, + .mute = NULL}; diff --git a/audio_pulse.c b/audio_pulse.c index 29feed88..96c547c2 100644 --- a/audio_pulse.c +++ b/audio_pulse.c @@ -137,4 +137,5 @@ audio_output audio_pulse = {.name = "pulse", .delay = NULL, .play = &play, .volume = NULL, - .parameters = NULL}; + .parameters = NULL, + .mute = NULL}; diff --git a/audio_sndio.c b/audio_sndio.c index e59dbb85..eb3be35d 100644 --- a/audio_sndio.c +++ b/audio_sndio.c @@ -81,4 +81,5 @@ audio_output audio_sndio = {.name = "sndio", .delay = NULL, .play = &play, .volume = &volume, - .parameters = NULL}; + .parameters = NULL, + .mute= NULL}; diff --git a/audio_stdout.c b/audio_stdout.c index 1637c825..3b908db4 100644 --- a/audio_stdout.c +++ b/audio_stdout.c @@ -96,4 +96,5 @@ audio_output audio_stdout = {.name = "stdout", .delay = NULL, .play = &play, .volume = NULL, - .parameters = NULL}; + .parameters = NULL, + .mute = NULL}; diff --git a/common.h b/common.h index 16c3c0e9..72e2758c 100644 --- a/common.h +++ b/common.h @@ -121,7 +121,7 @@ uint32_t uatoi(const char *nptr); shairport_cfg config; config_t config_file_stuff; -uint32_t buffer_occupancy; +int32_t buffer_occupancy; // allow it to be negative because seq_diff may be negative int64_t session_corrections; uint32_t play_segment_reference_frame; uint64_t play_segment_reference_frame_remote_time; diff --git a/player.c b/player.c index 92ebedf9..4538e4de 100644 --- a/player.c +++ b/player.c @@ -95,7 +95,6 @@ static uint64_t packet_count = 0; static int32_t last_seqno_read; // interthread variables -static double software_mixer_volume = 1.0; static int fix_volume = 0x10000; static pthread_mutex_t vol_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -452,7 +451,7 @@ static abuf_t *buffer_get_frame(void) { if (ab_synced) { do { curframe = audio_buffer + BUFIDX(ab_read); - if (curframe->ready) { + if ((ab_read!=ab_write) && (curframe->ready)) { // it could be synced and empty, under exceptional circumstances, with the frame unused, thus apparently ready if (curframe->sequence_number != ab_read) { // some kind of sync problem has occurred. @@ -838,7 +837,7 @@ static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) { } // finally, adjust the volume, if necessary - if (software_mixer_volume != 1.0) { + if (fix_volume != 65536.0) { // pthread_mutex_lock(&vol_mutex); op = outptr; for (i = 0; i < frame_size + stuff; i++) { @@ -1171,7 +1170,7 @@ static void *player_thread_func(void *arg) { inform("Sync error: %.1f (frames); net correction: %.1f (ppm); corrections: %.1f " "(ppm); missing packets %llu; late packets %llu; too late packets %llu; " "resend requests %llu; min DAC queue size %lli, min and max buffer occupancy " - "%u and %u.", + "%d and %d.", moving_average_sync_error, moving_average_correction * 1000000 / 352, moving_average_insertions_plus_deletions * 1000000 / 352, missing_packets, late_packets, too_late_packets, resend_requests, minimum_dac_queue_size, @@ -1179,7 +1178,7 @@ static void *player_thread_func(void *arg) { else inform("Synchronisation disabled. Missing packets %llu; late packets %llu; too late packets %llu; " "resend requests %llu; min and max buffer occupancy " - "%u and %u.", + "%d and %d.", missing_packets, late_packets, too_late_packets, resend_requests, minimum_buffer_occupancy, maximum_buffer_occupancy); @@ -1201,7 +1200,7 @@ static void *player_thread_func(void *arg) { } // takes the volume as specified by the airplay protocol -void player_volume(double f) { +void player_volume(double airplay_volume) { // The volume ranges -144.0 (mute) or -30 -- 0. See // http://git.zx2c4.com/Airtunes2/about/#setting-volume @@ -1218,50 +1217,71 @@ void player_volume(double f) { // or 20 times the log of the ratio. Then multiplied by 100 for convenience. // Thus, we ask our vol2attn function for an appropriate dB between -96.3 and 0 dB and translate // it back to a number. - - double linear_volume = 0.0; - - if (config.output->volume) { - // debug(1,"Set volume to %f.",f); - config.output->volume(f); // volume will be sent as metadata by the config.output device - linear_volume = 1.0; // no attenuation needed -- this value is used as a flag to avoid calculations - } - - if (config.output->parameters) + + + // get the audio parameters + + int32_t min,max; + + if (config.output->parameters) { config.output->parameters(&audio_information); - else { - long mindb = -9630; - if (config.volume_range_db) { - long suggested_alsa_min_db = -(long)trunc(config.volume_range_db*100); - if (suggested_alsa_min_db > mindb) - mindb = suggested_alsa_min_db; - else - inform("The volume_range_db setting, %f is greater than the native range of the mixer %f, so it is ignored.",config.volume_range_db,mindb/100.0); - } - double scaled_volume = vol2attn(f, 0, mindb); - linear_volume = pow(10, scaled_volume / 2000); - audio_information.airplay_volume = f; - audio_information.minimum_volume_dB = mindb; - audio_information.maximum_volume_dB = 0; - audio_information.current_volume_dB = scaled_volume; - audio_information.has_true_mute = 0; - audio_information.is_muted = 0; - // debug(1,"Minimum software volume set to %d centi-dB",f,mindb); + max = audio_information.maximum_volume_dB; + min = audio_information.minimum_volume_dB; + } else { + max = 0; + min = -9630; } - audio_information.valid = 1; - // debug(1,"Software volume set to %f on scale with a %f dB",f,linear_volume); + + double temp_fix_volume = 65536.0; + double scaled_volume; + + if (airplay_volume==-144.0) { // if we are muting + scaled_volume = min; // this is just for the metadata + if (config.output->mute) + config.output->mute(1); // use real mute if it's there + else if (config.output->volume) { + config.output_.volume(min); // otherwise set the output to the lowest value + temp_fix_volume = 0; // and set the software multiplier to 0. + } + } else { // not muting + + if (config.volume_range_db) { + int32_t possible_min = max - (int)trunc(config.volume_range_db*100); + if (possible_min > min) + min = possible_min; + } + + // now we have the true desired range, get the volume that is being set + scaled_volume = vol2attn(airplay_volume, max, min); + double software_volume = 0; dB or attenuation + + if (config.output->volume) { + if (scaled_volume>=min) { + config.output->volume(scaled_volume); + } else { + config.output->volume(min); + software_volume = scaled_volume-min; // this is the attenuation in dB needed in software + } + } else { + software_volume = scaled_volume; + } + if (config.output->mute) // if there is hardware mute... + config.output->mute(0); // turn it off + temp_fix_volume = 65536.0 * pow(10, software_volume / 2000); + } + // debug(1,"Software volume set to %f on scale with a %f dB",airplay_volume,pow(10, software_volume / 2000)); pthread_mutex_lock(&vol_mutex); - software_mixer_volume = linear_volume; - fix_volume = 65536.0 * software_mixer_volume; + fix_volume = temp_fix_volume; pthread_mutex_unlock(&vol_mutex); + #ifdef CONFIG_METADATA char *dv = malloc(128); // will be freed in the metadata thread if (dv) { memset(dv, 0, 128); - snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", audio_information.airplay_volume, - audio_information.current_volume_dB / 100.0, - audio_information.minimum_volume_dB / 100.0, - audio_information.maximum_volume_dB / 100.0); + snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, + scaled_volume / 100.0, + min / 100.0, + max / 100.0); send_ssnc_metadata('pvol', dv, strlen(dv), 1); } #endif From f424c6dbf7dcd1404858bee112965e1c447c8078 Mon Sep 17 00:00:00 2001 From: Mke Brady Date: Thu, 5 Nov 2015 22:35:38 +0000 Subject: [PATCH 2/6] Compiles, sorta runs, flaky --- player.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/player.c b/player.c index 4538e4de..e6842fc7 100644 --- a/player.c +++ b/player.c @@ -887,7 +887,6 @@ static void *player_thread_func(void *arg) { int32_t minimum_buffer_occupancy = BUFFER_FRAMES; int32_t maximum_buffer_occupancy = 0; - audio_information.valid = 0; buffer_occupancy = 0; int play_samples; @@ -1240,8 +1239,9 @@ void player_volume(double airplay_volume) { if (config.output->mute) config.output->mute(1); // use real mute if it's there else if (config.output->volume) { - config.output_.volume(min); // otherwise set the output to the lowest value + config.output->volume(min); // otherwise set the output to the lowest value temp_fix_volume = 0; // and set the software multiplier to 0. + debug(1,"Software mute: mixer volume set to minimum: %d and software volume will be set to 0.",min); } } else { // not muting @@ -1253,15 +1253,17 @@ void player_volume(double airplay_volume) { // now we have the true desired range, get the volume that is being set scaled_volume = vol2attn(airplay_volume, max, min); - double software_volume = 0; dB or attenuation + double software_volume = 0; // dB of attenuation if (config.output->volume) { if (scaled_volume>=min) { config.output->volume(scaled_volume); + debug(1,"Set Volume, no split: mixer volume set to: %f",scaled_volume); } else { config.output->volume(min); software_volume = scaled_volume-min; // this is the attenuation in dB needed in software - } + debug(1,"Set Volume, split: mixer volume set to: %f, software volume set to: %f.",min,software_volume); + } } else { software_volume = scaled_volume; } @@ -1269,7 +1271,7 @@ void player_volume(double airplay_volume) { config.output->mute(0); // turn it off temp_fix_volume = 65536.0 * pow(10, software_volume / 2000); } - // debug(1,"Software volume set to %f on scale with a %f dB",airplay_volume,pow(10, software_volume / 2000)); + debug(1,"Software linear volume set to %f ",temp_fix_volume); pthread_mutex_lock(&vol_mutex); fix_volume = temp_fix_volume; pthread_mutex_unlock(&vol_mutex); From b74705b7b6df4bfe49e98a9b6599c47d99ccb8b0 Mon Sep 17 00:00:00 2001 From: Mke Brady Date: Sat, 7 Nov 2015 18:10:56 +0000 Subject: [PATCH 3/6] Extensively revise the volume splitting -- put hardware at the bottom of the attenuation range. --- player.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 127 insertions(+), 6 deletions(-) diff --git a/player.c b/player.c index e6842fc7..16c91815 100644 --- a/player.c +++ b/player.c @@ -1207,17 +1207,136 @@ void player_volume(double airplay_volume) { // equal increments. Since the human ear's response is roughly logarithmic, we imagine these to // be power dB, i.e. from -30dB to 0dB. - // So, if we have a hardware mixer, we will pass this on to its dB volume settings. - // Without a hardware mixer, we have to do attenuation in software, so - // here, we ask for an attenuation we will apply to the signal amplitude in software. + // We may have a hardware mixer, and if so, we will give it priority. + // If a desired volume range is given, then we will try to accommodate it from + // the top of the hardware mixer's range downwards. + + // If not desired volume range is given, we will use the native resolution of the hardware mixer, if any, + // or failing that, the software mixer. The software mixer has a range of from -96.3 dB up to 0 dB, + // corresponding to a multiplier of 1 to 65535. + + // Otherwise, we will accommodate the desired volume range in the combination of the software and hardware mixer + // Intuitively (!), it seems best to give the hardware mixer as big a role as possible, so + // we will use it's full range and then accommodate the rest of the attenuation in software. + // A problem is that we don't know whether the lowest hardware volume actually mutes the output + // so we must assume that it does, so that the colume control goes at the "bottom" of the adjustment range + // The dB range of a value from 1 to 65536 is about 96.3 dB (log10 of 65536 is 4.8164). // Since the levels correspond with amplitude, they correspond to voltage, hence voltage dB, // or 20 times the log of the ratio. Then multiplied by 100 for convenience. // Thus, we ask our vol2attn function for an appropriate dB between -96.3 and 0 dB and translate // it back to a number. + int32_t hw_min_db, hw_max_db, hw_range_db, range_to_use, min_db, max_db; // hw_range_db is a flag; if - means no mixer + int32_t sw_min_db = -9630; + int32_t sw_max_db = 0; + int32_t sw_range_db = sw_max_db - sw_min_db; + int32_t desired_range_db; // this is used as a flag + + if (config.volume_range_db) + desired_range_db = (int32_t)trunc(config.volume_range_db*100); + else + desired_range_db = 0; + + if (config.output->parameters) { + // have a hardware mixer + config.output->parameters(&audio_information); + hw_max_db = audio_information.maximum_volume_dB; + hw_min_db = audio_information.minimum_volume_dB; + hw_range_db = hw_max_db-hw_min_db; + } else { + // don't have a hardware mixer + hw_max_db = hw_min_db = hw_range_db = 0; + } + + if (desired_range_db) { + debug(1,"An attenuation range of %d is requested.",desired_range_db); + // we have a desired volume range. + if (hw_range_db) { + // we have a hardware mixer + if (hw_range_db>=desired_range_db) { + // the hardware mixer can accommodate the desired range + max_db = hw_max_db; + min_db = max_db - desired_range_db; + } else { + if ((hw_range_db+sw_range_db)mute) { + config.output->mute(1); // use real mute if it's there + } else { + hardware_attenuation = hw_min_db; + software_attenuation = sw_min_db; + debug(1,"Software mute."); + } + + } else { + if (config.output->mute) + config.output->mute(0); // unmute mute if it's there + scaled_attenuation = vol2attn(airplay_volume, max_db, min_db); + if (hw_range_db) { + // if there is a hardware mixer + if (scaled_attenuation<=hw_max_db) { + // the attenuation is so low that's it's in the hardware mixer's range + debug(1,"Attenuation all taken care of by the hardware mixer."); + hardware_attenuation = scaled_attenuation; + software_attenuation = sw_max_db - (max_db-hw_max_db); // e.g. if the hw_max_db is +4 and the max is +40, this will be -36 (all by 100, of course) + } else { + debug(1,"Attenuation taken care of by hardware and software mixer."); + hardware_attenuation = hw_max_db; // the hardware mixer is turned up full + software_attenuation = sw_max_db - (max_db-scaled_attenuation); + } + } else { + // if there is no hardware mixer, the scaled_volume is the software volume + debug(1,"Attenuation all taken care of by the software mixer."); + software_attenuation = scaled_attenuation; + } + } + + if ((config.output->volume) && (hw_range_db)) { + 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); + } + 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 %f",software_attenuation,temp_fix_volume,airplay_volume); + + + /* // get the audio parameters int32_t min,max; @@ -1272,6 +1391,8 @@ void player_volume(double airplay_volume) { temp_fix_volume = 65536.0 * pow(10, software_volume / 2000); } debug(1,"Software linear volume set to %f ",temp_fix_volume); + */ + pthread_mutex_lock(&vol_mutex); fix_volume = temp_fix_volume; pthread_mutex_unlock(&vol_mutex); @@ -1281,9 +1402,9 @@ void player_volume(double airplay_volume) { if (dv) { memset(dv, 0, 128); snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, - scaled_volume / 100.0, - min / 100.0, - max / 100.0); + scaled_attenuation / 100.0, + min_db / 100.0, + max_db / 100.0); send_ssnc_metadata('pvol', dv, strlen(dv), 1); } #endif From b5e4b258f9c6b905d0248222fbdd6f627b91cd7a Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sat, 7 Nov 2015 19:07:37 +0000 Subject: [PATCH 4/6] Hook up the mute function. --- audio_alsa.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index 4a11260d..f2afc1a5 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -47,7 +47,6 @@ static void volume(double vol); static void linear_volume(double vol); static void parameters(audio_parameters *info); static void mute(int do_mute); -static int has_mute = 0; static double set_volume; audio_output audio_alsa = { @@ -60,6 +59,7 @@ audio_output audio_alsa = { .flush = &flush, .delay = &delay, .play = &play, + .mute = NULL, // to be set later on... .volume = NULL, // to be set later on... .parameters = NULL // to be set later on... }; @@ -252,7 +252,7 @@ static int init(int argc, char **argv) { } } if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) { - has_mute = 1; + audio_alsa.mute = &mute; // insert the mute function now we know it can do muting stuff debug(1, "Has mute ability."); } return 0; @@ -461,10 +461,11 @@ static void linear_volume(double vol) { } static void mute(int do_mute) { - if (has_mute) { - if (do_mute) - snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 0); - else - snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 1); - } + if (do_mute) { + // debug(1,"Mute"); + snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 0); + } else { + // debug(1,"Unmute"); + snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 1); + } } From 3557835b687cd1fa7b3ec40e147d6a41de456e66 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sat, 7 Nov 2015 19:08:10 +0000 Subject: [PATCH 5/6] Tidy up some debug messages --- player.c | 92 ++++++++++---------------------------------------------- 1 file changed, 16 insertions(+), 76 deletions(-) diff --git a/player.c b/player.c index 16c91815..34d7ad1d 100644 --- a/player.c +++ b/player.c @@ -1206,21 +1206,20 @@ void player_volume(double airplay_volume) { // By examination, the -30 -- 0 range is linear on the slider; i.e. the slider is calibrated in 30 // equal increments. Since the human ear's response is roughly logarithmic, we imagine these to // be power dB, i.e. from -30dB to 0dB. - - + // We may have a hardware mixer, and if so, we will give it priority. // If a desired volume range is given, then we will try to accommodate it from // the top of the hardware mixer's range downwards. - // If not desired volume range is given, we will use the native resolution of the hardware mixer, if any, + // If no desired volume range is given, we will use the native resolution of the hardware mixer, if any, // or failing that, the software mixer. The software mixer has a range of from -96.3 dB up to 0 dB, // corresponding to a multiplier of 1 to 65535. // Otherwise, we will accommodate the desired volume range in the combination of the software and hardware mixer // Intuitively (!), it seems best to give the hardware mixer as big a role as possible, so - // we will use it's full range and then accommodate the rest of the attenuation in software. + // we will use its full range and then accommodate the rest of the attenuation in software. // A problem is that we don't know whether the lowest hardware volume actually mutes the output - // so we must assume that it does, so that the colume control goes at the "bottom" of the adjustment range + // so we must assume that it does, and for this reason, the volume control goes at the "bottom" of the adjustment range // The dB range of a value from 1 to 65536 is about 96.3 dB (log10 of 65536 is 4.8164). // Since the levels correspond with amplitude, they correspond to voltage, hence voltage dB, @@ -1228,12 +1227,12 @@ void player_volume(double airplay_volume) { // Thus, we ask our vol2attn function for an appropriate dB between -96.3 and 0 dB and translate // it back to a number. - int32_t hw_min_db, hw_max_db, hw_range_db, range_to_use, min_db, max_db; // hw_range_db is a flag; if - means no mixer + int32_t hw_min_db, hw_max_db, hw_range_db, range_to_use, min_db, max_db; // hw_range_db is a flag; if 0 means no mixer int32_t sw_min_db = -9630; int32_t sw_max_db = 0; int32_t sw_range_db = sw_max_db - sw_min_db; - int32_t desired_range_db; // this is used as a flag + int32_t desired_range_db; // this is used as a flag; if 0 means no desired range if (config.volume_range_db) desired_range_db = (int32_t)trunc(config.volume_range_db*100); @@ -1252,7 +1251,7 @@ void player_volume(double airplay_volume) { } if (desired_range_db) { - debug(1,"An attenuation range of %d is requested.",desired_range_db); + // debug(1,"An attenuation range of %d is requested.",desired_range_db); // we have a desired volume range. if (hw_range_db) { // we have a hardware mixer @@ -1279,7 +1278,7 @@ void player_volume(double airplay_volume) { } } else { // we do not have a desired volume range, so use the mixer's volume range, if there is one. - debug(1,"No attenuation range requested."); + // debug(1,"No attenuation range requested."); if (hw_range_db) { min_db = hw_min_db; max_db = hw_max_db; @@ -1291,18 +1290,17 @@ void player_volume(double airplay_volume) { double hardware_attenuation, software_attenuation; double scaled_attenuation = hw_min_db+sw_min_db; - - debug(1, "Hardware range, max and min: %d,%d,%d. Software range, max and min: %d,%d,%d. Min and Max %d,%d.",hw_range_db,hw_max_db,hw_min_db,sw_range_db,sw_max_db,sw_min_db,min_db,max_db); - + // now, we can map the input to the desired output volume if (airplay_volume==-144.0) { // do a mute if (config.output->mute) { config.output->mute(1); // use real mute if it's there + software_attenuation = sw_min_db; // needed for when sound is unmuted; otherwise it might be very loud. } else { hardware_attenuation = hw_min_db; software_attenuation = sw_min_db; - debug(1,"Software mute."); + // debug(1,"Software mute."); } } else { @@ -1313,85 +1311,27 @@ void player_volume(double airplay_volume) { // if there is a hardware mixer if (scaled_attenuation<=hw_max_db) { // the attenuation is so low that's it's in the hardware mixer's range - debug(1,"Attenuation all taken care of by the hardware mixer."); + // debug(1,"Attenuation all taken care of by the hardware mixer."); hardware_attenuation = scaled_attenuation; software_attenuation = sw_max_db - (max_db-hw_max_db); // e.g. if the hw_max_db is +4 and the max is +40, this will be -36 (all by 100, of course) } else { - debug(1,"Attenuation taken care of by hardware and software mixer."); + // debug(1,"Attenuation taken care of by hardware and software mixer."); hardware_attenuation = hw_max_db; // the hardware mixer is turned up full software_attenuation = sw_max_db - (max_db-scaled_attenuation); } } else { // if there is no hardware mixer, the scaled_volume is the software volume - debug(1,"Attenuation all taken care of by the software mixer."); + // debug(1,"Attenuation all taken care of by the software mixer."); software_attenuation = scaled_attenuation; } } if ((config.output->volume) && (hw_range_db)) { 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); + //debug(1,"Hardware attenuation set to %f for airplay volume of %f.",hardware_attenuation,airplay_volume); } 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 %f",software_attenuation,temp_fix_volume,airplay_volume); - - - /* - // get the audio parameters - - int32_t min,max; - - if (config.output->parameters) { - config.output->parameters(&audio_information); - max = audio_information.maximum_volume_dB; - min = audio_information.minimum_volume_dB; - } else { - max = 0; - min = -9630; - } - - double temp_fix_volume = 65536.0; - double scaled_volume; - - if (airplay_volume==-144.0) { // if we are muting - scaled_volume = min; // this is just for the metadata - if (config.output->mute) - config.output->mute(1); // use real mute if it's there - else if (config.output->volume) { - config.output->volume(min); // otherwise set the output to the lowest value - temp_fix_volume = 0; // and set the software multiplier to 0. - debug(1,"Software mute: mixer volume set to minimum: %d and software volume will be set to 0.",min); - } - } else { // not muting - - if (config.volume_range_db) { - int32_t possible_min = max - (int)trunc(config.volume_range_db*100); - if (possible_min > min) - min = possible_min; - } - - // now we have the true desired range, get the volume that is being set - scaled_volume = vol2attn(airplay_volume, max, min); - double software_volume = 0; // dB of attenuation - - if (config.output->volume) { - if (scaled_volume>=min) { - config.output->volume(scaled_volume); - debug(1,"Set Volume, no split: mixer volume set to: %f",scaled_volume); - } else { - config.output->volume(min); - software_volume = scaled_volume-min; // this is the attenuation in dB needed in software - debug(1,"Set Volume, split: mixer volume set to: %f, software volume set to: %f.",min,software_volume); - } - } else { - software_volume = scaled_volume; - } - if (config.output->mute) // if there is hardware mute... - config.output->mute(0); // turn it off - temp_fix_volume = 65536.0 * pow(10, software_volume / 2000); - } - debug(1,"Software linear volume set to %f ",temp_fix_volume); - */ + // debug(1,"Software attenuation set to %f, i.e %f out of 65,536, for airplay volume of %f",software_attenuation,temp_fix_volume,airplay_volume); pthread_mutex_lock(&vol_mutex); fix_volume = temp_fix_volume; From 3d75ba381d349e6137848915ad57680b7031472d Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sat, 7 Nov 2015 19:20:43 +0000 Subject: [PATCH 6/6] Update RELEASENOTES.md --- RELEASENOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 407ae83c..2da0ce51 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,6 +1,7 @@ Version 2.7 -- Development Version ---- **New Features** +* Extend the volume range for some DACs. Background: some of the cheaper DACS have a very small volume range (that is, the ratio of the highest to the lowest volume, expressed in decibels). In some really cheap DACs it's only around 30 dB. That means that the difference betweeen the lowest and highest volume settings isn't large enough. With the new feature, if you set the `general` `volume_range_db` to more than the hardware mixer's range, Shairport Sync will combine the hardware mixer's range with a software attenuator to give the desired range. For example, suppose you want a volume range of 70 dB and the hardware mixer offers only 30 dB, then Shairport Sync will make up ther other 40 dB with a software attenuator. The drawbacks are that, when the volume is being changed, there may be a slight delay (0.15 seconds by default) as the audio, whose volume may have been adjusted in software, propagetes through the system. The other slight possible drawback is a slightly heavier load on the processor. * Add extra debug messages to the alsa back end to diagnose strange DACs. * Add configuration file for the libao back end -- to change the buffer size and the latency offset, same is for stdout. * Add shairport-sync.exe to .gitignore.