shithub: sox

Download patch

ref: b106f2ad26fda2a76299020097a8cb50fda01dd2
parent: bb56888a97ad1ab8facfd915c095fb5ba79a0b0d
author: robs <robs>
date: Sun Feb 17 12:32:55 EST 2008

simple V.U. meter

--- a/AUTHORS
+++ b/AUTHORS
@@ -24,6 +24,7 @@
                 Others: soxi, open input files via URL, file merging, play
                 multiple files with mixed format types, play with replay-gain,
                 building with cmake, much manual improvement and expansion,
+		improved displays with -S & -V including V.U. and clips,
                 various fixes, enhancements and clean-ups.
 
 Contributors:
--- a/ChangeLog
+++ b/ChangeLog
@@ -37,6 +37,7 @@
   o Command line support for multiple file comments.  (robs)
   o Added soxi utility to extract/display file header fields.  (robs)
   o Added pkg-config support. (Pascal Giard)
+  o Added simple V.U. meter.  (robs)
 
 Bug fixes:
 
--- a/sox.1
+++ b/sox.1
@@ -253,6 +253,16 @@
 (Note that the syntax of the
 .B set
 command may vary from system to system.)
+.SP
+To set a suitable recording level, a V.U. meter is usually used.  SoX includes a
+simple V.U. meter that can be invoked as follows:
+.EX
+	rec -n
+.EE
+The recording level should be adjusted (using the system mixer program)
+so that the meter is `in the red' (a single exclamation mark is shown)
+\fIat most very occasionally\fR, and never `off the scale' (two
+exclamation marks are shown).
 .SS Accuracy
 Many file formats that compress audio discard some of the audio signal
 information whilst doing so; converting to such a format then converting
@@ -543,6 +553,36 @@
 Display input file format/header information and input file(s)
 processing progress in terms of elapsed/remaining time and percentage
 complete.
+Also displays the number of samples written to the output file, a V.U. meter,
+and an indication if clipping has occurred.
+The V.U. meter shows up to two channels and is calibrated for digital
+audio as follows:
+.TS
+center box;
+cI lI lI
+cI lI lI
+r l l.
+dB FSD	Display
+>=	(right channel)	Comment
+\-25.5	\-
+\-23.5	T{
+=
+T}
+\-21.5	=\-
+\-19.5	==
+\-17.5	==\-
+\-15.5	===
+\-13.5	===\-
+\-11.5	====
+\-9.5	====\-
+\-7.5	=====
+\-5.5	=====\-
+\-3.5	======
+\-1.5	=====!	`In the red'
+\-0.5	====!!	`Off the scale'
+.TE
+
+.SP
 This option is enabled by default when using
 SoX to play or record audio.
 .TP
--- a/src/sox.c
+++ b/src/sox.c
@@ -134,6 +134,7 @@
 static sox_bool user_abort = sox_false;
 static sox_bool user_skip = sox_false;
 static int success = 0;
+static sox_sample_t omax[2], omin[2];
 
 
 /* local forward declarations */
@@ -718,7 +719,7 @@
   parse_effects(argc, argv);
 
   /* Not the best way for users to do this; now deprecated in favour of soxi. */
-  if (!nuser_effects && ofile->filetype && !strcmp(ofile->filetype, "null")) {
+  if (!show_progress && !nuser_effects && ofile->filetype && !strcmp(ofile->filetype, "null")) {
     for (i = 0; i < input_count; i++)
       report_file_info(files[i]);
     exit(0);
@@ -1224,6 +1225,19 @@
     sox_sample_t * obuf UNUSED, sox_size_t * isamp, sox_size_t * osamp)
 {
   size_t len;
+
+  for (len = 0; len < *isamp; len += effp->ininfo.channels) {
+    omax[0] = max(omax[0], ibuf[len]);
+    omin[0] = min(omin[0], ibuf[len]);
+    if (effp->ininfo.channels > 1) {
+      omax[1] = max(omax[1], ibuf[len + 1]);
+      omin[1] = min(omin[1], ibuf[len + 1]);
+    }
+    else {
+      omax[1] = omax[0];
+      omin[1] = omin[0];
+    }
+  }
   for (*osamp = *isamp; *osamp; ibuf += len, *osamp -= len) {
     len = sox_write(ofile->ft, ibuf, *osamp);
     if (len == 0) {
@@ -1528,6 +1542,24 @@
   return string[n];
 }
 
+static char const * vu(unsigned channel)
+{
+  static char const * const text[][2] = {
+    {"", ""}, {"-", "-"}, {"=", "="}, {"-=", "=-"},
+    {"==", "=="}, {"-==", "==-"}, {"===", "==="}, {"-===", "===-"},
+    {"====", "===="}, {"-====", "====-"}, {"=====", "====="},
+    {"-=====", "=====-"}, {"======", "======"},
+    {"!=====", "=====!"}, {"!!====", "====!!"}, /* 2 `red' levels */
+  };
+  int const red = 2, white = array_length(text) - red;
+  double const MAX = SOX_SAMPLE_MAX, MIN = SOX_SAMPLE_MIN;
+  double linear = max(omax[channel] / MAX, omin[channel] / MIN);
+  int vu_dB = linear? floor(2 * white + red - .5 + linear_to_dB(linear)) : 0;
+  int index = vu_dB < 2 * white? max(vu_dB / 2, 0) : vu_dB - white;
+  omax[channel] = omin[channel] = 0;
+  return text[index][channel];
+}
+
 static void display_status(sox_bool all_done)
 {
   static struct timeval then;
@@ -1542,9 +1574,10 @@
       left_time = max(in_time - read_time, 0);
       percentage = max(100. * read_wide_samples / input_wide_samples, 0);
     }
-    fprintf(stderr, "\rTime: %s [%s] of %s (%-5s) Samples out: %-6sClips: %-5s",
+    fprintf(stderr, "\r%s [%s] of %s (%-5s) Samps out:%-5s%6s|%-6sClips:%-5s",
       str_time(read_time), str_time(left_time), str_time(in_time),
-      sigfigs3p(percentage), sigfigs3(output_samples), sigfigs3(total_clips()));
+      sigfigs3p(percentage), sigfigs3(output_samples),
+      vu(0), vu(1), sigfigs3(total_clips()));
   }
   if (all_done)
     fputc('\n', stderr);