shithub: sox

Download patch

ref: 61a9fffb24f31fe524a6d4c5635b7e91dce6fe69
parent: 515acca506a03ca44da889aff6d3d3c29511b198
author: robs <robs>
date: Sat Oct 18 07:40:36 EDT 2008

tidy up bend effect

--- a/ChangeLog
+++ b/ChangeLog
@@ -52,7 +52,7 @@
   o New `riaa' effect: RIAA vinyl playback EQ.  (robs)
   o New `loudness' effect: gain control with ISO 226 loudness
     compensation.  (robs)
-  o New `bend' effect; pitch bending; W.I.P., subject to change.  (robs)
+  o New `bend' effect; pitch bending.  (robs)
   o New -b option for the norm effect; can be used to fix stereo
     imbalance.  (robs)
   o New --effects-file option to read effects and arguments from
--- a/sox.1
+++ b/sox.1
@@ -1140,10 +1140,27 @@
 .SP
 See also \fBequalizer\fR for a peaking equalisation effect.
 .TP
-\fBbend\fR [\fB\-f \fIframe-rate\fR(25)] [\fB\-o \fIoversample\fR(16)] { \fIdelay\fB,\fIcents\fB,\fIduration\fR }
-Pitch bending. Work in progress\*msubject to change.
+\fBbend\fR [\fB\-f \fIframe-rate\fR(25)] [\fB\-o \fIover-sample\fR(16)] { \fIdelay\fB,\fIcents\fB,\fIduration\fR }
+Changes pitch by specified amounts at specified times.
+Each given triple: \fIdelay\fB,\fIcents\fB,\fIduration\fR specifies one bend.
+.I delay
+is the amount of time after the start of the audio stream, or the end of the previous bend, at which to start bending the pitch;
+.I cents
+is the number of cents (100 cents = 1 semitone) by which to bend the pitch, and
+.I duration
+the length of time over which the pitch will be bent.
 .SP
-For example:
+The pitch-bending algorithm utilises the Discrete Fourier Transform (DTF)
+at a particular frame rate and over-sampling rate.
+The
+.B \-f
+and
+.B \-o
+parameters may be used to adjust these parameters and thus control the
+smoothness of the changes in pitch.
+.SP
+For example, an initial tone is generated, then bent three times, yeilding
+four different notes in total:
 .EX
 	play -n synth 2.5 sin 667 gain 1 \\
 		bend .35,180,.25 .15,740,.53 0,-520,.3
@@ -1152,7 +1169,7 @@
 to remove it, use
 .B gain\ \-5
 in place of
-.BR gain\ \1 .
+.BR gain\ 1 .
 .TP
 \fBchorus \fIgain-in gain-out\fR <\fIdelay decay speed depth \fB\-s\fR\^|\^\fB\-t\fR>
 Add a chorus effect to the audio.  This can make a single vocal sound
--- a/src/bend.c
+++ b/src/bend.c
@@ -28,76 +28,14 @@
  * ANY KIND. See http://www.dspguru.com/wol.htm for more information.
  */
 
+#ifdef NDEBUG /* Enable assert always. */
+#undef NDEBUG /* Must undef above assert.h or other that might include it. */
+#endif
+
 #include "sox_i.h"
 #include "getopt.h"
+#include <assert.h>
 
-static void smbFft(float *fftBuffer, long fftFrameSize, long sign)
-/* 
- * FFT routine, (C)1996 S.M.Bernsee. Sign = -1 is FFT, 1 is iFFT (inverse)
- * Fills fftBuffer[0...2*fftFrameSize-1] with the Fourier transform of the
- * time domain data in fftBuffer[0...2*fftFrameSize-1]. The FFT array takes
- * and returns the cosine and sine parts in an interleaved manner, ie.
- * fftBuffer[0] = cosPart[0], fftBuffer[1] = sinPart[0], asf. fftFrameSize
- * must be a power of 2. It expects a complex input signal (see footnote 2),
- * ie. when working with 'common' audio signals our input signal has to be
- * passed as {in[0],0.,in[1],0.,in[2],0.,...} asf. In that case, the transform
- * of the frequencies of interest is in fftBuffer[0...fftFrameSize].
- */
-{
-  float wr, wi, arg, *p1, *p2, temp;
-  float tr, ti, ur, ui, *p1r, *p1i, *p2r, *p2i;
-  long i, bitm, j, le, le2, k;
-
-  for (i = 2; i < 2 * fftFrameSize - 2; i += 2) {
-    for (bitm = 2, j = 0; bitm < 2 * fftFrameSize; bitm <<= 1) {
-      if (i & bitm)
-        j++;
-      j <<= 1;
-    }
-    if (i < j) {
-      p1 = fftBuffer + i;
-      p2 = fftBuffer + j;
-      temp = *p1;
-      *(p1++) = *p2;
-      *(p2++) = temp;
-      temp = *p1;
-      *p1 = *p2;
-      *p2 = temp;
-    }
-  }
-  for (k = 0, le = 2; k < (long) (log((double) fftFrameSize) / log(2.) + .5);
-       k++) {
-    le <<= 1;
-    le2 = le >> 1;
-    ur = 1.0;
-    ui = 0.0;
-    arg = M_PI / (le2 >> 1);
-    wr = cos(arg);
-    wi = sign * sin(arg);
-    for (j = 0; j < le2; j += 2) {
-      p1r = fftBuffer + j;
-      p1i = p1r + 1;
-      p2r = p1r + le2;
-      p2i = p2r + 1;
-      for (i = j; i < 2 * fftFrameSize; i += le) {
-        tr = *p2r * ur - *p2i * ui;
-        ti = *p2r * ui + *p2i * ur;
-        *p2r = *p1r - tr;
-        *p2i = *p1i - ti;
-        *p1r += tr;
-        *p1i += ti;
-        p1r += le;
-        p1i += le;
-        p2r += le;
-        p2i += le;
-      }
-      tr = ur * wr - ui * wi;
-      ui = ur * wi + ui * wr;
-      ur = tr;
-    }
-  }
-}
-
 #define MAX_FRAME_LENGTH 8192
 
 typedef struct {
@@ -117,7 +55,7 @@
 
   float gInFIFO[MAX_FRAME_LENGTH];
   float gOutFIFO[MAX_FRAME_LENGTH];
-  float gFFTworksp[2 * MAX_FRAME_LENGTH];
+  double gFFTworksp[2 * MAX_FRAME_LENGTH];
   float gLastPhase[MAX_FRAME_LENGTH / 2 + 1];
   float gSumPhase[MAX_FRAME_LENGTH / 2 + 1];
   float gOutputAccum[2 * MAX_FRAME_LENGTH];
@@ -181,6 +119,7 @@
 
   int n = effp->in_signal.rate / p->frame_rate + .5;
   for (p->fftFrameSize = 2; n > 2; p->fftFrameSize <<= 1, n >>= 1);
+  assert(p->fftFrameSize <= MAX_FRAME_LENGTH);
   p->shift = 1;
   parse(effp, 0, effp->in_signal.rate); /* Re-parse now rate is known */
   p->in_pos = p->bends_pos = 0;
@@ -244,13 +183,13 @@
       }
 
       /* ***************** ANALYSIS ******************* */
-      smbFft(p->gFFTworksp, p->fftFrameSize, -1);
+      lsx_safe_cdft(2 * p->fftFrameSize, 1, p->gFFTworksp);
 
       /* this is the analysis step */
       for (k = 0; k <= fftFrameSize2; k++) {
         /* de-interlace FFT buffer */
         real = p->gFFTworksp[2 * k];
-        imag = p->gFFTworksp[2 * k + 1];
+        imag = - p->gFFTworksp[2 * k + 1];
 
         /* compute magnitude and phase */
         magn = 2. * sqrt(real * real + imag * imag);
@@ -303,13 +242,13 @@
         phase = p->gSumPhase[k];
         /* get real and imag part and re-interleave */
         p->gFFTworksp[2 * k] = magn * cos(phase);
-        p->gFFTworksp[2 * k + 1] = magn * sin(phase);
+        p->gFFTworksp[2 * k + 1] = - magn * sin(phase);
       }
 
       for (k = p->fftFrameSize + 2; k < 2 * p->fftFrameSize; k++)
         p->gFFTworksp[k] = 0.; /* zero negative frequencies */
 
-      smbFft(p->gFFTworksp, p->fftFrameSize, 1); /* do inverse transform */
+      lsx_safe_cdft(2 * p->fftFrameSize, -1, p->gFFTworksp);
 
       /* do windowing and add to output accumulator */
       for (k = 0; k < p->fftFrameSize; k++) {
@@ -355,7 +294,7 @@
 sox_effect_handler_t const *sox_bend_effect_fn(void)
 {
   static sox_effect_handler_t handler = {
-    "bend", "{delay,cents,duration}", SOX_EFF_GETOPT,
+    "bend", "[-f frame-rate(25)] [-o over-sample(16)] {delay,cents,duration}", SOX_EFF_GETOPT,
     create, start, flow, 0, stop, kill, sizeof(priv_t)
   };
   return &handler;