/* libSoX statistics "effect" file. * * Compute various statistics on file and print them. * * Output is unmodified from input. * * July 5, 1991 * Copyright 1991 Lance Norskog And Sundry Contributors * This source code is freely redistributable and may be used for * any purpose. This copyright notice must be maintained. * Lance Norskog And Sundry Contributors are not responsible for * the consequences of using this software. */ #include "sox_i.h" #include /* Private data for stat effect */ typedef struct { double min, max, mid; double asum; double sum1, sum2; /* amplitudes */ double dmin, dmax; double dsum1, dsum2; /* deltas */ double scale; /* scale-factor */ double last; /* previous sample */ uint64_t read; /* samples processed */ int volume; int srms; int fft; unsigned long bin[4]; float *re_in; float *re_out; unsigned long fft_size; unsigned long fft_offset; } priv_t; /* * Process options */ static int sox_stat_getopts(sox_effect_t * effp, int argc, char **argv) { priv_t * stat = (priv_t *) effp->priv; stat->scale = SOX_SAMPLE_MAX; stat->volume = 0; stat->srms = 0; stat->fft = 0; --argc, ++argv; for (; argc > 0; argc--, argv++) { if (!(strcmp(*argv, "-v"))) stat->volume = 1; else if (!(strcmp(*argv, "-s"))) { if (argc <= 1) { lsx_fail("-s option: invalid argument"); return SOX_EOF; } argc--, argv++; /* Move to next argument. */ if (!sscanf(*argv, "%lf", &stat->scale)) { lsx_fail("-s option: invalid argument"); return SOX_EOF; } } else if (!(strcmp(*argv, "-rms"))) stat->srms = 1; else if (!(strcmp(*argv, "-freq"))) stat->fft = 1; else if (!(strcmp(*argv, "-d"))) stat->volume = 2; else { lsx_fail("Summary effect: unknown option"); return SOX_EOF; } } return SOX_SUCCESS; } /* * Prepare processing. */ static int sox_stat_start(sox_effect_t * effp) { priv_t * stat = (priv_t *) effp->priv; int i; stat->min = stat->max = stat->mid = 0; stat->asum = 0; stat->sum1 = stat->sum2 = 0; stat->dmin = stat->dmax = 0; stat->dsum1 = stat->dsum2 = 0; stat->last = 0; stat->read = 0; for (i = 0; i < 4; i++) stat->bin[i] = 0; stat->fft_size = 4096; stat->re_in = stat->re_out = NULL; if (stat->fft) { stat->fft_offset = 0; stat->re_in = lsx_malloc(sizeof(float) * stat->fft_size); stat->re_out = lsx_malloc(sizeof(float) * (stat->fft_size / 2 + 1)); } return SOX_SUCCESS; } /* * Print power spectrum to given stream */ static void print_power_spectrum(unsigned samples, double rate, float *re_in, float *re_out) { float ffa = rate / samples; unsigned i; lsx_power_spectrum_f((int)samples, re_in, re_out); for (i = 0; i < samples / 2; i++) /* FIXME: should be <= samples / 2 */ fprintf(stderr, "%f %f\n", ffa * i, re_out[i]); } /* * Processed signed long samples from ibuf to obuf. * Return number of samples processed. */ static int sox_stat_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf, size_t *isamp, size_t *osamp) { priv_t * stat = (priv_t *) effp->priv; int done, x, len = min(*isamp, *osamp); short count = 0; if (len) { if (stat->read == 0) /* 1st sample */ stat->min = stat->max = stat->mid = stat->last = (*ibuf)/stat->scale; if (stat->fft) { for (x = 0; x < len; x++) { SOX_SAMPLE_LOCALS; stat->re_in[stat->fft_offset++] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[x], effp->clips); if (stat->fft_offset >= stat->fft_size) { stat->fft_offset = 0; print_power_spectrum((unsigned) stat->fft_size, effp->in_signal.rate, stat->re_in, stat->re_out); } } } for (done = 0; done < len; done++) { long lsamp = *ibuf++; double delta, samp = (double)lsamp / stat->scale; /* work in scaled levels for both sample and delta */ stat->bin[(lsamp >> 30) + 2]++; *obuf++ = lsamp; if (stat->volume == 2) { fprintf(stderr,"%08lx ",lsamp); if (count++ == 5) { fprintf(stderr,"\n"); count = 0; } } /* update min/max */ if (stat->min > samp) stat->min = samp; else if (stat->max < samp) stat->max = samp; stat->mid = stat->min / 2 + stat->max / 2; stat->sum1 += samp; stat->sum2 += samp*samp; stat->asum += fabs(samp); delta = fabs(samp - stat->last); if (delta < stat->dmin) stat->dmin = delta; else if (delta > stat->dmax) stat->dmax = delta; stat->dsum1 += delta; stat->dsum2 += delta*delta; stat->last = samp; } stat->read += len; } *isamp = *osamp = len; /* Process all samples */ return SOX_SUCCESS; } /* * Process tail of input samples. */ static int sox_stat_drain(sox_effect_t * effp, sox_sample_t *obuf UNUSED, size_t *osamp) { priv_t * stat = (priv_t *) effp->priv; /* When we run out of samples, then we need to pad buffer with * zeros and then run FFT one last time to process any unprocessed * samples. */ if (stat->fft && stat->fft_offset) { unsigned int x; for (x = stat->fft_offset; x < stat->fft_size; x++) stat->re_in[x] = 0; print_power_spectrum((unsigned) stat->fft_size, effp->in_signal.rate, stat->re_in, stat->re_out); } *osamp = 0; return SOX_EOF; } /* * Do anything required when you stop reading samples. * Don't close input file! */ static int sox_stat_stop(sox_effect_t * effp) { priv_t * stat = (priv_t *) effp->priv; double amp, scale, rms = 0, freq; double x, ct; ct = stat->read; if (stat->srms) { /* adjust results to units of rms */ double f; rms = sqrt(stat->sum2/ct); f = 1.0/rms; stat->max *= f; stat->min *= f; stat->mid *= f; stat->asum *= f; stat->sum1 *= f; stat->sum2 *= f*f; stat->dmax *= f; stat->dmin *= f; stat->dsum1 *= f; stat->dsum2 *= f*f; stat->scale *= rms; } scale = stat->scale; amp = -stat->min; if (amp < stat->max) amp = stat->max; /* Just print the volume adjustment */ if (stat->volume == 1 && amp > 0) { fprintf(stderr, "%.3f\n", SOX_SAMPLE_MAX/(amp*scale)); return SOX_SUCCESS; } if (stat->volume == 2) fprintf(stderr, "\n\n"); /* print out the info */ fprintf(stderr, "Samples read: %12" PRIu64 "\n", stat->read); fprintf(stderr, "Length (seconds): %12.6f\n", (double)stat->read/effp->in_signal.rate/effp->in_signal.channels); if (stat->srms) fprintf(stderr, "Scaled by rms: %12.6f\n", rms); else fprintf(stderr, "Scaled by: %12.1f\n", scale); fprintf(stderr, "Maximum amplitude: %12.6f\n", stat->max); fprintf(stderr, "Minimum amplitude: %12.6f\n", stat->min); fprintf(stderr, "Midline amplitude: %12.6f\n", stat->mid); fprintf(stderr, "Mean norm: %12.6f\n", stat->asum/ct); fprintf(stderr, "Mean amplitude: %12.6f\n", stat->sum1/ct); fprintf(stderr, "RMS amplitude: %12.6f\n", sqrt(stat->sum2/ct)); fprintf(stderr, "Maximum delta: %12.6f\n", stat->dmax); fprintf(stderr, "Minimum delta: %12.6f\n", stat->dmin); fprintf(stderr, "Mean delta: %12.6f\n", stat->dsum1/(ct-1)); fprintf(stderr, "RMS delta: %12.6f\n", sqrt(stat->dsum2/(ct-1))); freq = sqrt(stat->dsum2/stat->sum2)*effp->in_signal.rate/(M_PI*2); fprintf(stderr, "Rough frequency: %12d\n", (int)freq); if (amp>0) fprintf(stderr, "Volume adjustment: %12.3f\n", SOX_SAMPLE_MAX/(amp*scale)); if (stat->bin[2] == 0 && stat->bin[3] == 0) fprintf(stderr, "\nProbably text, not sound\n"); else { x = (float)(stat->bin[0] + stat->bin[3]) / (float)(stat->bin[1] + stat->bin[2]); if (x >= 3.0) { /* use opposite encoding */ if (effp->in_encoding->encoding == SOX_ENCODING_UNSIGNED) fprintf(stderr,"\nTry: -t raw -e signed-integer -b 8 \n"); else fprintf(stderr,"\nTry: -t raw -e unsigned-integer -b 8 \n"); } else if (x <= 1.0 / 3.0) ; /* correctly decoded */ else if (x >= 0.5 && x <= 2.0) { /* use ULAW */ if (effp->in_encoding->encoding == SOX_ENCODING_ULAW) fprintf(stderr,"\nTry: -t raw -e unsigned-integer -b 8 \n"); else fprintf(stderr,"\nTry: -t raw -e mu-law -b 8 \n"); } else fprintf(stderr, "\nCan't guess the type\n"); } /* Release FFT memory */ free(stat->re_in); free(stat->re_out); return SOX_SUCCESS; } static sox_effect_handler_t sox_stat_effect = { "stat", "[ -s N ] [ -rms ] [-freq] [ -v ] [ -d ]", SOX_EFF_MCHAN | SOX_EFF_MODIFY, sox_stat_getopts, sox_stat_start, sox_stat_flow, sox_stat_drain, sox_stat_stop, NULL, sizeof(priv_t) }; const sox_effect_handler_t *lsx_stat_effect_fn(void) { return &sox_stat_effect; }