shithub: sox

Download patch

ref: 410ae9364d72c895e860ad018dd40fa7cffa8635
parent: 53c591f0bbcb0b91e5470b1f600137d389e60696
author: robs <robs>
date: Sat Mar 7 10:42:48 EST 2009

experimental stats effect

--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -28,14 +28,14 @@
 
 # Format with: !xargs echo|tr ' ' '\n'|sort|column|expand|sed 's/^/  /'
 set(effects_srcs
-  bend            dither          input           rate            stretch
-  biquad          earwax          loudness        remix           swap
-  biquads         echo            mcompand        repeat          synth
-  chorus          echos           mixer           reverb          tempo
-  compand         fade            noiseprof       reverse         tremolo
-  compandt        fft4g           noisered        silence         trim
-  contrast        filter          output          sinc            vol
-  crop            fir             overdrive       skeleff
+  bend            dither          input           rate            stats
+  biquad          earwax          loudness        remix           stretch
+  biquads         echo            mcompand        repeat          swap
+  chorus          echos           mixer           reverb          synth
+  compand         fade            noiseprof       reverse         tempo
+  compandt        fft4g           noisered        silence         tremolo
+  contrast        filter          output          sinc            trim
+  crop            fir             overdrive       skeleff         vol
   dcshift         firfit          pad             speed
   delay           flanger         pan             splice
   dft_filter      gain            phaser          stat
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -65,8 +65,8 @@
 	noisered.c noisered.h output.c overdrive.c pad.c pan.c phaser.c rate.c \
 	rate_filters.h rate_half_fir.h rate_poly_fir0.h rate_poly_fir.h \
 	remix.c repeat.c reverb.c reverse.c silence.c sinc.c skeleff.c speed.c \
-	splice.c stat.c stretch.c swap.c synth.c tempo.c tremolo.c trim.c \
-	vol.c
+	splice.c stat.c stats.c stretch.c swap.c synth.c tempo.c tremolo.c \
+	trim.c vol.c
 if HAVE_PNG
     libsox_la_SOURCES += spectrogram.c
 endif
--- a/src/effects.h
+++ b/src/effects.h
@@ -77,6 +77,7 @@
   EFFECT(speed)
   EFFECT(splice)
   EFFECT(stat)
+  EFFECT(stats)
   EFFECT(stretch)
   EFFECT(swap)
   EFFECT(synth)
--- a/src/rate.c
+++ b/src/rate.c
@@ -304,7 +304,7 @@
           prepare_coefs(coefs, f->num_coefs, phases, interp_order, mult);
       lsx_debug("fir_len=%i phases=%i coef_interp=%i mult=%i size=%s",
           f->num_coefs, phases, interp_order, mult,
-          lsx_sigfigs3((num_taps + 1) * (interp_order + 1) * sizeof(sample_t)));
+          lsx_sigfigs3((num_taps +1.) * (interp_order + 1) * sizeof(sample_t)));
       free(coefs);
     }
     last_stage.fn = f1->fn;
--- a/src/sox.c
+++ b/src/sox.c
@@ -1115,8 +1115,8 @@
     }
     fprintf(stderr, "\rIn:%-5s %s [%s] Out:%-5s [%6s|%-6s] %s Clip:%-5s",
       lsx_sigfigs3p(percentage), str_time(read_time), str_time(left_time),
-      lsx_sigfigs3(output_samples),
-      vu(0), vu(1), headroom(), lsx_sigfigs3(total_clips()));
+      lsx_sigfigs3((double)output_samples),
+      vu(0), vu(1), headroom(), lsx_sigfigs3((double)total_clips()));
   }
   if (all_done)
     fputc('\n', stderr);
@@ -2463,7 +2463,7 @@
     case Bitrate: {
       struct stat st;    /* ft->fp may validly be NULL, so stat not fstat */
       if (!stat(filename, &st) && (st.st_mode & S_IFMT) == S_IFREG)
-        printf("%s\n", lsx_sigfigs3((size_t)(8. * st.st_size / secs + .5)));
+        printf("%s\n", lsx_sigfigs3(8. * st.st_size / secs));
       else puts("0");
       break;
     }
--- a/src/sox.h
+++ b/src/sox.h
@@ -568,7 +568,7 @@
 int lsx_enum_option(int c, lsx_enum_item const * items);
 sox_bool lsx_strends(char const * str, char const * end);
 char const * lsx_find_file_extension(char const * pathname);
-char const * lsx_sigfigs3(size_t number);
+char const * lsx_sigfigs3(double number);
 char const * lsx_sigfigs3p(double percentage);
 
 /* WARNING END */
--- /dev/null
+++ b/src/stats.c
@@ -1,0 +1,298 @@
+/* libSoX effect: stats   (c) 2009 robs@users.sourceforge.net
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "sox_i.h"
+#include "getopt.h"
+#include <ctype.h>
+#include <string.h>
+
+typedef struct {
+  int       scale_bits, hex_bits;
+  double    time_constant, scale;
+
+  double    last, sigma_x, sigma_x2, avg_sigma_x2, min_sigma_x2, max_sigma_x2;
+  double    min, max, mult, min_run, min_runs, max_run, max_runs;
+  off_t     num_samples, tc_samples, min_count, max_count;
+  uint32_t  mask;
+} priv_t;
+
+static int getopts(sox_effect_t * effp, int argc, char **argv)
+{
+  priv_t * p = (priv_t *)effp->priv;
+  int c;
+
+  p->time_constant = .05;
+  p->scale = 1;
+  while ((c = getopt(argc, argv, "+x:b:t:s:")) != -1) switch (c) {
+    GETOPT_NUMERIC('x', hex_bits      ,  2 , 32)
+    GETOPT_NUMERIC('b', scale_bits    ,  2 , 32)
+    GETOPT_NUMERIC('w', time_constant ,  .01 , 10)
+    GETOPT_NUMERIC('s', scale         ,  -99, 99)
+    default: lsx_fail("invalid option `-%c'", optopt); return lsx_usage(effp);
+  }
+  if (p->hex_bits)
+    p->scale_bits = p->hex_bits;
+  return optind != argc? lsx_usage(effp) : SOX_SUCCESS;
+}
+
+static int start(sox_effect_t * effp)
+{
+  priv_t * p = (priv_t *)effp->priv;
+
+  p->last = 0;
+  p->mult = exp((-1 / p->time_constant / effp->in_signal.rate));
+  p->tc_samples = 5 * p->time_constant * effp->in_signal.rate + .5;
+  p->sigma_x = p->sigma_x2 = p->avg_sigma_x2 = p->max_sigma_x2 = 0;
+  p->min = p->min_sigma_x2 = 2;
+  p->max = -p->min;
+  p->num_samples = 0;
+  p->mask = 0;
+  return SOX_SUCCESS;
+}
+
+static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
+    sox_sample_t * obuf, size_t * ilen, size_t * olen)
+{
+  priv_t * p = (priv_t *)effp->priv;
+  size_t len = *ilen = *olen = min(*ilen, *olen);
+  memcpy(obuf, ibuf, len * sizeof(*olen));
+
+  for (; len--; ++ibuf, ++p->num_samples) {
+    double d = SOX_SAMPLE_TO_FLOAT_64BIT(*ibuf,);
+
+    if (d < p->min)
+      p->min = d, p->min_count = 1, p->min_run = 1, p->min_runs = 0;
+    else if (d == p->min) {
+      ++p->min_count;
+      p->min_run = d == p->last? p->min_run + 1 : 1;
+    }
+    else if (p->last == p->min)
+      p->min_runs += sqr(p->min_run);
+
+    if (d > p->max)
+      p->max = d, p->max_count = 1, p->max_run = 1, p->max_runs = 0;
+    else if (d == p->max) {
+      ++p->max_count;
+      p->max_run = d == p->last? p->max_run + 1 : 1;
+    }
+    else if (p->last == p->max)
+      p->max_runs += sqr(p->max_run);
+
+    p->sigma_x += d;
+    p->sigma_x2 += sqr(d);
+
+    if (!p->avg_sigma_x2)
+      p->avg_sigma_x2 = sqr(d);
+    p->avg_sigma_x2 = p->avg_sigma_x2 * p->mult + (1 - p->mult) * sqr(d);
+
+    if (p->avg_sigma_x2 > p->max_sigma_x2)
+      p->max_sigma_x2 = p->avg_sigma_x2;
+    else if (p->avg_sigma_x2 < p->min_sigma_x2 && p->num_samples >= p->tc_samples)
+      p->min_sigma_x2 = p->avg_sigma_x2;
+
+    p->last = d;
+    p->mask |= *ibuf;
+  }
+  return SOX_SUCCESS;
+}
+
+static int drain(sox_effect_t * effp, sox_sample_t * obuf, size_t * olen)
+{
+  priv_t * p = (priv_t *)effp->priv;
+
+  if (p->last == p->min)
+    p->min_runs += sqr(p->min_run);
+  if (p->last == p->max)
+    p->max_runs += sqr(p->max_run);
+
+  (void)obuf, *olen = 0;
+  return SOX_SUCCESS;
+}
+
+static unsigned bit_depth(uint32_t mask, double min, double max, unsigned * x)
+{
+  SOX_SAMPLE_LOCALS;
+  unsigned result = 32, dummy = 0;
+
+  for (; result && !(mask & 1); --result, mask >>= 1);
+  if (x)
+    *x = result;
+  mask = SOX_FLOAT_64BIT_TO_SAMPLE(max, dummy);
+  if (min < 0)
+    mask |= ~(SOX_FLOAT_64BIT_TO_SAMPLE(min, dummy) << 1);
+  for (; result && !(mask & SOX_SAMPLE_MIN); --result, mask <<= 1);
+  return result;
+}
+
+static void output(priv_t const * p, double x)
+{
+  if (p->scale_bits) {
+    unsigned mult = 1 << (p->scale_bits - 1);
+    int i;
+    x = floor(x * mult + .5);
+    i = min(x, mult - 1.);
+    if (p->hex_bits)
+      if (x < 0) {
+        char buf[30];
+        sprintf(buf, "%x", -i);
+        fprintf(stderr, " %*c%s", 9 - strlen(buf), '-', buf);
+      }
+      else fprintf(stderr, " %9x", i);
+    else fprintf(stderr, " %9i", i);
+  }
+  else fprintf(stderr, " %9.*f", fabs(p->scale) < 10 ? 6 : 5, p->scale * x);
+}
+
+static int stop(sox_effect_t * effp)
+{
+  priv_t * p = (priv_t *)effp->priv;
+
+  if (!effp->flow) {
+    double min_runs = 0, max_count = 0, min = 2, max = -2, max_sigma_x = 0, sigma_x = 0, sigma_x2 = 0, min_sigma_x2 = 2, max_sigma_x2 = 0, avg_peak = 0;
+    off_t num_samples = 0, min_count = 0, max_runs = 0;
+    uint32_t mask = 0;
+    unsigned b1, b2, i, n = effp->flows > 1 ? effp->flows : 0;
+
+    for (i = 0; i < effp->flows; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      min = min(min, q->min);
+      max = max(max, q->max);
+      min_sigma_x2 = min(min_sigma_x2, q->min_sigma_x2);
+      max_sigma_x2 = max(max_sigma_x2, q->max_sigma_x2);
+      sigma_x += q->sigma_x;
+      sigma_x2 += q->sigma_x2;
+      num_samples += q->num_samples;
+      mask |= q->mask;
+      if (fabs(q->sigma_x) > fabs(max_sigma_x))
+        max_sigma_x = q->sigma_x;
+      min_count += q->min_count;
+      min_runs += q->min_runs;
+      max_count += q->max_count;
+      max_runs += q->max_runs;
+      avg_peak += max(-q->min, q->max);
+    }
+    avg_peak /= effp->flows;
+
+    if (!num_samples) {
+      lsx_warn("no audio");
+      return SOX_SUCCESS;
+    }
+
+    if (n == 2)
+      fprintf(stderr, "            Over-all     Left      Right\n");
+    else if (n) {
+      fprintf(stderr, "            Over-all");
+      for (i = 0; i < n; ++i)
+        fprintf(stderr, "     Ch%-3i", i + 1);
+      fprintf(stderr, "\n");
+    }
+
+    fprintf(stderr, "DC offset ");
+    output(p, max_sigma_x / p->num_samples);
+    for (i = 0; i < n; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      output(p, q->sigma_x / q->num_samples);
+    }
+
+    fprintf(stderr, "\nMin level ");
+    output(p, min);
+    for (i = 0; i < n; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      output(p, q->min);
+    }
+
+    fprintf(stderr, "\nMax level ");
+    output(p, max);
+    for (i = 0; i < n; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      output(p, q->max);
+    }
+
+    fprintf(stderr, "\nPk lev dB %10.2f", linear_to_dB(max(-min, max)));
+    for (i = 0; i < n; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      fprintf(stderr, "%10.2f", linear_to_dB(max(-q->min, q->max)));
+    }
+
+    fprintf(stderr, "\nRMS lev dB%10.2f", linear_to_dB(sqrt(sigma_x2 / num_samples)));
+    for (i = 0; i < n; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      fprintf(stderr, "%10.2f", linear_to_dB(sqrt(q->sigma_x2 / q->num_samples)));
+    }
+
+    fprintf(stderr, "\nRMS Pk dB %10.2f", linear_to_dB(sqrt(max_sigma_x2)));
+    for (i = 0; i < n; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      fprintf(stderr, "%10.2f", linear_to_dB(sqrt(q->max_sigma_x2)));
+    }
+
+    fprintf(stderr, "\nRMS Tr dB ");
+    if (min_sigma_x2 != 1)
+      fprintf(stderr, "%10.2f", linear_to_dB(sqrt(min_sigma_x2)));
+    else fprintf(stderr, "         -");
+    for (i = 0; i < n; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      if (q->min_sigma_x2 != 1)
+        fprintf(stderr, "%10.2f", linear_to_dB(sqrt(q->min_sigma_x2)));
+      else fprintf(stderr, "         -");
+    }
+
+    if (effp->flows > 1)
+      fprintf(stderr, "\nCrest factor       -");
+    else fprintf(stderr, "\nCrest factor %7.2f", sigma_x2 ? avg_peak / sqrt(sigma_x2 / num_samples) : 1);
+    for (i = 0; i < n; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      fprintf(stderr, "%10.2f", q->sigma_x2? max(-q->min, q->max) / sqrt(q->sigma_x2 / q->num_samples) : 1);
+    }
+
+    fprintf(stderr, "\nFlat factor%9.2f", linear_to_dB((min_runs + max_runs) / (min_count + max_count)));
+    for (i = 0; i < n; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      fprintf(stderr, " %9.2f", linear_to_dB((q->min_runs + q->max_runs) / (q->min_count + q->max_count)));
+    }
+
+    fprintf(stderr, "\nPk count   %9s", lsx_sigfigs3((min_count + max_count) / effp->flows));
+    for (i = 0; i < n; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      fprintf(stderr, " %9s", lsx_sigfigs3((double)(q->min_count + q->max_count)));
+    }
+
+    b1 = bit_depth(mask, min, max, &b2);
+    fprintf(stderr, "\nBit-depth      %2u/%-2u", b1, b2);
+    for (i = 0; i < n; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      b1 = bit_depth(q->mask, q->min, q->max, &b2);
+      fprintf(stderr, "     %2u/%-2u", b1, b2);
+    }
+
+    fprintf(stderr, "\nNum samples%9s", lsx_sigfigs3((double)p->num_samples));
+    fprintf(stderr, "\nLength s   %9.3f", p->num_samples / effp->in_signal.rate);
+    fprintf(stderr, "\nScale max ");
+    output(p, 1.);
+    fprintf(stderr, "\nWindow s   %9.3f", p->time_constant);
+    fprintf(stderr, "\n");
+  }
+  return SOX_SUCCESS;
+}
+
+sox_effect_handler_t const * lsx_stats_effect_fn(void)
+{
+  static sox_effect_handler_t handler = {
+    "stats", "[-b bits|-x bits|-s scale] [-w window-time]", SOX_EFF_MODIFY,
+    getopts, start, flow, drain, stop, NULL, sizeof(priv_t)};
+  return &handler;
+}
--- a/src/util.c
+++ b/src/util.c
@@ -101,18 +101,22 @@
   return p->value;
 }
 
-char const * lsx_sigfigs3(size_t number)
+char const * lsx_sigfigs3(double number)
 {
-  static char string[16][10];
-  static unsigned n;
-  unsigned a, b, c = 2;
-  sprintf(string[n = (n+1) & 15], "%#.3g", (double)number);
-  if (sscanf(string[n], "%u.%ue%u", &a, &b, &c) == 3)
-    a = 100*a + b;
-  switch (c%3) {
-    case 0: sprintf(string[n], "%u.%02u%c", a/100,a%100, " kMGTPE"[c/3]); break;
-    case 1: sprintf(string[n], "%u.%u%c"  , a/10 ,a%10 , " kMGTPE"[c/3]); break;
-    case 2: sprintf(string[n], "%u%c"     , a          , " kMGTPE"[c/3]); break;
+  static char const symbols[] = "\0kMGTPEZY";
+  static char string[16][10];   /* FIXME: not thread-safe */
+  static unsigned n;            /* ditto */
+  unsigned a, b, c;
+  sprintf(string[n = (n+1) & 15], "%#.3g", number);
+  switch (sscanf(string[n], "%u.%ue%u", &a, &b, &c)) {
+    case 2: if (b) return string[n]; /* Can fall through */
+    case 1: c = 2; break;
+    case 3: a = 100*a + b; break;
+  }
+  if (c < array_length(symbols) * 3 - 3) switch (c%3) {
+    case 0: sprintf(string[n], "%u.%02u%c", a/100,a%100, symbols[c/3]); break;
+    case 1: sprintf(string[n], "%u.%u%c"  , a/10 ,a%10 , symbols[c/3]); break;
+    case 2: sprintf(string[n], "%u%c"     , a          , symbols[c/3]); break;
   }
   return string[n];
 }