]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Expose some of the settings used in the disable_standby_mode.
authorMike Brady <mikebrady@eircom.net>
Thu, 20 Jun 2019 09:45:27 +0000 (10:45 +0100)
committerMike Brady <mikebrady@eircom.net>
Thu, 20 Jun 2019 09:45:27 +0000 (10:45 +0100)
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.

audio_alsa.c
common.c
common.h
scripts/shairport-sync.conf

index 99fb945c78f359e5db1b76ed8c7d6a4364cb9952..69aaa0a31a77743b67a47b81699fa4443c06a203 100644 (file)
@@ -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;
               }
             }
           }
index bdbb9a877cd2905d8e095e3ec179df68dca7e878..3964816126771c8bf65ce7447c4e4d0a571abfbe 100644 (file)
--- 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;
 }
index 2a960d41501fdad30c8fb8eb57b2aae529faf4df..e9ec58c51b675e61fb02dc1c961ef8f3c14d78f9 100644 (file)
--- 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
index 59e1c3399c6632959f19f59aaac1179363b729e5..5e6089aa79715e116a5654152379a8a45ca8522b 100644 (file)
@@ -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.