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;
}
}
+ /* 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) ||
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
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));
}
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");
"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)) {
(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);
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];
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;
}
}
}
// 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);
// 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;
}