From: Mike Brady Date: Thu, 20 Jun 2019 09:45:27 +0000 (+0100) Subject: Expose some of the settings used in the disable_standby_mode. X-Git-Tag: 3.3.2~4^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a8faacd3fcf7868622e2f079a2d8284f50235371;p=thirdparty%2Fshairport-sync.git Expose some of the settings used in the disable_standby_mode. Specifically, firstly, the disable_standby_mode_silence_threshold, which is the amount of audio in the output device's hardware buffer. It should normally be close to the value givein in the audio_backend_buffer_desired_length_in_seconds setting. If it drops to this value, silence is added to the buffer to prevent the output device from becoming idle. Secondly, the disable_standby_mode_silence_scan_interval is the time between checks of the output device's hardware buffer. The code for generating frames of possibly-dithered silence has been simplified. --- diff --git a/audio_alsa.c b/audio_alsa.c index 99fb945c..69aaa0a3 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -1003,16 +1003,14 @@ static int init(int argc, char **argv) { config.audio_backend_buffer_interpolation_threshold_in_seconds = 0.120; // below this, basic interpolation will be used to save time. config.alsa_maximum_stall_time = 0.200; // 200 milliseconds -- if it takes longer, it's a problem - config.audio_backend_silence_threshold = + config.disable_standby_mode_silence_threshold = 0.040; // start sending silent frames if the delay goes below this time - config.audio_backend_silence_scan_interval = 0.004; // check silence threshold this often + config.disable_standby_mode_silence_scan_interval = 0.004; // check silence threshold this often stall_monitor_error_threshold = (uint64_t)1000000 * config.alsa_maximum_stall_time; // stall time max to microseconds; stall_monitor_error_threshold = (stall_monitor_error_threshold << 32) / 1000000; // now in fp form - debug(1, - "stall_monitor_error_threshold is 0x%" PRIx64 ", with alsa_maximum_stall_time of %f sec.", - stall_monitor_error_threshold, config.alsa_maximum_stall_time); + debug(1, "alsa: alsa_maximum_stall_time of %f sec.", config.alsa_maximum_stall_time); stall_monitor_start_time = 0; stall_monitor_frame_count = 0; @@ -1233,6 +1231,29 @@ static int init(int argc, char **argv) { } } + /* Get the optional disable_standby_mode_silence_threshold setting. */ + if (config_lookup_float(config.cfg, "alsa.disable_standby_mode_silence_threshold", &dvalue)) { + if (dvalue < 0.0) { + warn("Invalid alsa disable_standby_mode_silence_threshold setting \"%f\". It " + "must be greater than 0. Default is \"%f\". No setting is made.", + dvalue, config.disable_standby_mode_silence_threshold); + } else { + config.disable_standby_mode_silence_threshold = dvalue; + } + } + + /* Get the optional disable_standby_mode_silence_scan_interval setting. */ + if (config_lookup_float(config.cfg, "alsa.disable_standby_mode_silence_scan_interval", + &dvalue)) { + if (dvalue < 0.0) { + warn("Invalid alsa disable_standby_mode_silence_scan_interval setting \"%f\". It " + "must be greater than 0. Default is \"%f\". No setting is made.", + dvalue, config.disable_standby_mode_silence_scan_interval); + } else { + config.disable_standby_mode_silence_scan_interval = dvalue; + } + } + /* Get the optional disable_standby_mode setting. */ if (config_lookup_string(config.cfg, "alsa.disable_standby_mode", &str)) { if ((strcasecmp(str, "no") == 0) || (strcasecmp(str, "off") == 0) || @@ -1276,6 +1297,10 @@ static int init(int argc, char **argv) { config.disable_standby_mode == disable_standby_off ? "never" : config.disable_standby_mode == disable_standby_always ? "always" : "auto"); + debug(1, "alsa: disable_standby_mode_silence_threshold is %f seconds.", + config.disable_standby_mode_silence_threshold); + debug(1, "alsa: disable_standby_mode_silence_scan_interval is %f seconds.", + config.disable_standby_mode_silence_scan_interval); } optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour @@ -1643,7 +1668,7 @@ int do_play(void *buf, int samples) { measurement_data_is_valid = 0; if (ret == -EPIPE) { /* underrun */ debug(1, "alsa: underrun while writing %d samples to alsa device.", samples); - int tret = snd_pcm_recover(alsa_handle, ret, debuglev > 0 ? 0 : 1); + int tret = snd_pcm_recover(alsa_handle, ret, 1); if (tret < 0) { warn("alsa: can't recover from SND_PCM_STATE_XRUN: %s.", snd_strerror(tret)); } @@ -1898,9 +1923,9 @@ void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { int frame_count = 0; int error_count = 0; - int error_threshold_exceeded = 0; + int error_detected = 0; int okb = -1; - while (error_threshold_exceeded == + while (error_detected == 0) { // if too many play errors occur early on, we will turn off the disable stanby mode if (okb != config.keep_dac_busy) { debug(2, "keep_dac_busy is now \"%s\"", config.keep_dac_busy == 0 ? "no" : "yes"); @@ -1911,7 +1936,7 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { "do_alsa_device_init_if_needed."); do_alsa_device_init_if_needed(); } - int sleep_time_ms = (int)(config.audio_backend_silence_scan_interval * 1000); + int sleep_time_ms = (int)(config.disable_standby_mode_silence_scan_interval * 1000); pthread_cleanup_debug_mutex_lock(&alsa_mutex, 200000, 0); // check possible state transitions here if ((alsa_backend_state == abm_disconnected) && (config.keep_dac_busy != 0)) { @@ -1950,25 +1975,19 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { (char *)errorstring); } long buffer_size_threshold = - (long)(config.audio_backend_silence_threshold * config.output_rate); + (long)(config.disable_standby_mode_silence_threshold * config.output_rate); + size_t size_of_silence_buffer; if (buffer_size < buffer_size_threshold) { uint64_t sleep_time_in_fp = sleep_time_ms; sleep_time_in_fp = sleep_time_in_fp << 32; sleep_time_in_fp = sleep_time_in_fp / 1000; - // debug(1,"alsa: sleep_time: %d ms or 0x%" PRIx64 " in fp - // form.",sleep_time_ms,sleep_time_in_fp); int frames_of_silence = - // (config.output_rate * - // sleep_time_ms * 2) / 1000; int frames_of_silence = 1024; - size_t size_of_silence_buffer = frames_of_silence * frame_size; - // debug(1, "alsa: alsa_buffer_monitor_thread_code -- silence buffer - // length: %u bytes.", - // size_of_silence_buffer); + size_of_silence_buffer = frames_of_silence * frame_size; void *silence = malloc(size_of_silence_buffer); if (silence == NULL) { - debug(1, "alsa: alsa_buffer_monitor_thread_code -- failed to " - "allocate memory for a " - "silent frame buffer."); + warn("disable_standby_mode has been turned off because a memory allocation error " + "occurred."); + error_detected = 1; } else { int ret; pthread_cleanup_push(malloc_cleanup, silence); @@ -1980,12 +1999,9 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { generate_zero_frames(silence, frames_of_silence, config.output_format, use_dither, // i.e. with dither dither_random_number_store); - // debug(1,"Play %d frames of silence with most_recent_write_time of - // %" PRIx64 ".", - // frames_of_silence,most_recent_write_time); ret = do_play(silence, frames_of_silence); frame_count++; - pthread_cleanup_pop(1); + pthread_cleanup_pop(1); // free malloced buffer if (ret < 0) { error_count++; char errorstring[1024]; @@ -1997,7 +2013,7 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { warn("disable_standby_mode has been turned off because too many underruns " "occurred. Is Shairport Sync outputting to a virtual device or running in a " "virtual machine?"); - error_threshold_exceeded = 1; + error_detected = 1; } } } diff --git a/common.c b/common.c index bdbb9a87..39648161 100644 --- a/common.c +++ b/common.c @@ -1368,61 +1368,62 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma // return the last random number used // assuming the buffer has been assigned + // add a TPDF dither -- see + // http://educypedia.karadimov.info/library/DitherExplained.pdf + // and the discussion around https://www.hydrogenaud.io/forums/index.php?showtopic=16963&st=25 + + // I think, for a 32 --> 16 bits, the range of + // random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from + // -32768 to +32767 + + // Actually, what would be generated here is from -65535 to 65535, i.e. one less on the limits. + + // See the original paper at + // http://www.ece.rochester.edu/courses/ECE472/resources/Papers/Lipshitz_1992.pdf + // by Lipshitz, Wannamaker and Vanderkooy, 1992. + + int64_t dither_mask = 0; + switch (format) { + case SPS_FORMAT_S32: + case SPS_FORMAT_S32_LE: + case SPS_FORMAT_S32_BE: + dither_mask = (int64_t)1 << (64 - 32); + break; + case SPS_FORMAT_S24: + case SPS_FORMAT_S24_LE: + case SPS_FORMAT_S24_BE: + case SPS_FORMAT_S24_3LE: + case SPS_FORMAT_S24_3BE: + dither_mask = (int64_t)1 << (64 - 24); + break; + case SPS_FORMAT_S16: + case SPS_FORMAT_S16_LE: + case SPS_FORMAT_S16_BE: + dither_mask = (int64_t)1 << (64 - 16); + break; + case SPS_FORMAT_S8: + case SPS_FORMAT_U8: + dither_mask = (int64_t)1 << (64 - 8); + break; + case SPS_FORMAT_UNKNOWN: + die("Unexpected SPS_FORMAT_UNKNOWN while calculating dither mask."); + break; + case SPS_FORMAT_AUTO: + die("Unexpected SPS_FORMAT_AUTO while calculating dither mask."); + break; + case SPS_FORMAT_INVALID: + die("Unexpected SPS_FORMAT_INVALID while calculating dither mask."); + break; + } + dither_mask -= 1; + int64_t previous_random_number = random_number_in; char *p = outp; size_t sample_number; for (sample_number = 0; sample_number < number_of_frames * 2; sample_number++) { int64_t hyper_sample = 0; - // add a TPDF dither -- see - // http://educypedia.karadimov.info/library/DitherExplained.pdf - // and the discussion around https://www.hydrogenaud.io/forums/index.php?showtopic=16963&st=25 - - // I think, for a 32 --> 16 bits, the range of - // random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from - // -32768 to +32767 - - // Actually, what would be generated here is from -65535 to 65535, i.e. one less on the limits. - // See the original paper at - // http://www.ece.rochester.edu/courses/ECE472/resources/Papers/Lipshitz_1992.pdf - // by Lipshitz, Wannamaker and Vanderkooy, 1992. - - int64_t dither_mask = 0; - switch (format) { - case SPS_FORMAT_S32: - case SPS_FORMAT_S32_LE: - case SPS_FORMAT_S32_BE: - dither_mask = (int64_t)1 << (64 - 32); - break; - case SPS_FORMAT_S24: - case SPS_FORMAT_S24_LE: - case SPS_FORMAT_S24_BE: - case SPS_FORMAT_S24_3LE: - case SPS_FORMAT_S24_3BE: - dither_mask = (int64_t)1 << (64 - 24); - break; - case SPS_FORMAT_S16: - case SPS_FORMAT_S16_LE: - case SPS_FORMAT_S16_BE: - dither_mask = (int64_t)1 << (64 - 16); - break; - case SPS_FORMAT_S8: - case SPS_FORMAT_U8: - dither_mask = (int64_t)1 << (64 - 8); - break; - case SPS_FORMAT_UNKNOWN: - die("Unexpected SPS_FORMAT_UNKNOWN while calculating dither mask."); - break; - case SPS_FORMAT_AUTO: - die("Unexpected SPS_FORMAT_AUTO while calculating dither mask."); - break; - case SPS_FORMAT_INVALID: - die("Unexpected SPS_FORMAT_INVALID while calculating dither mask."); - break; - } - dither_mask -= 1; - // int64_t r = r64i(); int64_t r = ranarray64i(); int64_t tpdf = (r & dither_mask) - (previous_random_number & dither_mask); @@ -1434,126 +1435,87 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma // move the result to the desired position in the int64_t char *op = p; - int result; // this is the length of the sample + int sample_length; // this is the length of the sample - uint8_t byt; switch (format) { case SPS_FORMAT_S32: hyper_sample >>= (64 - 32); *(int32_t *)op = hyper_sample; - result = 4; + sample_length = 4; break; case SPS_FORMAT_S32_LE: - hyper_sample >>= (64 - 32); - byt = (uint8_t)hyper_sample; - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 24); - *op++ = byt; - result = 4; + *op++ = (uint8_t)(hyper_sample >> (64 - 32)); // 32 bits, ls byte + *op++ = (uint8_t)(hyper_sample >> (64 - 32 + 8)); // 32 bits, less significant middle byte + *op++ = (uint8_t)(hyper_sample >> (64 - 32 + 16)); // 32 bits, more significant middle byte + *op = (uint8_t)(hyper_sample >> (64 - 32 + 24)); // 32 bits, ms byte + sample_length = 4; break; case SPS_FORMAT_S32_BE: - hyper_sample >>= (64 - 32); - byt = (uint8_t)(hyper_sample >> 24); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)hyper_sample; - *op++ = byt; - result = 4; + *op++ = (uint8_t)(hyper_sample >> (64 - 32 + 24)); // 32 bits, ms byte + *op++ = (uint8_t)(hyper_sample >> (64 - 32 + 16)); // 32 bits, more significant middle byte + *op++ = (uint8_t)(hyper_sample >> (64 - 32 + 8)); // 32 bits, less significant middle byte + *op = (uint8_t)(hyper_sample >> (64 - 32)); // 32 bits, ls byte + sample_length = 4; break; case SPS_FORMAT_S24_3LE: - hyper_sample >>= (64 - 24); - byt = (uint8_t)hyper_sample; - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - result = 3; + *op++ = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte + *op = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte + sample_length = 3; break; case SPS_FORMAT_S24_3BE: - hyper_sample >>= (64 - 24); - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)hyper_sample; - *op++ = byt; - result = 3; + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte + *op = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte + sample_length = 3; break; case SPS_FORMAT_S24: hyper_sample >>= (64 - 24); *(int32_t *)op = hyper_sample; - result = 4; + sample_length = 4; break; case SPS_FORMAT_S24_LE: - hyper_sample >>= (64 - 24); - byt = (uint8_t)hyper_sample; - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - *op++ = 0; - result = 4; + *op++ = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte + *op = 0; + sample_length = 4; break; case SPS_FORMAT_S24_BE: - hyper_sample >>= (64 - 24); *op++ = 0; - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)hyper_sample; - *op++ = byt; - result = 4; + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte + *op = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte + sample_length = 4; break; case SPS_FORMAT_S16_LE: - hyper_sample >>= (64 - 16); - byt = (uint8_t)hyper_sample; - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - result = 2; + *op++ = (uint8_t)(hyper_sample >> (64 - 16)); + *op++ = (uint8_t)(hyper_sample >> (64 - 16 + 8)); // 16 bits, ms byte + sample_length = 2; break; case SPS_FORMAT_S16_BE: - hyper_sample >>= (64 - 16); - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)hyper_sample; - *op++ = byt; - result = 2; + *op++ = (uint8_t)(hyper_sample >> (64 - 16 + 8)); // 16 bits, ms byte + *op = (uint8_t)(hyper_sample >> (64 - 16)); + sample_length = 2; break; case SPS_FORMAT_S16: - hyper_sample >>= (64 - 16); - *(int16_t *)op = (int16_t)hyper_sample; - result = 2; + *(int16_t *)op = (int16_t)(hyper_sample >> (64 - 16)); + sample_length = 2; break; case SPS_FORMAT_S8: - hyper_sample >>= (int8_t)(64 - 8); - *op = hyper_sample; - result = 1; + *op = (int8_t)(hyper_sample >> (64 - 8)); + sample_length = 1; break; case SPS_FORMAT_U8: - hyper_sample >>= (uint8_t)(64 - 8); - hyper_sample += 128; - *op = hyper_sample; - result = 1; + *op = 128 + (uint8_t)(hyper_sample >> (64 - 8)); + sample_length = 1; break; default: - result = 0; // stop a compiler warning + sample_length = 0; // stop a compiler warning die("Unexpected SPS_FORMAT_* with index %d while outputting silence", format); } - p += result; + p += sample_length; previous_random_number = r; } - // hack - // memset(outp,0,number_of_frames * 4); return previous_random_number; } diff --git a/common.h b/common.h index 2a960d41..e9ec58c5 100644 --- a/common.h +++ b/common.h @@ -201,8 +201,9 @@ typedef struct { double audio_backend_buffer_interpolation_threshold_in_seconds; // below this, soxr interpolation // will not occur -- it'll be // basic interpolation instead. - double audio_backend_silence_threshold; // below this, silence will be added to the output buffer - double audio_backend_silence_scan_interval; // check the threshold this often + double disable_standby_mode_silence_threshold; // below this, silence will be added to the output + // buffer + double disable_standby_mode_silence_scan_interval; // check the threshold this often double audio_backend_latency_offset; // this will be the offset in seconds to compensate for any // fixed latency there might be in the audio path diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 59e1c339..5e6089aa 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -93,6 +93,8 @@ alsa = // maximum_stall_time = 0.200; // Use this optional advanced setting to control how long to wait for data to be consumed by the output device before considering it an error. It should never approach 200 ms. // use_precision_timing = "auto"; // Use this optional advanced setting to control how Shairport Sync gathers timing information. When set to "auto", if the output device is a real hardware device, precision timing will be used. Choose "no" for more compatible standard timing, choose "yes" to force the use of precision timing, which may cause problems. // disable_standby_mode = "never"; // This setting prevents the DAC from entering the standby mode. Some DACs make small "popping" noises when they go in and out of standby mode. Settings can be: "always", "auto" or "never". Default is "never", but only for backwards compatibility. The "auto" setting prevents entry to standby mode while Shairport Sync is in the "active" mode. You can use "yes" instead of "always" and "no" instead of "never". +// disable_standby_mode_silence_threshold = 0.040; // Use this optional advanced setting to control how little audio should remain in the output buffer before the disable_standby code should start sending silence to the output device. +// disable_standby_mode_silence_scan_interval = 0.004; // Use this optional advanced setting to control how often the amount of audio remaining in the output buffer should be checked. }; // Parameters for the "sndio" audio back end. All are optional.