shithub: sox

Download patch

ref: 1faf05aacdd69ad8071ad6cb0e47c1536c7479c5
parent: 3cda6b3e056b68f4d46af3c7e25e226bd6b70592
author: robs <robs>
date: Mon Mar 23 15:59:19 EDT 2009

add equal power cross-fade

--- a/src/splice.c
+++ b/src/splice.c
@@ -18,12 +18,12 @@
 
 #include "sox_i.h"
 
-static float difference(const float * a, const float * b, size_t length)
+static double difference(const sox_sample_t * a, const sox_sample_t * b, size_t length)
 {
-  float diff = 0;
+  double diff = 0;
   size_t i = 0;
 
-  #define _ diff += sqr(a[i] - b[i]), ++i; /* Loop optimisation */
+  #define _ diff += sqr((double)a[i] - b[i]), ++i; /* Loop optimisation */
   do {_ _ _ _ _ _ _ _} while (i < length); /* N.B. length ≡ 0 (mod 8) */
   #undef _
   return diff;
@@ -30,11 +30,11 @@
 }
 
 /* Find where the two segments are most alike over the overlap period. */
-static size_t best_overlap_position(float const * f1, float const * f2,
+static size_t best_overlap_position(sox_sample_t const * f1, sox_sample_t const * f2,
     size_t overlap, size_t search, size_t channels)
 {
   size_t i, best_pos = 0;
-  float diff, least_diff = difference(f2, f1, channels * overlap);
+  double diff, least_diff = difference(f2, f1, channels * overlap);
 
   for (i = 1; i < search; ++i) { /* linear search */
     diff = difference(f2 + channels * i, f1, channels * overlap);
@@ -44,30 +44,8 @@
   return best_pos;
 }
 
-static void splice(const float * in1, const float * in2,
-    float * output, size_t overlap, size_t channels)
-{
-  size_t i, j, k = 0;
-  float fade_step = 1.0f / (float) overlap;
-
-  for (i = 0; i < overlap; ++i) {
-    float fade_in  = fade_step * (float) i;
-    float fade_out = 1.0f - fade_in;
-    for (j = 0; j < channels; ++j, ++k)
-      output[k] = in1[k] * fade_out + in2[k] * fade_in;
-  }
-}
-
-static size_t do_splice(float * f, size_t overlap, size_t search, size_t channels)
-{
-  size_t offset = search? best_overlap_position(
-      f, f + overlap * channels, overlap, search, channels) : 0;
-  splice(f, f + (overlap + offset) * channels,
-      f + (overlap + offset) * channels, overlap, channels);
-  return overlap + offset;
-}
-
 typedef struct {
+  sox_bool uncorrelated;
   unsigned nsplices;     /* Number of splices requested */
   struct {
     char * str;          /* Command-line argument to parse for this splice */
@@ -80,10 +58,49 @@
   unsigned splices_pos;  /* Number of splices completed so far */
   size_t buffer_pos; /* Number of samples through the current splice */
   size_t max_buffer_size;
-  float * buffer;
+  sox_sample_t * buffer;
   unsigned state;
 } priv_t;
 
+static void splice(sox_effect_t * effp, const sox_sample_t * in1, const
+    sox_sample_t * in2, sox_sample_t * output, size_t overlap, size_t channels)
+{
+  priv_t * p = (priv_t *)effp->priv;
+  size_t i, j, k = 0;
+
+  if (p->uncorrelated) { /* Fade for constant RMS level (`power') */
+    double fade_step = M_PI_2 / overlap;
+    for (i = 0; i < overlap; ++i) {
+      double fade_in  = sin(i * fade_step);
+      double fade_out = cos(i * fade_step);
+      for (j = 0; j < channels; ++j, ++k) {
+        double d = in1[k] * fade_out + in2[k] * fade_in;
+        output[k] = SOX_ROUND_CLIP_COUNT(d, effp->clips); /* Might clip */
+      }
+    }
+  }
+  else {                 /* Fade for constant peak level (`gain') */
+    double fade_step = 1. / overlap;
+    for (i = 0; i < overlap; ++i) {
+      double fade_in  = fade_step * i;
+      double fade_out = 1 - fade_in;
+      for (j = 0; j < channels; ++j, ++k) {
+        double d = in1[k] * fade_out + in2[k] * fade_in;
+        output[k] = SOX_ROUND_CLIP_COUNT(d, effp->clips); /* Should not clip */
+      }
+    }
+  }
+}
+
+static size_t do_splice(sox_effect_t * effp, sox_sample_t * f, size_t overlap, size_t search, size_t channels)
+{
+  size_t offset = search? best_overlap_position(
+      f, f + overlap * channels, overlap, search, channels) : 0;
+  splice(effp, f, f + (overlap + offset) * channels,
+      f + (overlap + offset) * channels, overlap, channels);
+  return overlap + offset;
+}
+
 static int parse(sox_effect_t * effp, char * * argv, sox_rate_t rate)
 {
   priv_t * p = (priv_t *)effp->priv;
@@ -129,6 +146,8 @@
 {
   priv_t * p = (priv_t *)effp->priv;
   --argc, ++argv;
+  if (argc && !strcmp(*argv, "-u"))
+    --argc, ++argv, p->uncorrelated = sox_true;
   p->splices = lsx_calloc(p->nsplices = argc, sizeof(*p->splices));
   return parse(effp, argv, 1e5); /* No rate yet; parse with dummy */
 }
@@ -143,8 +162,11 @@
   p->in_pos = p->buffer_pos = p->splices_pos = 0;
   p->state = p->splices_pos != p->nsplices && p->in_pos == p->splices[p->splices_pos].start;
   for (i = 0; i < p->nsplices; ++i)
-    if (p->splices[i].overlap)
+    if (p->splices[i].overlap) {
+      if (p->uncorrelated && effp->in_signal.mult)
+        *effp->in_signal.mult *= pow(.5, .5);
       return SOX_SUCCESS;
+    }
   return SOX_EFF_NULL;
 }
 
@@ -153,7 +175,6 @@
 {
   priv_t * p = (priv_t *)effp->priv;
   size_t c, idone = 0, odone = 0;
-  SOX_SAMPLE_LOCALS;
   *isamp /= effp->in_signal.channels;
   *osamp /= effp->in_signal.channels;
 
@@ -176,7 +197,7 @@
       size_t buffer_size = (2 * p->splices[p->splices_pos].overlap + p->splices[p->splices_pos].search) * effp->in_signal.channels;
       for (; idone < *isamp; ++idone, ++p->in_pos) {
         if (p->buffer_pos == buffer_size) {
-          p->buffer_pos = do_splice(p->buffer,
+          p->buffer_pos = do_splice(effp, p->buffer,
               p->splices[p->splices_pos].overlap,
               p->splices[p->splices_pos].search,
               (size_t)effp->in_signal.channels) * effp->in_signal.channels;
@@ -185,7 +206,7 @@
           break;
         }
         for (c = 0; c < effp->in_signal.channels; ++c)
-          p->buffer[p->buffer_pos++] = SOX_SAMPLE_TO_FLOAT_32BIT(*ibuf++, effp->clips);
+          p->buffer[p->buffer_pos++] = *ibuf++;
       }
       break;
     }
@@ -201,7 +222,7 @@
           goto copying;
         }
         for (c = 0; c < effp->in_signal.channels; ++c)
-          *obuf++ = SOX_FLOAT_32BIT_TO_SAMPLE(p->buffer[p->buffer_pos++], effp->clips);
+          *obuf++ = p->buffer[p->buffer_pos++];
       }
       break;
     }
@@ -240,7 +261,13 @@
 sox_effect_handler_t const * lsx_splice_effect_fn(void)
 {
   static sox_effect_handler_t handler = {
-    "splice", "{position[,excess[,leeway]]}", SOX_EFF_MCHAN|SOX_EFF_LENGTH,
+    "splice", "[-u] {position[,excess[,leeway]]}"
+    "\n  (default)  Correlated audio: fade for constant peak"
+    "\n  -u         Uncorrelated audio (e.g. cross-fade): fade for constant RMS"
+    "\n  position   The length of part 1 (including the excess)"
+    "\n  excess     At the end of part 1 & the start of part2 (default 0.005)"
+    "\n  leeway     Before part2 (default 0.005; set to 0 for cross-fade)",
+    SOX_EFF_MCHAN|SOX_EFF_LENGTH,
     create, start, flow, drain, stop, kill, sizeof(priv_t)
   };
   return &handler;