1 /* libSoX statistics "effect" file.
2  *
3  * Compute various statistics on file and print them.
4  *
5  * Output is unmodified from input.
6  *
7  * July 5, 1991
8  * Copyright 1991 Lance Norskog And Sundry Contributors
9  * This source code is freely redistributable and may be used for
10  * any purpose.  This copyright notice must be maintained.
11  * Lance Norskog And Sundry Contributors are not responsible for
12  * the consequences of using this software.
13  */
14 
15 #include "sox_i.h"
16 
17 #include <string.h>
18 
19 /* Private data for stat effect */
20 typedef struct {
21   double min, max, mid;
22   double asum;
23   double sum1, sum2;            /* amplitudes */
24   double dmin, dmax;
25   double dsum1, dsum2;          /* deltas */
26   double scale;                 /* scale-factor */
27   double last;                  /* previous sample */
28   uint64_t read;               /* samples processed */
29   int volume;
30   int srms;
31   int fft;
32   unsigned long bin[4];
33   float *re_in;
34   float *re_out;
35   unsigned long fft_size;
36   unsigned long fft_offset;
37 } priv_t;
38 
39 
40 /*
41  * Process options
42  */
sox_stat_getopts(sox_effect_t * effp,int argc,char ** argv)43 static int sox_stat_getopts(sox_effect_t * effp, int argc, char **argv)
44 {
45   priv_t * stat = (priv_t *) effp->priv;
46 
47   stat->scale = SOX_SAMPLE_MAX;
48   stat->volume = 0;
49   stat->srms = 0;
50   stat->fft = 0;
51 
52   --argc, ++argv;
53   for (; argc > 0; argc--, argv++) {
54     if (!(strcmp(*argv, "-v")))
55       stat->volume = 1;
56     else if (!(strcmp(*argv, "-s"))) {
57       if (argc <= 1) {
58         lsx_fail("-s option: invalid argument");
59         return SOX_EOF;
60       }
61       argc--, argv++;              /* Move to next argument. */
62       if (!sscanf(*argv, "%lf", &stat->scale)) {
63         lsx_fail("-s option: invalid argument");
64         return SOX_EOF;
65       }
66     } else if (!(strcmp(*argv, "-rms")))
67       stat->srms = 1;
68     else if (!(strcmp(*argv, "-freq")))
69       stat->fft = 1;
70     else if (!(strcmp(*argv, "-d")))
71       stat->volume = 2;
72     else {
73       lsx_fail("Summary effect: unknown option");
74       return SOX_EOF;
75     }
76   }
77 
78   return SOX_SUCCESS;
79 }
80 
81 /*
82  * Prepare processing.
83  */
sox_stat_start(sox_effect_t * effp)84 static int sox_stat_start(sox_effect_t * effp)
85 {
86   priv_t * stat = (priv_t *) effp->priv;
87   int i;
88 
89   stat->min = stat->max = stat->mid = 0;
90   stat->asum = 0;
91   stat->sum1 = stat->sum2 = 0;
92 
93   stat->dmin = stat->dmax = 0;
94   stat->dsum1 = stat->dsum2 = 0;
95 
96   stat->last = 0;
97   stat->read = 0;
98 
99   for (i = 0; i < 4; i++)
100     stat->bin[i] = 0;
101 
102   stat->fft_size = 4096;
103   stat->re_in = stat->re_out = NULL;
104 
105   if (stat->fft) {
106     stat->fft_offset = 0;
107     stat->re_in = lsx_malloc(sizeof(float) * stat->fft_size);
108     stat->re_out = lsx_malloc(sizeof(float) * (stat->fft_size / 2 + 1));
109   }
110 
111   return SOX_SUCCESS;
112 }
113 
114 /*
115  * Print power spectrum to given stream
116  */
print_power_spectrum(unsigned samples,double rate,float * re_in,float * re_out)117 static void print_power_spectrum(unsigned samples, double rate, float *re_in, float *re_out)
118 {
119   float ffa = rate / samples;
120   unsigned i;
121 
122   lsx_power_spectrum_f((int)samples, re_in, re_out);
123   for (i = 0; i < samples / 2; i++) /* FIXME: should be <= samples / 2 */
124     fprintf(stderr, "%f  %f\n", ffa * i, re_out[i]);
125 }
126 
127 /*
128  * Processed signed long samples from ibuf to obuf.
129  * Return number of samples processed.
130  */
sox_stat_flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)131 static int sox_stat_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
132                         size_t *isamp, size_t *osamp)
133 {
134   priv_t * stat = (priv_t *) effp->priv;
135   int done, x, len = min(*isamp, *osamp);
136   short count = 0;
137 
138   if (len) {
139     if (stat->read == 0)          /* 1st sample */
140       stat->min = stat->max = stat->mid = stat->last = (*ibuf)/stat->scale;
141 
142     if (stat->fft) {
143       for (x = 0; x < len; x++) {
144         SOX_SAMPLE_LOCALS;
145         stat->re_in[stat->fft_offset++] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[x], effp->clips);
146 
147         if (stat->fft_offset >= stat->fft_size) {
148           stat->fft_offset = 0;
149           print_power_spectrum((unsigned) stat->fft_size, effp->in_signal.rate, stat->re_in, stat->re_out);
150         }
151 
152       }
153     }
154 
155     for (done = 0; done < len; done++) {
156       long lsamp = *ibuf++;
157       double delta, samp = (double)lsamp / stat->scale;
158       /* work in scaled levels for both sample and delta */
159       stat->bin[(lsamp >> 30) + 2]++;
160       *obuf++ = lsamp;
161 
162       if (stat->volume == 2) {
163           fprintf(stderr,"%08lx ",lsamp);
164           if (count++ == 5) {
165               fprintf(stderr,"\n");
166               count = 0;
167           }
168       }
169 
170       /* update min/max */
171       if (stat->min > samp)
172         stat->min = samp;
173       else if (stat->max < samp)
174         stat->max = samp;
175       stat->mid = stat->min / 2 + stat->max / 2;
176 
177       stat->sum1 += samp;
178       stat->sum2 += samp*samp;
179       stat->asum += fabs(samp);
180 
181       delta = fabs(samp - stat->last);
182       if (delta < stat->dmin)
183         stat->dmin = delta;
184       else if (delta > stat->dmax)
185         stat->dmax = delta;
186 
187       stat->dsum1 += delta;
188       stat->dsum2 += delta*delta;
189 
190       stat->last = samp;
191     }
192     stat->read += len;
193   }
194 
195   *isamp = *osamp = len;
196   /* Process all samples */
197 
198   return SOX_SUCCESS;
199 }
200 
201 /*
202  * Process tail of input samples.
203  */
sox_stat_drain(sox_effect_t * effp,sox_sample_t * obuf UNUSED,size_t * osamp)204 static int sox_stat_drain(sox_effect_t * effp, sox_sample_t *obuf UNUSED, size_t *osamp)
205 {
206   priv_t * stat = (priv_t *) effp->priv;
207 
208   /* When we run out of samples, then we need to pad buffer with
209    * zeros and then run FFT one last time to process any unprocessed
210    * samples.
211    */
212   if (stat->fft && stat->fft_offset) {
213     unsigned int x;
214 
215     for (x = stat->fft_offset; x < stat->fft_size; x++)
216       stat->re_in[x] = 0;
217 
218     print_power_spectrum((unsigned) stat->fft_size, effp->in_signal.rate, stat->re_in, stat->re_out);
219   }
220 
221   *osamp = 0;
222   return SOX_EOF;
223 }
224 
225 /*
226  * Do anything required when you stop reading samples.
227  * Don't close input file!
228  */
sox_stat_stop(sox_effect_t * effp)229 static int sox_stat_stop(sox_effect_t * effp)
230 {
231   priv_t * stat = (priv_t *) effp->priv;
232   double amp, scale, rms = 0, freq;
233   double x, ct;
234 
235   ct = stat->read;
236 
237   if (stat->srms) {  /* adjust results to units of rms */
238     double f;
239     rms = sqrt(stat->sum2/ct);
240     f = 1.0/rms;
241     stat->max *= f;
242     stat->min *= f;
243     stat->mid *= f;
244     stat->asum *= f;
245     stat->sum1 *= f;
246     stat->sum2 *= f*f;
247     stat->dmax *= f;
248     stat->dmin *= f;
249     stat->dsum1 *= f;
250     stat->dsum2 *= f*f;
251     stat->scale *= rms;
252   }
253 
254   scale = stat->scale;
255 
256   amp = -stat->min;
257   if (amp < stat->max)
258     amp = stat->max;
259 
260   /* Just print the volume adjustment */
261   if (stat->volume == 1 && amp > 0) {
262     fprintf(stderr, "%.3f\n", SOX_SAMPLE_MAX/(amp*scale));
263     return SOX_SUCCESS;
264   }
265   if (stat->volume == 2)
266     fprintf(stderr, "\n\n");
267   /* print out the info */
268   fprintf(stderr, "Samples read:      %12" PRIu64 "\n", stat->read);
269   fprintf(stderr, "Length (seconds):  %12.6f\n", (double)stat->read/effp->in_signal.rate/effp->in_signal.channels);
270   if (stat->srms)
271     fprintf(stderr, "Scaled by rms:     %12.6f\n", rms);
272   else
273     fprintf(stderr, "Scaled by:         %12.1f\n", scale);
274   fprintf(stderr, "Maximum amplitude: %12.6f\n", stat->max);
275   fprintf(stderr, "Minimum amplitude: %12.6f\n", stat->min);
276   fprintf(stderr, "Midline amplitude: %12.6f\n", stat->mid);
277   fprintf(stderr, "Mean    norm:      %12.6f\n", stat->asum/ct);
278   fprintf(stderr, "Mean    amplitude: %12.6f\n", stat->sum1/ct);
279   fprintf(stderr, "RMS     amplitude: %12.6f\n", sqrt(stat->sum2/ct));
280 
281   fprintf(stderr, "Maximum delta:     %12.6f\n", stat->dmax);
282   fprintf(stderr, "Minimum delta:     %12.6f\n", stat->dmin);
283   fprintf(stderr, "Mean    delta:     %12.6f\n", stat->dsum1/(ct-1));
284   fprintf(stderr, "RMS     delta:     %12.6f\n", sqrt(stat->dsum2/(ct-1)));
285   freq = sqrt(stat->dsum2/stat->sum2)*effp->in_signal.rate/(M_PI*2);
286   fprintf(stderr, "Rough   frequency: %12d\n", (int)freq);
287 
288   if (amp>0)
289     fprintf(stderr, "Volume adjustment: %12.3f\n", SOX_SAMPLE_MAX/(amp*scale));
290 
291   if (stat->bin[2] == 0 && stat->bin[3] == 0)
292     fprintf(stderr, "\nProbably text, not sound\n");
293   else {
294 
295     x = (float)(stat->bin[0] + stat->bin[3]) / (float)(stat->bin[1] + stat->bin[2]);
296 
297     if (x >= 3.0) {             /* use opposite encoding */
298       if (effp->in_encoding->encoding == SOX_ENCODING_UNSIGNED)
299         fprintf(stderr,"\nTry: -t raw -e signed-integer -b 8 \n");
300       else
301         fprintf(stderr,"\nTry: -t raw -e unsigned-integer -b 8 \n");
302     } else if (x <= 1.0 / 3.0)
303       ;                         /* correctly decoded */
304     else if (x >= 0.5 && x <= 2.0) { /* use ULAW */
305       if (effp->in_encoding->encoding == SOX_ENCODING_ULAW)
306         fprintf(stderr,"\nTry: -t raw -e unsigned-integer -b 8 \n");
307       else
308         fprintf(stderr,"\nTry: -t raw -e mu-law -b 8 \n");
309     } else
310       fprintf(stderr, "\nCan't guess the type\n");
311   }
312 
313   /* Release FFT memory */
314   free(stat->re_in);
315   free(stat->re_out);
316 
317   return SOX_SUCCESS;
318 
319 }
320 
321 static sox_effect_handler_t sox_stat_effect = {
322   "stat",
323   "[ -s N ] [ -rms ] [-freq] [ -v ] [ -d ]",
324   SOX_EFF_MCHAN | SOX_EFF_MODIFY,
325   sox_stat_getopts,
326   sox_stat_start,
327   sox_stat_flow,
328   sox_stat_drain,
329   sox_stat_stop,
330   NULL, sizeof(priv_t)
331 };
332 
lsx_stat_effect_fn(void)333 const sox_effect_handler_t *lsx_stat_effect_fn(void)
334 {
335   return &sox_stat_effect;
336 }
337