ref: 6e81fe0425519f0e1f977c5cc4f7cda7e4c2bfde
parent: 53ed54677948b1fec4c60b487e4f9f9d04fd25bf
author: cbagwell <cbagwell>
date: Tue Aug 12 19:06:10 EDT 2008
Initial support for writing to multiple output files.
--- a/ChangeLog
+++ b/ChangeLog
@@ -23,6 +23,10 @@
sox -V file(s) -n
doesn't read to EOF.
+Other new features:
+ o New --output option to write to multiple files in one run.
+ Only useful with certain effects like trim and silence. (cbagwell)
+
Other bug fixes:
o Bump library version because it was not binary compatible with
--- a/sox.1
+++ b/sox.1
@@ -458,6 +458,27 @@
Note that this balancing factor does not guarantee that no clipping will occur,
however, in many cases, the number of clips will be low and the resultant
distortion imperceptable.
+.SS Output Files
+SoX's default behavior is to take one or more input files and
+write them to a single output file. In fact, the command line syntax
+only supports specifying a single filename in all cases.
+
+SoX's output routine can be configured to output multiple files in
+certain cases. If the output method 'multiple' is specified
+then SoX will create a new file each time the specified effects
+terminate the output early. Each time a new file is created, the effects
+chain is restarted. In case multiple effects are in the chain, the
+chain is only restart from the effect that terminated so that no
+input samples are lost.
+
+There are only a few effects that can stop processing input early such
+as the trim and silence effects so the application for multiple files
+is very limited.
+
+If the user specifies 'filename.ext' as the output file then SoX will
+first use a filename of 'filename0001.ext' and will increment the number
+for each new file.
+
.SS Stopping SoX
Usually SoX will complete its processing and exit automatically, however
if desired, it can be terminated by pressing the
--- a/src/sox.c
+++ b/src/sox.c
@@ -76,6 +76,7 @@
static enum {sox_sequence, sox_concatenate, sox_mix, sox_mix_power, sox_merge, sox_multiply}
combine_method = sox_concatenate;
+static enum { sox_single, sox_multiple } output_method = sox_single;
#define is_serial(m) ((m) <= sox_concatenate)
#define is_parallel(m) (!is_serial(m))
static sox_bool interactive = sox_false;
@@ -114,8 +115,8 @@
#define ofile files[file_count - 1]
static size_t file_count = 0;
static size_t input_count = 0;
+static size_t output_count = 0;
-
/* Effects */
/* We parse effects into a temporary effects table and then place into
@@ -143,6 +144,8 @@
static unsigned long input_wide_samples = 0;
static unsigned long read_wide_samples = 0;
static unsigned long output_samples = 0;
+static sox_bool input_eof = sox_false;
+static sox_bool output_eof = sox_false;
static sox_bool user_abort = sox_false;
static sox_bool user_skip = sox_false;
static int success = 0;
@@ -473,6 +476,9 @@
read_wide_samples += olen;
olen *= effp->in_signal.channels;
*osamp = olen;
+
+ input_eof = olen ? sox_false : sox_true;
+
return olen? SOX_SUCCESS : SOX_EOF;
}
@@ -519,6 +525,7 @@
*osamp = 0;
len = *isamp? sox_write(ofile->ft, ibuf, *isamp) : 0;
output_samples += len / ofile->ft->signal.channels;
+ output_eof = (len != *isamp) ? sox_true: sox_false;
if (len != *isamp) {
if (ofile->ft->sox_errno)
display_error(ofile->ft);
@@ -735,6 +742,52 @@
return c == 'y' || c == 'Y';
}
+static char *fndup_with_count(const char *filename, int count)
+{
+ char *expand_fn, *efn;
+ const char *fn, *ext, *end;
+ sox_bool found_marker = sox_false;
+
+ fn = filename;
+
+ efn = expand_fn = malloc(1024);
+
+ /* Find extension in case user didn't specify a substitution
+ * marker.
+ */
+ end = ext = filename + strlen(filename);
+ while (ext > filename && *ext != '.')
+ ext--;
+
+ /* In case extension not found, point back to end of string to do less
+ * copying later.
+ */
+ if (*ext != '.')
+ ext = end;
+
+ while (fn < end)
+ {
+ /* FIXME: Look for %n and if found replacew with a sprintf of count */
+ *efn++ = *fn++;
+ }
+
+ *efn = 0;
+
+ /* If user didn't tell us what to do then default to putting
+ * the count right before file extension.
+ */
+ if (!found_marker)
+ {
+ efn -= strlen (ext);
+
+ sprintf(efn, "%04d", count);
+ efn = efn + 4;
+ strcat(efn, ext);
+ }
+
+ return expand_fn;
+}
+
static void open_output_file(void)
{
double factor;
@@ -741,6 +794,8 @@
int i;
sox_comments_t p = ofile->oob.comments;
sox_oob_t oob = files[0]->ft->oob;
+ char *expand_fn;
+
oob.comments = sox_copy_comments(files[0]->ft->oob.comments);
if (!oob.comments && !p)
@@ -763,9 +818,14 @@
oob.loops[i].length = oob.loops[i].length * factor;
}
- ofile->ft = sox_open_write(ofile->filename, &ofile->signal, &ofile->encoding,
+ if (output_method == sox_multiple)
+ expand_fn = fndup_with_count(ofile->filename, ++output_count);
+ else
+ expand_fn = strdup(ofile->filename);
+ ofile->ft = sox_open_write(expand_fn, &ofile->signal, &ofile->encoding,
ofile->filetype, &oob, overwrite_permitted);
sox_delete_comments(&oob.comments);
+ free(expand_fn);
if (!ofile->ft)
/* sox_open_write() will call sox_warn for most errors.
@@ -912,6 +972,7 @@
static int process(void)
{ /* Input(s) -> Balancing -> Combiner -> Effects -> Output */
unsigned i;
+ int flow_status;
for (i = 0; i < nuser_effects; ++i)
*user_efftab[i] = efftab_options[i];
@@ -920,8 +981,6 @@
set_combiner_and_output_encoding_parameters();
open_output_file();
- /* FIXME: For sox_sequence, this needs proper cleanup of effects-chain memory
- * (including `kill' of auto effects) */
ofile_effects_chain.length = 0;
ofile_effects_chain.global_info = sox_effects_globals;
@@ -933,7 +992,18 @@
signal(SIGTERM, sigint); /* Stop gracefully, as soon as we possibly can. */
signal(SIGINT , sigint); /* Either skip current input or behave as SIGTERM. */
- return sox_flow_effects(&ofile_effects_chain, update_status);
+ flow_status = sox_flow_effects(&ofile_effects_chain, update_status);
+
+ /* When in sox_multiple mode, changing to a new output file is
+ * based on effects return ST_EOF. In that case, don't return
+ * SOX_EOF. Treat input EOF or errors writing to output file as
+ * real error case.
+ */
+ if (output_method == sox_multiple && current_input < input_count &&
+ !(input_eof || output_eof))
+ flow_status = SOX_SUCCESS;
+
+ return flow_status;
}
static void display_SoX_version(FILE * file)
@@ -1046,6 +1116,8 @@
"--interactive prompt to overwrite output file",
"-m, --combine mix mix multiple input files (instead of concatenating)",
"-M, --combine merge merge multiple input files (instead of concatenating)",
+"--output single write to single output file (default)",
+"--output multiple write to multiple output file",
"--plot gnuplot|octave generate script to plot response of filter effect",
"-q, --no-show-progress run in quiet mode; opposite of -S",
"--replay-gain track|album|off default: off (sox, rec), track (play)",
@@ -1244,6 +1316,7 @@
{"plot" , required_argument, NULL, 0},
{"replay-gain" , required_argument, NULL, 0},
{"version" , no_argument, NULL, 0},
+ {"output" , required_argument, NULL, 0},
{"channels" , required_argument, NULL, 'c'},
{"compression" , required_argument, NULL, 'C'},
@@ -1268,6 +1341,12 @@
ENUM_ITEM(sox_,multiply)
{0, 0}};
+static enum_item const output_methods[] = {
+ ENUM_ITEM(sox_,single)
+ ENUM_ITEM(sox_,multiple)
+ {0, 0}};
+
+
enum {ENDIAN_little, ENDIAN_big, ENDIAN_swap};
static enum_item const endian_options[] = {
ENUM_ITEM(ENDIAN_,little)
@@ -1384,6 +1463,10 @@
display_SoX_version(stdout);
exit(0);
break;
+
+ case 13:
+ output_method = enum_option(option_index, output_methods);
+ break;
}
break;
@@ -1831,15 +1914,31 @@
}
/* Save things that sox_sequence needs to be reinitialised for each segued
- * block of input files: */
+ * block of input files. Also used by sox_multiple output mode. */
ofile_signal_options = ofile->signal;
ofile_encoding_options = ofile->encoding;
for (i = 0; i < nuser_effects; ++i)
efftab_options[i] = *user_efftab[i];
- if (combine_method == sox_sequence) do {
+ if (combine_method == sox_sequence || output_method == sox_multiple) do {
+ /* If ofile->ft is set then we've called this at least once.
+ * Need to do some cleanup before continueing on.
+ */
if (ofile->ft)
+ {
+ /* If in sox_multiple mode and something besides input/combiner
+ * effect stopped the writing then only restart those effects
+ * so that no input samples are lost.
+ * If input reached EOF then restart all effects.
+ */
+ if (output_method == sox_multiple && !input_eof)
+ /* FIXME: Make a function that does that */
+ ;
+ else if (output_method == sox_multiple)
+ sox_delete_effects(&ofile_effects_chain);
sox_close(ofile->ft);
+ ofile->ft = NULL;
+ }
} while (process() != SOX_EOF && !user_abort && current_input < input_count);
else process();
sox_delete_effects(&ofile_effects_chain);