]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Soxr interpolation working.
authorMike Brady <mikebrady@eircom.net>
Sun, 11 Sep 2016 10:33:37 +0000 (11:33 +0100)
committerMike Brady <mikebrady@eircom.net>
Sun, 11 Sep 2016 10:33:37 +0000 (11:33 +0100)
player.c

index 0e226c1c978aee7b36f1a74c6ec197f790550953..890a04feda2f6aa2d67c4be3d3019c41d3b6baf4 100644 (file)
--- a/player.c
+++ b/player.c
@@ -403,7 +403,8 @@ static int init_decoder(int32_t fmtp[12]) {
       field            compatibleVersion       uint8_t         indicating compatible version,
                                                             value must be set to 0
 
-      field            bitDepth                uint8_t         describes the bit depth of the source
+      field            bitDepth                uint8_t         describes the bit depth of the
+     source
      PCM
      data
      (maximum
@@ -419,7 +420,8 @@ static int init_decoder(int32_t fmtp[12]) {
       field            kb                      uint8_t         currently unused tuning parameter.
                                                             value should be set to 10
 
-      field            numChannels             uint8_t         describes the channel count (1 = mono,
+      field            numChannels             uint8_t         describes the channel count (1 =
+     mono,
      2
      =
      stereo,
@@ -439,7 +441,8 @@ static int init_decoder(int32_t fmtp[12]) {
      the encoded stream.
                                                           value of 0 indicates unknown
 
-      field            avgBitRate              uint32_t        the average bit rate in bits per second
+      field            avgBitRate              uint32_t        the average bit rate in bits per
+     second
      of
      the
      Apple
@@ -1222,7 +1225,7 @@ static inline int32_t mean_32(int32_t a, int32_t b) {
 // (b) multiplies each sample by the fixedvolume (a 16-bit quantity)
 // (c) dithers the result to the output size 32/24/16 bits
 // (d) outputs the result in the approprate format
-// formats accepted so far include U8, S8, S16_LE, S24_LE and S32_LE on a little endinan machine.
+// formats accepted so far include U8, S8, S16_LE, S24_LE and S32_LE on a little endian machine.
 
 // stuff: 1 means add 1; 0 means do nothing; -1 means remove 1
 static int stuff_buffer_basic_32(int32_t *inptr, int length, enum sps_format_t l_output_format,
@@ -1323,9 +1326,97 @@ static int stuff_buffer_basic(short *inptr, int length, short *outptr, int stuff
 }
 
 #ifdef HAVE_LIBSOXR
-static int stuff_buffer_soxr_32(int32_t *inptr, int length, int32_t *outptr, int stuff,
-                                int l_output_bit_depth) {
-  return length;
+// this takes an array of signed 32-bit integers and
+// (a) uses libsoxr to
+// resample the array to have one more or one less frame, as specified in
+// stuff,
+// (b) multiplies each sample by the fixedvolume (a 16-bit quantity)
+// (c) dithers the result to the output size 32/24/16 bits
+// (d) outputs the result in the approprate format
+// formats accepted so far include U8, S8, S16_LE, S24_LE and S32_LE on a little endian machine.
+
+static int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length,
+                                enum sps_format_t l_output_format, char *outptr, int stuff,
+                                int dither) {
+  if (scratchBuffer == NULL) {
+    die("soxr scratchBuffer not initialised.");
+  }
+  int tstuff = stuff;
+  if ((stuff > 1) || (stuff < -1) || (length < 100)) {
+    // debug(1, "Stuff argument to stuff_buffer must be from -1 to +1 and length >100.");
+    tstuff = 0; // if any of these conditions hold, don't stuff anything/
+  }
+
+  if (tstuff) {
+    // debug(1,"Stuff %d.",stuff);
+    soxr_io_spec_t io_spec;
+    io_spec.itype = SOXR_INT32_I;
+    io_spec.otype = SOXR_INT32_I;
+    io_spec.scale = 1.0; // this seems to crash if not = 1.0
+    io_spec.e = NULL;
+    io_spec.flags = 0;
+
+    size_t odone;
+
+    soxr_error_t error = soxr_oneshot(length, length + tstuff, 2, /* Rates and # of chans. */
+                                      inptr, length, NULL,        /* Input. */
+                                      scratchBuffer, length + tstuff, &odone, /* Output. */
+                                      &io_spec,    /* Input, output and transfer spec. */
+                                      NULL, NULL); /* Default configuration.*/
+
+    if (error)
+      die("soxr error: %s\n", "error: %s\n", soxr_strerror(error));
+
+    if (odone > length + 1)
+      die("odone = %d!\n", odone);
+
+    int i;
+    int32_t *ip, *op;
+    ip = inptr;
+    op = scratchBuffer;
+
+    const int gpm = 5;
+    // keep the first (dpm) samples, to mitigate the Gibbs phenomenon
+    for (i = 0; i < gpm; i++) {
+      *op++ = *ip++;
+      *op++ = *ip++;
+    }
+
+    // keep the last (dpm) samples, to mitigate the Gibbs phenomenon
+    op = scratchBuffer + (length + tstuff - gpm) * sizeof(int32_t);
+    ip = inptr + (length - gpm) * sizeof(int32_t);
+    for (i = 0; i < gpm; i++) {
+      *op++ = *ip++;
+      *op++ = *ip++;
+    }
+
+    // now, do the volume, dither and formatting processing
+    ip = scratchBuffer;
+    char *l_outptr = outptr;
+    for (i = 0; i < length + tstuff; i++) {
+      process_sample(*ip++, &l_outptr, l_output_format, fix_volume, dither);
+      process_sample(*ip++, &l_outptr, l_output_format, fix_volume, dither);
+      //*op = dithered_vol(*op);
+      // op++;
+      //*op = dithered_vol(*op);
+      // op++;
+    };
+
+  } else { // the whole frame, if no stuffing
+
+    // now, do the volume, dither and formatting processing
+    int32_t *ip = inptr;
+    char *l_outptr = outptr;
+    int i;
+
+    for (i = 0; i < length; i++) {
+      process_sample(*ip++, &l_outptr, l_output_format, fix_volume, dither);
+      process_sample(*ip++, &l_outptr, l_output_format, fix_volume, dither);
+      //*op++ = dithered_vol(*ip++);
+      //*op++ = dithered_vol(*ip++);
+    };
+  }
+  return length + tstuff;
 }
 // stuff: 1 means add 1; 0 means do nothing; -1 means remove 1
 static int stuff_buffer_soxr(short *inptr, int length, short *outptr, int stuff) {
@@ -1485,6 +1576,8 @@ static void *player_thread_func(void *arg) {
 
   signed short *inbuf, *tbuf, *silence;
 
+  int32_t *sbuf;
+
   char *outbuf;
 
   int inbuflength;
@@ -1517,6 +1610,13 @@ static void *player_thread_func(void *arg) {
                   (max_frames_per_packet * output_sample_ratio + max_frame_size_change));
     if (tbuf == NULL)
       debug(1, "Failed to allocate memory for the transition buffer.");
+    sbuf = 0;
+    if (config.packet_stuffing == ST_soxr) { // needed for stuffing
+      sbuf = malloc(sizeof(int32_t) * 2 *
+                    (max_frames_per_packet * output_sample_ratio + max_frame_size_change));
+      if (sbuf == NULL)
+        debug(1, "Failed to allocate memory for the transition buffer.");
+    }
   } else {
     tbuf = 0;
   }
@@ -1856,8 +1956,9 @@ static void *player_thread_func(void *arg) {
             case ST_soxr:
               //                if (amount_to_stuff) debug(1,"Soxr stuff...");
               if (tbuf)
-                play_samples = stuff_buffer_soxr_32((int32_t *)tbuf, inbuflength, (int32_t *)outbuf,
-                                                    amount_to_stuff, output_bit_depth);
+                play_samples = stuff_buffer_soxr_32((int32_t *)tbuf, (int32_t *)sbuf, inbuflength,
+                                                    config.output_format, outbuf, amount_to_stuff,
+                                                    enable_dither);
               else
                 play_samples =
                     stuff_buffer_soxr(inbuf, inbuflength, (short *)outbuf, amount_to_stuff);
@@ -2101,6 +2202,8 @@ static void *player_thread_func(void *arg) {
   free(silence);
   if (tbuf)
     free(tbuf);
+  if (sbuf)
+    free(sbuf);
   debug(1, "Shut down audio, control and timing threads");
   itr.please_stop = 1;
   pthread_kill(rtp_audio_thread, SIGUSR1);