shithub: sox

Download patch

ref: 6781f7b4630e07f84679fd8e79044a825751aac9
parent: b3524a9a9831d9d3dd49bfa8ee1d3ad55c1ff2a2
author: cbagwell <cbagwell>
date: Mon Jul 31 23:23:04 EDT 2000

Adding first version of fft code to stat effect.

--- a/sox.1
+++ b/sox.1
@@ -113,7 +113,7 @@
 .br
 	split
 .br
-	stat [ \fIdebug\fB | \fI-v\fB ]
+	stat [ \fI-s n\fB ] [\fI-rms\fB ] [ \fI-v\fB ] [ \fI-d\fB ]
 .br
 	stretch [ \fIfactor [ window fade shift fading ]\fB
 .br
@@ -901,27 +901,44 @@
 Turn a mono sample into a stereo sample by copying
 the input channel to the left and right channels.
 .TP 10
-stat [ debug | -v ]
+stat [ \fI-s n\fB ] [\fI-rms\fB ] [ \fI-v\fB ] [ \fI-d\fB ]
 Do a statistical check on the input file,
-and print results on the standard error file.
-.B stat
-may copy the file untouched from input to output,
-if you select an output file.  
+and print results on the standard error file.  Audio data is passed
+unmodified from input to output file unless used along with the
+.B -e
+option.
+
 The "Volume Adjustment:" field in the statistics
 gives you the argument to the
 .B -v
 .I number
 which will make the sample as loud as possible without clipping. 
-There is an optional parameter
+
+The option
 .B -v
-that will print out the "Volume Adjustment:" field's value and
+will print out the "Volume Adjustment:" field's value only and
 return.  This could be of use in scripts to auto convert the
-volume.  There is an also an optional parameter
-.B debug
-that will place sox into debug mode and print out a hex dump of the
+volume.  
+
+The
+.B -s n
+option is used to scale the input data by a given factor.  The default value
+of n is the max value of a signed long variable (0x7fffffff).  Internal effects
+always work with signed long PCM data and so the value should relate to this
+fact.
+
+The
+.B -rms
+option will convert all output average values to \fIroot mean square\fR
+format.
+
+There is also an optional parameter
+.B -d
+that will print out a hex dump of the
 sound file from the internal buffer that is in 32-bit signed PCM data.
 This is mainly only of use in tracking down endian problems that
 creep in to sox on cross-platform versions.
+
 .TP 10
 stretch \fIfactor [window fade shift fading]\fB
 Time stretch file by a given factor. Change duration without affecting the pitch. 
@@ -984,16 +1001,13 @@
 enforces certain effects.
 If the two files have different sampling
 rates, the requested effect must be one of
-.B copy,
+.B polyphase, rate, 
 or
-.B rate,
-." or
-." .B resample.
+.B resample.
 If the two files have different numbers of channels,
 the 
 .B avg
-." or other channel mixing
-effect must be requested.
+or other channel mixing effect must be requested.
 .SH BUGS
 The syntax is horrific.  Thats the breaks when trying to handle all things from the command line.
 .P
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -601,6 +601,7 @@
 extern int st_stat_getopts();
 extern int st_stat_start();
 extern int st_stat_flow();
+extern int st_stat_drain();
 extern int st_stat_stop();
 
 extern int st_stretch_getopts();
@@ -731,7 +732,7 @@
 		st_null_drain, st_split_stop},
 	{"stat", ST_EFF_MCHAN | ST_EFF_REPORT | ST_EFF_RATE | ST_EFF_CHAN,
 		st_stat_getopts, st_stat_start, st_stat_flow, 
-		st_null_drain, st_stat_stop},
+		st_stat_drain, st_stat_stop},
 	{"stretch", 0,
 	        st_stretch_getopts, st_stretch_start, st_stretch_flow,
 	        st_stretch_drain, st_stretch_stop},
--- a/src/stat.c
+++ b/src/stat.c
@@ -1,4 +1,3 @@
-
 /*
  * July 5, 1991
  * Copyright 1991 Lance Norskog And Sundry Contributors
@@ -12,7 +11,9 @@
  * Sound Tools statistics "effect" file.
  *
  * Build various statistics on file and print them.
- * No output.
+ *
+ * Output is unmodified from input.
+ *
  */
 
 #include <math.h>
@@ -30,7 +31,13 @@
 	ULONG	read;		/* samples processed */
 	int	volume;
 	int	srms;
+	int	fft;
 	ULONG   bin[4];
+	double  *re;
+	double  *im;
+	ULONG   fft_bits;
+	ULONG   fft_size;
+	ULONG   fft_offset;
 } *stat_t;
 
 
@@ -47,13 +54,16 @@
 	stat->scale = MAXLONG;
 	stat->volume = 0;
 	stat->srms = 0;
+	stat->fft = 0;
+
 	while (n>0)
 	{
-		if (!(strcmp(argv[0], "-v"))) {
+		if (!(strcmp(argv[0], "-v"))) 
+		{
 			stat->volume = 1;
-			goto did1;
 		}
-		if (!(strcmp(argv[0], "-s"))) {
+		else if (!(strcmp(argv[0], "-s"))) 
+		{
 			double scale;
 
 			if (n <= 1) 
@@ -61,10 +71,6 @@
 			  st_fail("-s option: invalid argument");
 			  return (ST_EOF);
 			}
-			if (!strcmp(argv[1],"rms")) {
-				stat->srms=1;
-				goto did2;
-			}
 			if (!sscanf(argv[1], "%lf", &scale))
 			{
 			  st_fail("-s option: invalid argument");
@@ -71,21 +77,20 @@
 			  return (ST_EOF);
 			}
 			stat->scale = scale;
-			goto did2;
+
+			/* Two option argument.  Account for this */
+	                --n; ++argv;
 		}
-		if (!(strcmp(argv[0], "-rms"))) {
-			double scale;
-			if (n <= 1 || !sscanf(argv[1], "%lf", &scale))
-			{
-			  st_fail("-s option expects float argument");
-			  return(ST_EOF);
-			}
+		else if (!(strcmp(argv[0], "-rms"))) 
+		{
 			stat->srms = 1;
-			goto did2;
 		}
-		if (!(strcmp(argv[0], "debug"))) {
+		else if (!(strcmp(argv[0], "-freq"))) 
+		{
+			stat->fft = 1;
+		}
+		else if (!(strcmp(argv[0], "debug"))) {
 			stat->volume = 2;
-			goto did1;
 		}
 		else
 		{
@@ -92,8 +97,7 @@
 			st_fail("Summary effect: unknown option");
 			return(ST_EOF);
 		}
-	  did2: --n; ++argv;
-	  did1: --n; ++argv;
+	        --n; ++argv;
 	}
 	return (ST_SUCCESS);
 }
@@ -106,6 +110,7 @@
 {
 	stat_t stat = (stat_t) effp->priv;
 	int i;
+	LONG  bitmask;
 
 	stat->min = stat->max = 0;
 	stat->asum = 0;
@@ -120,6 +125,37 @@
 	for (i = 0; i < 4; i++)
 		stat->bin[i] = 0;
 
+	stat->fft_size = 4096;
+	stat->re = 0;
+	stat->im = 0;
+
+	if (stat->fft)
+	{
+	    bitmask = 0x80000000L;
+	    stat->fft_bits = 31;
+	    stat->fft_offset = 0;
+	    while (bitmask && !(stat->fft_size & bitmask))
+	    {
+		bitmask = bitmask >> 1;
+		stat->fft_bits--;
+	    }
+
+	    if (bitmask && (stat->fft_size & ~bitmask))
+	    {
+		st_fail("FFT can only use sample buffers of 2^n. Buffer size used is %ld\n",stat->fft_size);
+		return(ST_EOF);
+	    }
+
+	    stat->re = malloc(sizeof(double) * stat->fft_size);
+	    stat->im = malloc(sizeof(double) * stat->fft_size);
+
+	    if (!stat->re || !stat->im)
+	    {
+		st_fail("Unable to allocate memory for FFT buffers.\n");
+		return (ST_EOF);
+	    }
+	}
+
 	return (ST_SUCCESS);
 }
 
@@ -128,6 +164,8 @@
  * Return number of samples processed.
  */
 
+extern int FFT(short dir,long m,double *x,double *y);
+
 int st_stat_flow(effp, ibuf, obuf, isamp, osamp)
 eff_t effp;
 LONG *ibuf, *obuf;
@@ -134,8 +172,10 @@
 LONG *isamp, *osamp;
 {
 	stat_t stat = (stat_t) effp->priv;
-	int len, done;
+	int len, done, x, x1;
 	short count;
+        float magnitude;
+        float ffa;
 
 	count = 0;
 	len = ((*isamp > *osamp) ? *osamp : *isamp);
@@ -144,6 +184,38 @@
 	if (stat->read == 0)	/* 1st sample */
 		stat->min = stat->max = stat->last = (*ibuf)/stat->scale;
 
+	if (stat->fft)
+	{
+            for (x = 0; x < len; x++)
+            {
+		stat->re[stat->fft_offset] = ibuf[x];
+		stat->im[stat->fft_offset++] = 0;
+
+		if (stat->fft_offset >= stat->fft_size)
+		{
+		    stat->fft_offset = 0;
+	            FFT(1,stat->fft_bits,stat->re,stat->im);
+	            ffa = (float)effp->ininfo.rate/stat->fft_size;
+	            for (x1= 0; x1 < stat->fft_size/2; x1++)
+	            {
+	                if (x1 == 0 || x1 == 1)
+	                {
+		            magnitude = 0.0; /* no DC */
+	                }
+	                else
+	                {
+		            magnitude = sqrt(stat->re[x1]*stat->re[x1] + stat->im[x1]*stat->im[x1]);
+		            if (x1 == (stat->fft_size/2) - 1)
+		                magnitude *= 2.0;
+	                }
+	                printf("%f  %f\n",ffa*x1, magnitude);
+	            }
+		}
+
+	    }
+	}
+
+
 	for(done = 0; done < len; done++) {
 		long lsamp;
 		double samp, delta;
@@ -153,9 +225,10 @@
 		stat->bin[RIGHT(lsamp,30)+2]++;
 		*obuf++ = lsamp;
 
+
 		if (stat->volume == 2)
 		{
-		    fprintf(stderr,"%f ",samp);
+		    fprintf(stderr,"%08lx ",lsamp);
 		    if (count++ == 5)
 		    {
 			fprintf(stderr,"\n");
@@ -191,6 +264,57 @@
 }
 
 /*
+ * Process tail of input samples.
+ */
+int st_stat_drain(effp, obuf, osamp)
+eff_t effp;
+LONG *obuf;
+LONG *osamp;
+{
+    stat_t stat = (stat_t) effp->priv;
+    int x;
+    float magnitude;
+    float ffa;
+
+    /* When we run out of samples, then we need to pad buffer with
+     * zeros and then run FFT one last time.  Only perform this
+     * operation if there are at least 1 sample in the buffer.
+     */
+
+    if (stat->fft && stat->fft_offset)
+    {
+	/* When we run out of samples, then we need to pad buffer with
+	 * zeros and then run FFT one last time.  Only perform this
+	 * operation if there are at least 1 sample in the buffer.
+	 */
+        for (x = stat->fft_offset; x < stat->fft_size; x++)
+        {
+	    stat->re[x] = 0;
+	    stat->im[x] = 0;
+        }
+        FFT(1,stat->fft_bits,stat->re,stat->im);
+	ffa = (float)effp->ininfo.rate/stat->fft_size;
+	for (x=0; x < stat->fft_size/2; x++)
+	{
+	    if (x == 0 || x == 1)
+	    {
+		magnitude = 0.0; /* no DC */
+	    }
+	    else
+	    {
+		magnitude = sqrt(stat->re[x]*stat->re[x] + stat->im[x]*stat->im[x]);
+		if (x != (stat->fft_size/2) - 1)
+		    magnitude *= 2.0;
+	    }
+	    printf("%f  %f\n",ffa*x, magnitude);
+	}
+    }
+
+    *osamp = 0;
+    return (ST_SUCCESS);
+}
+
+/*
  * Do anything required when you stop reading samples.  
  * Don't close input file! 
  */
@@ -231,7 +355,7 @@
 		return (ST_SUCCESS);
 	}
 	if (stat->volume == 2) {
-		fprintf(stderr, "\n");
+		fprintf(stderr, "\n\n");
 	}
 	/* print out the info */
 	fprintf(stderr, "Samples read:      %12lu\n", stat->read);
@@ -293,6 +417,95 @@
                         fprintf (stderr, "\nCan't guess the type\n");
 		}
         }
+
+	/* Release FFT memory if allocated */
+	if (stat->re)
+	    free((void *)stat->re);
+	if (stat->im)
+	    free((void *)stat->im);
+
 	return (ST_SUCCESS);
 
+}
+
+
+/*
+   This computes an in-place complex-to-complex FFT 
+   x and y are the real and imaginary arrays of 2^(m-1) points.
+   dir =  1 gives forward transform
+   dir = -1 gives reverse transform 
+*/
+int FFT(dir,m,re,im)
+short dir;
+long m;
+double *re,*im;
+{
+   long n,i,i1,j,k,i2,l,l1,l2;
+   double c1,c2,tre,tim,t1,t2,u1,u2,z;
+
+   /* Calculate the number of points */
+   n = 1;
+   for (i=0;i<m;i++) 
+      n *= 2;
+
+   /* Do the bit reversal */
+   i2 = n >> 1;
+
+   j = 0;
+
+   for (i=0;i<n-1;i++) {
+      if (i < j) {
+         tre = re[i];
+         tim = im[i];
+         re[i] = re[j];
+         im[i] = im[j];
+         re[j] = tre;
+         im[j] = tim;
+      }
+      k = i2;
+      while (k <= j) {
+         j -= k;
+         k >>= 1;
+      }
+      j += k;
+   }
+
+   /* Compute the FFT */
+   c1 = -1.0; 
+   c2 = 0.0;
+   l2 = 1;
+   for (l=0;l<m;l++) {
+      l1 = l2;
+      l2 <<= 1;
+      u1 = 1.0; 
+      u2 = 0.0;
+      for (j=0;j<l1;j++) {
+         for (i=j;i<n;i+=l2) {
+            i1 = i + l1;
+            t1 = u1 * re[i1] - u2 * im[i1];
+            t2 = u1 * im[i1] + u2 * re[i1];
+            re[i1] = re[i] - t1; 
+            im[i1] = im[i] - t2;
+            re[i] += t1;
+            im[i] += t2;
+         }
+         z =  u1 * c1 - u2 * c2;
+         u2 = u1 * c2 + u2 * c1;
+         u1 = z;
+      }
+      c2 = sqrt((1.0 - c1) / 2.0);
+      if (dir == 1) 
+         c2 = -c2;
+      c1 = sqrt((1.0 + c1) / 2.0);
+   }
+
+   /* Scaling for forward transform */
+   if (dir == 1) {
+      for (i=0;i<n;i++) {
+         re[i] /= n;
+         im[i] /= n;
+      }
+   }
+
+   return ST_SUCCESS;
 }