]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Add some settings to the new PulseAudio backend. Limit the silent lead-in time to...
authorMike Brady <mikebrady@eircom.net>
Fri, 30 Jun 2017 19:05:48 +0000 (20:05 +0100)
committerMike Brady <mikebrady@eircom.net>
Fri, 30 Jun 2017 19:05:48 +0000 (20:05 +0100)
audio_pa.c
common.h
player.c
shairport.c

index 969886b11a4be756e5a879b6b52bed0335cdb02e..7bb096860abcb7c877ae48018d5506957444160d 100644 (file)
@@ -1,4 +1,4 @@
-// Based on
+// Based (distantly, with thanks) on
 // http://stackoverflow.com/questions/29977651/how-can-the-pulseaudio-asynchronous-library-be-used-to-play-raw-pcm-data
 
 #include "audio.h"
@@ -45,6 +45,46 @@ static int init(int argc, char **argv) {
   // default values
   config.audio_backend_buffer_desired_length = 0.35;
   config.audio_backend_latency_offset = 0;
+    
+  // get settings from settings file first, allow them to be overridden by
+  // command line options
+
+  if (config.cfg != NULL) {
+    const char *str;
+    double dvalue;
+
+    /* Get the desired buffer size setting. */
+    if (config_lookup_float(config.cfg, "pa.audio_backend_buffer_desired_length_in_seconds",
+                            &dvalue)) {
+      if ((dvalue < 0) || (dvalue > 1.5)) {
+        die("Invalid pa audio_backend_buffer_desired_length_in_seconds \"%f\". It "
+            "should be between 0 and "
+            "1.5, default is 0.35 seconds",
+            dvalue);
+      } else {
+        config.audio_backend_buffer_desired_length = dvalue;
+      }
+    }
+
+    /* Get the latency offset. */
+    if (config_lookup_float(config.cfg, "pa.audio_backend_latency_offset_in_seconds", &dvalue)) {
+      if ((dvalue < -1.0) || (dvalue > 1.5)) {
+        die("Invalid pa audio_backend_latency_offset_in_seconds \"%f\". It "
+            "should be between -1.0 and +1.5, default is 0 seconds",
+            dvalue);
+      } else {
+        config.audio_backend_latency_offset = dvalue;
+      }
+    }
+
+    /* Get the Application Name. */
+    if (config_lookup_string(config.cfg, "pa.application_name", &str)) {
+      config.pa_application_name = (char *)str;
+    }
+  }
+  
+  
+  // finish collecting settings
   
   // allocate space for the audio buffer
   audio_lmb = malloc(audio_size);
@@ -58,7 +98,10 @@ static int init(int argc, char **argv) {
   mainloop = pa_threaded_mainloop_new();
   assert(mainloop);
   mainloop_api = pa_threaded_mainloop_get_api(mainloop);
-  context = pa_context_new(mainloop_api, "Shairport Sync");
+  if (config.pa_application_name)
+    context = pa_context_new(mainloop_api, config.pa_application_name);
+  else
+    context = pa_context_new(mainloop_api, "Shairport Sync");   
   assert(context);
 
   // Set a callback so we can wait for the context to be ready
index 2cfeaba7d53f10f131a156c0606b939c15fa4303..9327cab34920a6921490fa56265937a1643135db 100644 (file)
--- a/common.h
+++ b/common.h
@@ -74,6 +74,10 @@ typedef struct {
   char *password;
   char *service_name; // the name for the shairport service, e.g. "Shairport Sync Version %v running
                       // on host %h"
+#ifdef CONFIG_PA
+  char *pa_application_name; // the name under which Shairport Sync shows up as an "Application" in the Sound Preferences in most desktop Linuxes.
+                             // Defaults to "Shairport Sync". Shairport Sync must be playing to see it.
+#endif
 #ifdef CONFIG_METADATA
   int metadata_enabled;
   char *metadata_pipename;
@@ -134,6 +138,7 @@ typedef struct {
                                               // audio backend buffer -- the DAC buffer for ALSA
   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
+  double audio_backend_silent_lead_in_time; // the length of the silence that should precede a play.
   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;
index 3cd8088e7bbf42e2317f0470bebf4a355552631b..b176a7190e1dfd52b28c2cf45f417cd7bc0ef168 100644 (file)
--- a/player.c
+++ b/player.c
@@ -874,80 +874,124 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
             int64_t filler_size = max_dac_delay; // 0.1 second -- the maximum we'll add to the DAC
 
             if (local_time_now >= conn->first_packet_time_to_play) {
+              debug(1,"Gone past starting time");
+              have_sent_prefiller_silence = 1;
+              conn->ab_buffering = 0;
+
               // we've gone past the time...
               // debug(1,"Run past the exact start time by %llu frames, with time now of %llx, fpttp
               // of %llx and dac_delay of %d and %d packets;
               // flush.",(((tn-conn->first_packet_time_to_play)*config.output_rate)>>32)+dac_delay,tn,conn->first_packet_time_to_play,dac_delay,seq_diff(ab_read,
               // ab_write));
 
+/*
               if (config.output->flush)
                 config.output->flush();
               ab_resync(conn);
               conn->first_packet_timestamp = 0;
               conn->first_packet_time_to_play = 0;
               conn->time_since_play_started = 0;
+*/
             } else {
-              // conn->first_packet_time_to_play is definitely later than local_time_now
-              if ((config.output->delay) && (have_sent_prefiller_silence != 0)) {
-                int resp = config.output->delay(&dac_delay);
-                if (resp != 0) {
-                  debug(1, "Error %d getting dac_delay in buffer_get_frame.", resp);
-                  dac_delay = 0;
-                }
-              } else
-                dac_delay = 0;
-              int64_t gross_frame_gap =
-                  ((conn->first_packet_time_to_play - local_time_now) * config.output_rate) >> 32;
-              int64_t exact_frame_gap = gross_frame_gap - dac_delay;
-              if (exact_frame_gap < 0) {
-                // we've gone past the time...
-                // debug(1,"Run a bit past the exact start time by %lld frames, with time now of
-                // %llx, fpttp of %llx and dac_delay of %d and %d packets;
-                // flush.",-exact_frame_gap,tn,conn->first_packet_time_to_play,dac_delay,seq_diff(ab_read,
-                // ab_write));
-                if (config.output->flush)
-                  config.output->flush();
-                ab_resync(conn);
-                conn->first_packet_timestamp = 0;
-                conn->first_packet_time_to_play = 0;
-              } else {
-                int64_t fs = filler_size;
-                if (fs > (max_dac_delay - dac_delay))
-                  fs = max_dac_delay - dac_delay;
-                if (fs < 0) {
-                  debug(2, "frame size (fs) < 0 with max_dac_delay of %lld and dac_delay of %ld",
-                        max_dac_delay, dac_delay);
-                  fs = 0;
-                }
-                if ((exact_frame_gap <= fs) ||
-                    (exact_frame_gap <= conn->max_frames_per_packet * 2)) {
-                  fs = exact_frame_gap;
-                  // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d,
-                  // with %d packets, ab_read is %04x, ab_write is
-                  // %04x.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read,
-                  // ab_write),ab_read,ab_write);
-                  conn->ab_buffering = 0;
-                }
-                signed short *silence;
-                // if (fs==0)
-                //  debug(2,"Zero length silence buffer needed with gross_frame_gap of %lld and
-                //  dac_delay of %lld.",gross_frame_gap,dac_delay);
-                // the fs (number of frames of silence to play) can be zero in the DAC doesn't start
-                // ouotputting frames for a while -- it could get loaded up but not start responding
-                // for many milliseconds.
-                if (fs != 0) {
-                  silence = malloc(conn->output_bytes_per_frame * fs);
-                  if (silence == NULL)
-                    debug(1, "Failed to allocate %d byte silence buffer.", fs);
-                  else {
-                    memset(silence, 0, conn->output_bytes_per_frame * fs);
-                    // debug(1,"Frames to start: %llu, DAC delay %ld, buffer: %d
-                    // packets.",exact_frame_gap,dac_delay,seq_diff(conn->ab_read, conn->ab_write,
-                    // conn->ab_read));
-                    config.output->play(silence, fs);
-                    free(silence);
-                    have_sent_prefiller_silence = 1;
+              // do some calculations
+              int64_t lead_time = conn->first_packet_time_to_play-local_time_now;
+              int64_t lead_in_time = (int64_t)(config.audio_backend_silent_lead_in_time*(int64_t)0x100000000);
+              //debug(1,"Lead in time is %llx.",lead_in_time);
+              // an audio_backend_silent_lead_in_time of less than zero means start filling ASAP 
+              if ((lead_in_time<0) || (lead_time<=lead_in_time)) {
+                // debug(1,"Continuing...");
+                if (config.output->delay) {
+                  // conn->first_packet_time_to_play is definitely later than local_time_now
+                  if (have_sent_prefiller_silence != 0) {
+                    int resp = config.output->delay(&dac_delay);
+                    if (resp != 0) {
+                      debug(1, "Error %d getting dac_delay in buffer_get_frame.", resp);
+                      dac_delay = 0;
+                    }
+                  } else {
+                    dac_delay = 0;
+                  }
+                  int64_t gross_frame_gap =
+                      ((conn->first_packet_time_to_play - local_time_now) * config.output_rate) >> 32;
+                  int64_t exact_frame_gap = gross_frame_gap - dac_delay;
+                  if (exact_frame_gap < 0) {
+                    // we've gone past the time...
+                    // debug(1,"Run a bit past the exact start time by %lld frames, with time now of
+                    // %llx, fpttp of %llx and dac_delay of %d and %d packets;
+                    // flush.",-exact_frame_gap,tn,conn->first_packet_time_to_play,dac_delay,seq_diff(ab_read,
+                    // ab_write));
+                    if (config.output->flush)
+                      config.output->flush();
+                    ab_resync(conn);
+                    conn->first_packet_timestamp = 0;
+                    conn->first_packet_time_to_play = 0;
+                  } else {
+                    int64_t fs = filler_size;
+                    if (fs > (max_dac_delay - dac_delay))
+                      fs = max_dac_delay - dac_delay;
+                    if (fs < 0) {
+                      debug(2, "frame size (fs) < 0 with max_dac_delay of %lld and dac_delay of %ld",
+                            max_dac_delay, dac_delay);
+                      fs = 0;
+                    }
+                    if ((exact_frame_gap <= fs) ||
+                        (exact_frame_gap <= conn->max_frames_per_packet * 2)) {
+                      fs = exact_frame_gap;
+                      // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d,
+                      // with %d packets, ab_read is %04x, ab_write is
+                      // %04x.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read,
+                      // ab_write),ab_read,ab_write);
+                      conn->ab_buffering = 0;
+                    }
+                    signed short *silence;
+                    // if (fs==0)
+                    //  debug(2,"Zero length silence buffer needed with gross_frame_gap of %lld and
+                    //  dac_delay of %lld.",gross_frame_gap,dac_delay);
+                    // the fs (number of frames of silence to play) can be zero in the DAC doesn't start
+                    // ouotputting frames for a while -- it could get loaded up but not start responding
+                    // for many milliseconds.
+                    if (fs != 0) {
+                      silence = malloc(conn->output_bytes_per_frame * fs);
+                      if (silence == NULL)
+                        debug(1, "Failed to allocate %d byte silence buffer.", fs);
+                      else {
+                        memset(silence, 0, conn->output_bytes_per_frame * fs);
+                        debug(1,"Frames to start: %llu, DAC delay %ld, buffer: %d packets.",exact_frame_gap,dac_delay,seq_diff(conn->ab_read, conn->ab_write, conn->ab_read));
+                        config.output->play(silence, fs);
+                        free(silence);
+                        have_sent_prefiller_silence = 1;
+                      }
+                    }
                   }
+                } else {
+                  //no delay function on back end -- just send the prefiller silence
+                  debug(1,"Back end has no delay function.");
+                  // send the appropriate prefiller here...
+                  
+                  signed short *silence;
+                  if (lead_time != 0) {
+                    int64_t frame_gap = (lead_time * config.output_rate) >> 32;
+                    debug(1,"%d frames needed.",frame_gap);
+                    while (frame_gap>0) {
+                      size_t fs = config.output_rate/10;
+                      if (fs>frame_gap)
+                        fs = frame_gap;
+                      
+                      silence = malloc(conn->output_bytes_per_frame * fs);
+                      if (silence == NULL)
+                        debug(1, "Failed to allocate %d frame silence buffer.", fs);
+                      else {
+                        debug(1, "Outputting %d frames of silence.", fs);
+                        memset(silence, 0, conn->output_bytes_per_frame * fs);
+                        config.output->play(silence, fs);
+                        free(silence);
+                        
+                      }
+                      frame_gap -= fs;
+                    }
+                  }
+                  have_sent_prefiller_silence = 1;
+                  conn->ab_buffering = 0;
                 }
               }
             }
@@ -1755,9 +1799,9 @@ static void *player_thread_func(void *arg) {
             }
 
             if (sync_error_out_of_bounds > 3) {
-             // debug(1, "New lost sync with source for %d consecutive packets -- flushing and "
-             //          "resyncing. Error: %lld.",
-             //        sync_error_out_of_bounds, sync_error);
+              // debug(1, "New lost sync with source for %d consecutive packets -- flushing and "
+              //          "resyncing. Error: %lld.",
+              //        sync_error_out_of_bounds, sync_error);
               sync_error_out_of_bounds = 0;
 
               size_t filler_length =
@@ -1919,7 +1963,8 @@ static void *player_thread_func(void *arg) {
                 sync_error_out_of_bounds++;
                 // debug(1,"Sync error out of bounds: Error: %lld; previous error: %lld; DAC: %lld;
                 // timestamp: %llx, time now
-                // %llx",sync_error,previous_sync_error,current_delay,inframe->timestamp,local_time_now);
+                //
+              %llx",sync_error,previous_sync_error,current_delay,inframe->timestamp,local_time_now);
                 if (sync_error_out_of_bounds > 3) {
                   debug(1, "Lost sync with source for %d consecutive packets -- flushing and "
                            "resyncing. Error: %lld.",
index 4c77b3b73101ba63ec60445c0d4a7667e2d4ee2b..e07e8c2b60786ce389f57cf70ac810a092258f9d 100644 (file)
@@ -372,6 +372,8 @@ int parse_options(int argc, char **argv) {
 
   config.resyncthreshold = 1.0 * fResyncthreshold / 44100;
   config.tolerance = 1.0 * fTolerance / 44100;
+  config.audio_backend_silent_lead_in_time = -1.0; // flag to indicate it has not been set
+ config.audio_backend_silent_lead_in_time = 0.0; // flag to indicate it has not been set
 
   config_setting_t *setting;
   const char *str = 0;