ref: e7b81f74082543c3175a6a670879b5584e24ff04
parent: 50b30d651e24804bca1d27739330a659a45dd2f5
author: robs <robs>
date: Fri May 25 17:25:06 EDT 2007
multi-channel effects chain
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -95,9 +95,11 @@
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/soxstdint.h.cmake
)
-add_library(lib${PROJECT_NAME} 8svx adpcm adpcms aifc-fmt aiff aiff-fmt al-fmt au auto avr biquad biquads cdr chorus compand compandt cvsd cvsd-fmt dat dcshift dither dvms-fmt earwax echo echos fade FFT filter flanger g711 g721 g723_24 g723_40 g72x getopt1 getopt gsm handlers hcom ima-fmt ima_rw la-fmt lpc10 lu-fmt maud mcompand misc mixer noiseprof noisered nulfile pad pan phaser pitch polyphas prc rate raw raw-fmt repeat resample reverb reverse s1-fmt s2-fmt s3-fmt s4-fmt sf silence skeleff skelform smp sndrtool soxio speed sphere stat stretch swap synth tremolo trim tx16w u1-fmt u2-fmt u3-fmt u4-fmt ul-fmt util vibro voc vol vox vox-fmt wav wve xa xmalloc soxstdint ${optional_srcs})
+add_library(lib${PROJECT_NAME} 8svx adpcm adpcms aifc-fmt aiff aiff-fmt al-fmt au auto avr biquad biquads cdr chorus compand compandt cvsd cvsd-fmt dat dcshift dither dvms-fmt earwax echo echos effects fade FFT filter flanger g711 g721 g723_24 g723_40 g72x getopt1 getopt gsm handlers hcom ima-fmt ima_rw la-fmt lpc10 lu-fmt maud mcompand misc mixer noiseprof noisered nulfile pad pan phaser pitch polyphas prc rate raw raw-fmt repeat resample reverb reverse s1-fmt s2-fmt s3-fmt s4-fmt sf silence skeleff skelform smp sndrtool soxio speed sphere stat stretch swap synth tremolo trim tx16w u1-fmt u2-fmt u3-fmt u4-fmt ul-fmt util vibro voc vol vox vox-fmt wav wve xa xmalloc soxstdint ${optional_srcs})
add_executable(${PROJECT_NAME} ${PROJECT_NAME}.c)
target_link_libraries(${PROJECT_NAME} lib${PROJECT_NAME} lpc10 ${optional_libs})
add_executable(sox_sample_test sox_sample_test.c)
add_custom_target(rec ALL ln -sf sox rec DEPENDS sox)
add_custom_target(play ALL ln -sf sox play DEPENDS sox)
+find_program(CTAGS NAMES exuberant-ctags ctags)
+add_custom_target(tags ${CTAGS} --recurse --extra=fq ${CMAKE_CURRENT_SOURCE_DIR})
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -183,7 +183,7 @@
g711.c g711.h g721.c g723_24.c g723_40.c g72x.c g72x.h vox.c vox.h \
raw.c raw.h handlers.c misc.c sox_i.h skelform.c soxio.c \
util.c xmalloc.c xmalloc.h getopt.c getopt1.c getopt.h \
- soxconfig.h
+ soxconfig.h effects.c
libsox_la_CFLAGS = @SAMPLERATE_CFLAGS@
libsox_la_LIBADD = @LIBGSM_LIBADD@ @SAMPLERATE_LIBS@
--- /dev/null
+++ b/src/effects.c
@@ -1,0 +1,284 @@
+/*
+ * 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 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,
+ * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+ */
+
+/* SoX Effects chain (c) 2007 robs@users.sourceforge.net */
+
+#include "sox_i.h"
+
+struct sox_effect * effects[MAX_EFFECTS];
+unsigned neffects;
+
+
+void add_effect(struct sox_effect * e, sox_signalinfo_t * in, sox_signalinfo_t * out, int * effects_mask)
+{
+ unsigned f, flows;
+
+ *effects_mask = sox_updateeffect(e, in, out, *effects_mask);
+
+ flows = (e->h->flags & SOX_EFF_MCHAN)? 1 : e->ininfo.channels;
+
+ effects[neffects] = xcalloc(flows, sizeof(effects[neffects][0]));
+ effects[neffects][0] = *e;
+ effects[neffects][0].flows = flows;
+
+ for (f = 1; f < flows; ++f)
+ effects[neffects][f] = effects[neffects][0];
+
+ ++neffects;
+}
+
+static void stop_effect(unsigned e)
+{
+ unsigned i;
+
+ sox_size_t clips = 0;
+ int (*stop)(eff_t effp) =
+ effects[e][0].h->stop? effects[e][0].h->stop : sox_effect_nothing;
+
+ for (i = 0; i < effects[e][0].flows; ++i) {
+ stop(&effects[e][i]);
+ clips += effects[e][i].clips;
+ }
+ if (clips != 0)
+ sox_warn("'%s' clipped %u samples; decrease volume?",effects[e][0].name,clips);
+}
+
+void stop_effects(void)
+{
+ unsigned e;
+ for (e = 0; e < neffects; ++e)
+ stop_effect(e);
+}
+
+static void kill_effect(unsigned e)
+{
+ int (*kill)(eff_t effp) =
+ effects[e][0].h->kill? effects[e][0].h->kill : sox_effect_nothing;
+
+ kill(&effects[e][0]); /* One kill for all flows */
+}
+
+void kill_effects(void)
+{
+ unsigned e;
+ for (e = 0; e < neffects; ++e)
+ kill_effect(e);
+}
+
+int start_effects(void)
+{
+ unsigned i, j;
+ int ret = SOX_SUCCESS;
+
+ for (i = 0; i < neffects; ++i) {
+ struct sox_effect * e = &effects[i][0];
+ sox_bool is_always_null = (e->h->flags & SOX_EFF_NULL) != 0;
+ int (*start)(eff_t effp) = e->h->start? e->h->start : sox_effect_nothing;
+
+ if (is_always_null)
+ sox_report("'%s' has no effect (is a proxy effect)", e->name);
+ else {
+ e->clips = 0;
+ ret = start(e);
+ if (ret == SOX_EFF_NULL)
+ sox_warn("'%s' has no effect in this configuration", e->name);
+ else if (ret != SOX_SUCCESS)
+ return SOX_EOF;
+ }
+ if (is_always_null || ret == SOX_EFF_NULL) { /* remove from the chain */
+ kill_effect(i);
+ free(effects[i]);
+ --neffects;
+ for (j = i--; j < neffects; ++j)
+ effects[j] = effects[j + 1];
+ }
+ else for (j = 1; j < effects[i][0].flows; ++j) {
+ effects[i][j].clips = 0;
+ if (start(&effects[i][j]) != SOX_SUCCESS)
+ return SOX_EOF;
+ }
+ }
+ for (i = 0; i < neffects; ++i) {
+ struct sox_effect * e = &effects[i][0];
+ sox_report("Effects chain: %-10s %uHz %u channels %s",
+ e->name, e->ininfo.rate, e->ininfo.channels,
+ (e->h->flags & SOX_EFF_MCHAN)? "(multi)" : "");
+ }
+ return SOX_SUCCESS;
+}
+
+static sox_ssample_t **ibufc, **obufc; /* Channel interleave buffers */
+
+static int flow_effect(unsigned e)
+{
+ sox_size_t i, f, idone, odone;
+ const sox_ssample_t *ibuf;
+ int effstatus = SOX_SUCCESS;
+ int (*flow)(eff_t, sox_ssample_t const*, sox_ssample_t*, sox_size_t*, sox_size_t*) =
+ effects[e][0].h->flow? effects[e][0].h->flow : sox_effect_nothing_flow;
+
+ idone = effects[e - 1][0].olen - effects[e - 1][0].odone;
+ odone = sox_bufsiz - effects[e][0].olen;
+
+ if (effects[e][0].flows == 1) /* Run effect on all channels at once */
+ effstatus = flow(&effects[e][0],
+ &effects[e - 1][0].obuf[effects[e - 1][0].odone],
+ &effects[e][0].obuf[effects[e][0].olen], &idone, &odone);
+ else { /* Run effect on each channel individually */
+ sox_ssample_t *obuf = &effects[e][0].obuf[effects[e][0].olen];
+ sox_size_t idone_last, odone_last;
+
+ ibuf = &effects[e - 1][0].obuf[effects[e - 1][0].odone];
+ for (i = 0; i < idone; i += effects[e][0].flows)
+ for (f = 0; f < effects[e][0].flows; ++f)
+ ibufc[f][i / effects[e][0].flows] = *ibuf++;
+
+ for (f = 0; f < effects[e][0].flows; ++f) {
+ sox_size_t idonec = idone / effects[e][0].flows;
+ sox_size_t odonec = odone / effects[e][0].flows;
+ int eff_status_c =
+ flow(&effects[e][f], ibufc[f], obufc[f], &idonec, &odonec);
+ if (f && (idonec != idone_last || odonec != odone_last)) {
+ sox_fail("'%s' flowed asymmetrically!", effects[e][0].name);
+ effstatus = SOX_EOF;
+ }
+ idone_last = idonec;
+ odone_last = odonec;
+
+ if (eff_status_c != SOX_SUCCESS)
+ effstatus = SOX_EOF;
+ }
+
+ for (i = 0; i < odone_last; ++i)
+ for (f = 0; f < effects[e][0].flows; ++f)
+ *obuf++ = obufc[f][i];
+
+ idone = f * idone_last;
+ odone = f * odone_last;
+ }
+ effects[e - 1][0].odone += idone;
+ if (effects[e - 1][0].odone == effects[e - 1][0].olen) /* Can reuse this buffer? */
+ effects[e - 1][0].odone = effects[e - 1][0].olen = 0;
+
+ effects[e][0].olen += odone;
+
+ return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
+}
+
+static int drain_effect(unsigned e)
+{
+ sox_size_t i, f, odone;
+ int effstatus = SOX_SUCCESS;
+ int (*drain)(eff_t, sox_ssample_t*, sox_size_t*) =
+ effects[e][0].h->drain? effects[e][0].h->drain : sox_effect_nothing_drain;
+
+ odone = sox_bufsiz - effects[e][0].olen;
+
+ if (effects[e][0].flows == 1) /* Run effect on all channels at once */
+ effstatus = drain(&effects[e][0],
+ &effects[e][0].obuf[effects[e][0].olen], &odone);
+ else { /* Run effect on each channel individually */
+ sox_ssample_t *obuf = &effects[e][0].obuf[effects[e][0].olen];
+ sox_size_t odone_last;
+
+ for (f = 0; f < effects[e][0].flows; ++f) {
+ sox_size_t odonec = odone / effects[e][0].flows;
+ int eff_status_c =
+ drain(&effects[e][f], obufc[f], &odonec);
+ if (f && (odonec != odone_last)) {
+ sox_fail("'%s' drained asymmetrically!", effects[e][0].name);
+ effstatus = SOX_EOF;
+ }
+ odone_last = odonec;
+
+ if (eff_status_c != SOX_SUCCESS)
+ effstatus = SOX_EOF;
+ }
+
+ for (i = 0; i < odone_last; ++i)
+ for (f = 0; f < effects[e][0].flows; ++f)
+ *obuf++ = obufc[f][i];
+ odone = f * odone_last;
+ }
+ if (!odone)
+ effstatus = SOX_EOF;
+
+ effects[e][0].olen += odone;
+
+ return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
+}
+
+int flow_effects(void (* update_status)(sox_bool), sox_bool * user_abort)
+{
+ int e, source_e = 0; /* effect indices */
+ int flow_status = SOX_SUCCESS;
+ sox_bool draining = sox_true;
+ sox_size_t f, max_flows = 0;
+
+ for (e = 0; e < (int)neffects; ++e) {
+ effects[e][0].obuf = xmalloc(sox_bufsiz * sizeof(effects[e][0].obuf[0]));
+ effects[e][0].odone = effects[e][0].olen = 0;
+ max_flows = max(max_flows, effects[e][0].flows);
+ }
+
+ ibufc = xcalloc(max_flows, sizeof(*ibufc));
+ obufc = xcalloc(max_flows, sizeof(*obufc));
+ for (f = 0; f < max_flows; ++f) {
+ ibufc[f] = xcalloc(sox_bufsiz / 2, sizeof(ibufc[f][0]));
+ obufc[f] = xcalloc(sox_bufsiz / 2, sizeof(obufc[f][0]));
+ }
+
+ --e;
+ while (source_e < (int)neffects) {
+ if (e == source_e && (draining || effects[e - 1][0].odone == effects[e - 1][0].olen)) {
+ if (drain_effect(e) == SOX_EOF) {
+ ++source_e;
+ draining = sox_false;
+ }
+ } else if (flow_effect(e) == SOX_EOF) {
+ flow_status = SOX_EOF;
+ source_e = e;
+ draining = sox_true;
+ }
+ if (effects[e][0].odone < effects[e][0].olen)
+ ++e;
+ else if (--e < source_e)
+ e = source_e;
+
+ update_status(*user_abort || source_e == (int)neffects);
+
+ if (*user_abort) /* Don't get stuck in this loop. */
+ return SOX_EOF;
+ }
+
+ for (f = 0; f < max_flows; ++f) {
+ free(ibufc[f]);
+ free(obufc[f]);
+ }
+ free(obufc);
+ free(ibufc);
+
+ for (e = 0; e < (int)neffects; ++e)
+ free(effects[e][0].obuf);
+
+ return flow_status;
+}
+
+void delete_effects(void)
+{
+ while (neffects)
+ free(effects[--neffects]);
+}
--- a/src/sox.c
+++ b/src/sox.c
@@ -72,7 +72,9 @@
#endif
static sox_bool play = sox_false, rec = sox_false;
+#ifdef HAVE_LTDL_H
static sox_bool plugins_initted = sox_false;
+#endif
static enum {sox_sequence, sox_concatenate, sox_mix, sox_merge} combine_method = sox_concatenate;
static sox_size_t mixing_clips = 0;
static sox_bool repeatable_random = sox_false; /* Whether to invoke srand. */
@@ -91,8 +93,6 @@
static unsigned long read_wide_samples = 0;
static unsigned long output_samples = 0;
-static sox_ssample_t *ibufl, *ibufr, *obufl, *obufr; /* Left/right interleave buffers */
-
typedef struct file_info
{
char *filename;
@@ -112,15 +112,6 @@
static int process(void);
static void update_status(sox_bool all_done);
static void report_file_info(file_t f);
-static void parse_effects(int argc, char **argv);
-static void build_effects_table(void);
-static int start_all_effects(void);
-static int flow_effect_out(void);
-static int flow_effect(unsigned);
-static int drain_effect_out(void);
-static int drain_effect(unsigned);
-static void stop_effects(void);
-static void kill_effects(void);
#define MAX_INPUT_FILES 32
#define MAX_FILES MAX_INPUT_FILES + 2 /* 1 output file plus record input */
@@ -138,28 +129,11 @@
* converting a mono file to stereo. This allows the resample to work
* on half the data.
*
- * Real effects table only needs to be 2 entries bigger then the user
- * specified table. This is because at most we will need to add
- * a resample effect and a channel averaging effect.
+ * User effects table must be 4 entries smaller then the real
+ * effects table. This is because at most we will need to add
+ * a resample effect, a channel mixing effect, the input, and the output.
*/
-#define MAX_EFF 16
-#define MAX_USER_EFF 14
-
-/*
- * efftab[0] is a dummy entry used only as an input buffer for
- * reading input data into.
- *
- * If one was to support effects for quad-channel files, there would
- * need to be an effect table for each channel to handle effects
- * that don't set SOX_EFF_MCHAN.
- */
-
-static struct sox_effect efftab[MAX_EFF]; /* left/mono channel effects */
-static struct sox_effect efftabR[MAX_EFF];/* right channel effects */
-static unsigned neffects; /* # of effects to run on data */
-static unsigned input_eff; /* last input effect with data */
-static sox_bool input_eff_eof; /* has input_eff reached EOF? */
-
+#define MAX_USER_EFF (MAX_EFFECTS - 4)
static struct sox_effect user_efftab[MAX_USER_EFF];
static unsigned nuser_effects;
@@ -194,7 +168,6 @@
static void cleanup(void)
{
size_t i;
- int ret;
/* Close the input and output files before exiting. */
for (i = 0; i < input_count; i++) {
@@ -249,7 +222,7 @@
return f;
}
-static void set_device(file_t f, sox_bool recording)
+static void set_device(file_t f, sox_bool recording UNUSED)
{
#ifdef HAVE_LIBAO
if (!recording) {
@@ -454,6 +427,37 @@
}
}
+static void parse_effects(int argc, char **argv)
+{
+ int argc_effect;
+
+ for (nuser_effects = 0; optind < argc; ++nuser_effects) {
+ struct sox_effect * e = &user_efftab[nuser_effects];
+ int (*getopts)(eff_t effp, int argc, char *argv[]);
+
+ if (nuser_effects >= MAX_USER_EFF) {
+ sox_fail("too many effects specified (at most %i allowed)", MAX_USER_EFF);
+ exit(1);
+ }
+
+ argc_effect = sox_geteffect_opt(e, argc - optind, &argv[optind]);
+ if (argc_effect == SOX_EOF) {
+ sox_fail("Effect `%s' does not exist!", argv[optind]);
+ exit(1);
+ }
+ if (e->h->flags & SOX_EFF_DEPRECATED)
+ sox_warn("Effect `%s' is deprecated and may be removed in a future release; please refer to the manual sox(1) for an alternative effect", e->name);
+
+ optind++; /* Skip past effect name */
+ e->global_info = &effects_global_info;
+ getopts = e->h->getopts? e->h->getopts : sox_effect_nothing_getopts;
+ if (getopts(e, argc_effect, &argv[optind]) == SOX_EOF)
+ exit(2);
+
+ optind += argc_effect; /* Skip past the effect arguments */
+ }
+}
+
/* FIXME: Use vasprintf */
#ifdef HAVE_LTDL_H
#define MAX_NAME_LEN 1024
@@ -521,12 +525,6 @@
if desired) */
find_formats();
- /* Allocate buffers, size of which may have been set by --buffer */
- ibufl = xcalloc(sox_bufsiz / 2, sizeof(sox_ssample_t));
- obufl = xcalloc(sox_bufsiz / 2, sizeof(sox_ssample_t));
- ibufr = xcalloc(sox_bufsiz / 2, sizeof(sox_ssample_t));
- obufr = xcalloc(sox_bufsiz / 2, sizeof(sox_ssample_t));
-
/* Make sure we got at least the required # of input filenames */
input_count = file_count ? file_count - 1 : 0;
if (input_count < (combine_method <= sox_concatenate ? 1 : 2))
@@ -590,7 +588,7 @@
}
for (i = 0; i < input_count; i++) {
- int j;
+ unsigned j;
for (j =0; j < nuser_effects && !files[i]->desc->signal.channels; ++j)
files[i]->desc->signal.channels = user_efftab[j].ininfo.channels;
if (!files[i]->desc->signal.channels)
@@ -635,6 +633,8 @@
if (show_progress) {
if (user_abort)
fprintf(stderr, "Aborted.\n");
+ else if (user_skip)
+ fprintf(stderr, "Skipped.\n");
else
fprintf(stderr, "Done.\n");
}
@@ -762,9 +762,9 @@
* This hack is a huge time savings when trimming
* gigs of audio data into managable chunks
*/
- if (input_count == 1 && neffects > 1 && strcmp(efftab[1].name, "trim") == 0) {
+ if (input_count == 1 && neffects > 1 && strcmp(effects[1][0].name, "trim") == 0) {
if ((files[0]->desc->h->flags & SOX_FILE_SEEK) && files[0]->desc->seekable){
- sox_size_t offset = sox_trim_get_start(&efftab[1]);
+ sox_size_t offset = sox_trim_get_start(&effects[1][0]);
if (sox_seek(files[0]->desc, offset, SOX_SEEK_SET) != SOX_EOF) {
read_wide_samples = offset / files[0]->desc->signal.channels;
/* Assuming a failed seek stayed where it was. If the
@@ -771,7 +771,7 @@
* seek worked then reset the start location of
* trim so that it thinks user didn't request a skip.
*/
- sox_trim_clear_start(&efftab[1]);
+ sox_trim_clear_start(&effects[1][0]);
}
}
}
@@ -1029,6 +1029,10 @@
static void progress_to_file(file_t f)
{
+ if (user_skip) {
+ user_skip = sox_false;
+ fprintf(stderr, "Skipped.\n");
+ }
read_wide_samples = 0;
input_wide_samples = f->desc->length / f->desc->signal.channels;
if (show_progress && (sox_output_verbosity_level < 3 ||
@@ -1070,9 +1074,9 @@
files[i]->desc->signal.rate == files[i - 1]->desc->signal.rate;
}
-static sox_size_t sox_read_wide(ft_t desc, sox_ssample_t * buf)
+static sox_size_t sox_read_wide(ft_t desc, sox_ssample_t * buf, sox_size_t max)
{
- sox_size_t len = sox_bufsiz / combiner.channels;
+ sox_size_t len = max / combiner.channels;
len = sox_read(desc, buf, len * desc->signal.channels) / desc->signal.channels;
if (!len && desc->sox_errno)
sox_fail("%s: %s (%s)", desc->filename, desc->sox_errstr, strerror(desc->sox_errno));
@@ -1090,293 +1094,134 @@
}
}
-/*
- * Process: Input(s) -> Balancing -> Combiner -> Effects -> Output
- */
-
-static int process(void) {
- int flowstatus = 0;
- sox_size_t e, ws, s, i;
- sox_size_t ilen[MAX_INPUT_FILES];
+typedef struct input_combiner
+{
sox_ssample_t *ibuf[MAX_INPUT_FILES];
- sox_bool known_length = combine_method != sox_sequence;
- sox_size_t olen = 0;
+} * input_combiner_t;
- combiner = files[current_input]->desc->signal;
- if (combine_method == sox_sequence) {
- if (!current_input) for (i = 0; i < input_count; i++)
- report_file_info(files[i]);
- } else {
- sox_size_t total_channels = 0;
- sox_size_t min_channels = SOX_SIZE_MAX;
- sox_size_t max_channels = 0;
- sox_size_t min_rate = SOX_SIZE_MAX;
- sox_size_t max_rate = 0;
+assert_static(sizeof(struct input_combiner) <= SOX_MAX_EFFECT_PRIVSIZE,
+ /* else */ input_combiner_PRIVSIZE_too_big);
- for (i = 0; i < input_count; i++) { /* Report all inputs, then check */
- report_file_info(files[i]);
- total_channels += files[i]->desc->signal.channels;
- min_channels = min(min_channels, files[i]->desc->signal.channels);
- max_channels = max(max_channels, files[i]->desc->signal.channels);
- min_rate = min(min_rate, files[i]->desc->signal.rate);
- max_rate = max(max_rate, files[i]->desc->signal.rate);
- known_length = known_length && files[i]->desc->length != 0;
- if (combine_method == sox_concatenate)
- olen += files[i]->desc->length / files[i]->desc->signal.channels;
- else
- olen = max(olen, files[i]->desc->length / files[i]->desc->signal.channels);
- }
- if (min_rate != max_rate)
- sox_fail("Input files must have the same sample-rate");
- if (min_channels != max_channels) {
- if (combine_method == sox_concatenate) {
- sox_fail("Input files must have the same # channels");
- exit(1);
- } else if (combine_method == sox_mix)
- sox_warn("Input files don't have the same # channels");
- }
- if (min_rate != max_rate)
- exit(1);
+static int combiner_start(eff_t effp)
+{
+ input_combiner_t z = (input_combiner_t) effp->priv;
+ sox_size_t ws, i;
- combiner.channels =
- combine_method == sox_merge? total_channels : max_channels;
- }
-
- ofile->signal = ofile_signal;
- if (ofile->signal.rate == 0)
- ofile->signal.rate = combiner.rate;
- if (ofile->signal.size == -1)
- ofile->signal.size = combiner.size;
- if (ofile->signal.encoding == SOX_ENCODING_UNKNOWN)
- ofile->signal.encoding = combiner.encoding;
- if (ofile->signal.channels == 0)
- ofile->signal.channels = combiner.channels;
-
- combiner.rate = combiner.rate * effects_global_info.speed + .5;
-
- for (i = 0; i < nuser_effects; i++)
- known_length = known_length && !(user_efftab[i].h->flags & SOX_EFF_LENGTH);
-
- if (!known_length)
- olen = 0;
-
- {
- sox_loopinfo_t loops[SOX_MAX_NLOOPS];
- double factor;
- int i;
- char const *comment = NULL;
-
- if (ofile->comment == NULL)
- comment = files[0]->desc->comment ? files[0]->desc->comment : "Processed by SoX";
- else if (*ofile->comment != '\0')
- comment = ofile->comment;
-
- /*
- * copy loop info, resizing appropriately
- * it's in samples, so # channels don't matter
- * FIXME: This doesn't work for multi-file processing or
- * effects that change file length.
- */
- factor = (double) ofile->signal.rate / combiner.rate;
- for (i = 0; i < SOX_MAX_NLOOPS; i++) {
- loops[i].start = files[0]->desc->loops[i].start * factor;
- loops[i].length = files[0]->desc->loops[i].length * factor;
- loops[i].count = files[0]->desc->loops[i].count;
- loops[i].type = files[0]->desc->loops[i].type;
- }
-
- ofile->desc = sox_open_write(overwrite_permitted,
- ofile->filename,
- &ofile->signal,
- ofile->filetype,
- comment,
- olen,
- &files[0]->desc->instr,
- loops);
-
- if (!ofile->desc)
- /* sox_open_write() will call sox_warn for most errors.
- * Rely on that printing something. */
- exit(2);
-
- /* When writing to an audio device, auto turn on the
- * progress display to match behavior of ogg123,
- * unless the user requested us not to display anything. */
- if (show_progress == SOX_OPTION_DEFAULT)
- show_progress = (ofile->desc->h->flags & SOX_FILE_DEVICE) != 0 &&
- (ofile->desc->h->flags & SOX_FILE_PHONY) == 0;
-
- report_file_info(ofile);
- }
-
- build_effects_table();
-
- if (start_all_effects() != SOX_SUCCESS)
- exit(2); /* The failing effect should have displayed an error message */
-
- /* Allocate output buffers for effects */
- for (e = 0; e < neffects; e++) {
- efftab[e].obuf = (sox_ssample_t *)xmalloc(sox_bufsiz * sizeof(sox_ssample_t));
- if (efftabR[e].name)
- efftabR[e].obuf = (sox_ssample_t *)xmalloc(sox_bufsiz * sizeof(sox_ssample_t));
- }
-
if (combine_method <= sox_concatenate)
progress_to_file(files[current_input]);
else {
ws = 0;
for (i = 0; i < input_count; i++) {
- ibuf[i] = (sox_ssample_t *)xmalloc(sox_bufsiz * sizeof(sox_ssample_t));
+ z->ibuf[i] = (sox_ssample_t *)xmalloc(sox_bufsiz * sizeof(sox_ssample_t));
progress_to_file(files[i]);
ws = max(ws, input_wide_samples);
}
input_wide_samples = ws; /* Output length is that of longest input file. */
}
+ return SOX_SUCCESS;
+}
- optimize_trim();
+static int combiner_drain(eff_t effp, sox_ssample_t * obuf, sox_size_t * osamp)
+{
+ input_combiner_t z = (input_combiner_t) effp->priv;
+ sox_size_t ws, s, i;
+ sox_size_t ilen[MAX_INPUT_FILES];
+ sox_size_t olen = 0;
- input_eff = 0;
- input_eff_eof = sox_false;
-
- /* mark chain as empty */
- for(e = 1; e < neffects; e++)
- efftab[e].odone = efftab[e].olen = 0;
-
- signal(SIGINT, sigint);
- signal(SIGTERM, sigint);
- /* Run input data through effects until EOF (olen == 0) or user-abort. */
- do {
- efftab[0].olen = 0;
- if (combine_method <= sox_concatenate) {
- if (!user_skip)
- efftab[0].olen = sox_read_wide(files[current_input]->desc, efftab[0].obuf);
- if (efftab[0].olen == 0) { /* If EOF, go to the next input file. */
- update_status(sox_true);
- if (user_skip) {
- user_skip = sox_false;
- fprintf(stderr, "Skipped.\n");
- }
- if (++current_input < input_count) {
- if (combine_method == sox_sequence && !can_segue(current_input))
- break;
- progress_to_file(files[current_input]);
- continue;
- }
+ if (combine_method <= sox_concatenate) while (sox_true) {
+ if (!user_skip)
+ olen = sox_read_wide(files[current_input]->desc, obuf, *osamp);
+ if (olen == 0) { /* If EOF, go to the next input file. */
+ if (++current_input < input_count) {
+ if (combine_method == sox_sequence && !can_segue(current_input))
+ break;
+ progress_to_file(files[current_input]);
+ continue;
}
- balance_input(efftab[0].obuf, efftab[0].olen, files[current_input]);
- } else {
- sox_ssample_t * p = efftab[0].obuf;
- for (i = 0; i < input_count; ++i) {
- ilen[i] = sox_read_wide(files[i]->desc, ibuf[i]);
- balance_input(ibuf[i], ilen[i], files[i]);
- efftab[0].olen = max(efftab[0].olen, ilen[i]);
- }
- for (ws = 0; ws < efftab[0].olen; ++ws) /* wide samples */
- if (combine_method == sox_mix) { /* sum samples together */
- for (s = 0; s < combiner.channels; ++s, ++p) {
- *p = 0;
- for (i = 0; i < input_count; ++i)
- if (ws < ilen[i] && s < files[i]->desc->signal.channels) {
- /* Cast to double prevents integer overflow */
- double sample = *p + (double)ibuf[i][ws * files[i]->desc->signal.channels + s];
- *p = SOX_ROUND_CLIP_COUNT(sample, mixing_clips);
- }
- }
- } else { /* sox_merge: like a multi-track recorder */
+ }
+ balance_input(obuf, olen, files[current_input]);
+ break;
+ } else {
+ sox_ssample_t * p = obuf;
+ for (i = 0; i < input_count; ++i) {
+ ilen[i] = sox_read_wide(files[i]->desc, z->ibuf[i], *osamp);
+ balance_input(z->ibuf[i], ilen[i], files[i]);
+ olen = max(olen, ilen[i]);
+ }
+ for (ws = 0; ws < olen; ++ws) /* wide samples */
+ if (combine_method == sox_mix) { /* sum samples together */
+ for (s = 0; s < effp->ininfo.channels; ++s, ++p) {
+ *p = 0;
for (i = 0; i < input_count; ++i)
- for (s = 0; s < files[i]->desc->signal.channels; ++s)
- *p++ = (ws < ilen[i]) * ibuf[i][ws * files[i]->desc->signal.channels + s];
- }
+ if (ws < ilen[i] && s < files[i]->desc->signal.channels) {
+ /* Cast to double prevents integer overflow */
+ double sample = *p + (double)z->ibuf[i][ws * files[i]->desc->signal.channels + s];
+ *p = SOX_ROUND_CLIP_COUNT(sample, mixing_clips);
+ }
+ }
+ } else { /* sox_merge: like a multi-track recorder */
+ for (i = 0; i < input_count; ++i)
+ for (s = 0; s < files[i]->desc->signal.channels; ++s)
+ *p++ = (ws < ilen[i]) * z->ibuf[i][ws * files[i]->desc->signal.channels + s];
}
- if (efftab[0].olen == 0)
- break;
+ }
+ read_wide_samples += olen;
+ olen *= effp->ininfo.channels;
+ *osamp = olen;
+ return olen? SOX_SUCCESS : SOX_EOF;
+}
- efftab[0].odone = 0;
- read_wide_samples += efftab[0].olen;
- efftab[0].olen *= combiner.channels;
- flowstatus = flow_effect_out();
- update_status(user_abort || ofile->desc->sox_errno || flowstatus);
+static int combiner_stop(eff_t effp)
+{
+ input_combiner_t z = (input_combiner_t) effp->priv;
+ sox_size_t i;
- /* Quit reading/writing on user aborts. This will close
- * the files nicely as if an EOF was reached on read. */
- if (user_abort)
- break;
-
- /* If there's an error, don't try to write more. */
- if (ofile->desc->sox_errno)
- break;
- } while (flowstatus == 0);
-
- /* Drain the effects; don't write if output is indicating errors. */
- if (ofile->desc->sox_errno == 0)
- drain_effect_out();
-
if (combine_method > sox_concatenate)
/* Free input buffers now that they are not used */
for (i = 0; i < input_count; i++)
- free(ibuf[i]);
+ free(z->ibuf[i]);
- /* Free output buffers now that they won't be used */
- for (e = 0; e < neffects; e++) {
- free(efftab[e].obuf);
- free(efftabR[e].obuf);
- }
-
- /* N.B. more data may be written during stop_effects */
- stop_effects();
- return flowstatus;
+ return SOX_SUCCESS;
}
-static void parse_effects(int argc, char **argv)
+static sox_effect_t const * input_combiner_effect_fn(void)
{
- int argc_effect;
+ static sox_effect_t driver = {
+ "input", 0, SOX_EFF_MCHAN,
+ 0, combiner_start, 0, combiner_drain, combiner_stop, 0
+ };
+ return &driver;
+}
- for (nuser_effects = 0; optind < argc; ++nuser_effects) {
- struct sox_effect * e = &user_efftab[nuser_effects];
- int (*getopts)(eff_t effp, int argc, char *argv[]);
-
- if (nuser_effects >= MAX_USER_EFF) {
- sox_fail("too many effects specified (at most %i allowed)", MAX_USER_EFF);
- exit(1);
+static int output_flow(eff_t effp UNUSED, sox_ssample_t const * ibuf,
+ sox_ssample_t * obuf UNUSED, sox_size_t * isamp, sox_size_t * osamp)
+{
+ size_t len;
+ for (*osamp = *isamp; *osamp; ibuf += len, *osamp -= len) {
+ len = sox_write(ofile->desc, ibuf, *osamp);
+ if (len == 0) {
+ sox_warn("Error writing: %s", ofile->desc->sox_errstr);
+ return SOX_EOF;
}
-
- argc_effect = sox_geteffect_opt(e, argc - optind, &argv[optind]);
- if (argc_effect == SOX_EOF) {
- sox_fail("Effect `%s' does not exist!", argv[optind]);
- exit(1);
- }
- if (e->h->flags & SOX_EFF_DEPRECATED)
- sox_warn("Effect `%s' is deprecated and may be removed in a future release; please refer to the manual sox(1) for an alternative effect", e->name);
-
- optind++; /* Skip past effect name */
- e->global_info = &effects_global_info;
- getopts = e->h->getopts? e->h->getopts : sox_effect_nothing_getopts;
- if (getopts(e, argc_effect, &argv[optind]) == SOX_EOF)
- exit(2);
-
- optind += argc_effect; /* Skip past the effect arguments */
+ if (user_abort) /* Don't get stuck in this loop. */
+ return SOX_EOF;
}
+ output_samples += *isamp / ofile->desc->signal.channels;
+ return SOX_SUCCESS;
}
-static void add_effect(int * effects_mask)
+static sox_effect_t const * output_effect_fn(void)
{
- struct sox_effect * e = &efftab[neffects];
-
- /* Copy format info to effect table */
- *effects_mask =
- sox_updateeffect(e, &combiner, &ofile->desc->signal, *effects_mask);
-
- /* If this effect can't handle multiple channels then account for this. */
- if (e->ininfo.channels > 1 && !(e->h->flags & SOX_EFF_MCHAN))
- memcpy(&efftabR[neffects], e, sizeof(*e));
- else memset(&efftabR[neffects], 0, sizeof(*e));
-
- ++neffects;
+ static sox_effect_t driver = {
+ "output", 0, SOX_EFF_MCHAN, 0, 0, output_flow, 0, 0, 0
+ };
+ return &driver;
}
static void add_default_effect(char const * name, int * effects_mask)
{
- struct sox_effect * e = &efftab[neffects];
+ struct sox_effect eff;
+ struct sox_effect * e = &eff;
int (*getopts)(eff_t effp, int argc, char *argv[]);
/* Find effect and update initial pointers */
@@ -1388,12 +1233,12 @@
if (getopts(e, 0, NULL) == SOX_EOF)
exit(2);
- add_effect(effects_mask);
+ add_effect(e, &combiner, &ofile->desc->signal, effects_mask);
}
/* If needed effects are not given, auto-add at (performance) optimal point.
*/
-static void build_effects_table(void)
+static void add_effects(void)
{
unsigned i;
int effects_mask = 0;
@@ -1401,6 +1246,7 @@
sox_bool need_chan = combiner.channels != ofile->desc->signal.channels;
int user_mchan = -1;
sox_size_t channels = combiner.channels;
+ struct sox_effect eff;
{ /* Check if we have to add effects to change rate/chans or if the
user has specified effects to do this, in which case, check if
@@ -1429,12 +1275,11 @@
}
}
- /* --------- add the effects ------------------------ */
+ eff.h = input_combiner_effect_fn();
+ eff.name = eff.h->name;
+ add_effect(&eff, &combiner, &ofile->desc->signal, &effects_mask);
- /* efftab[0] is always the input stream and always exists */
- neffects = 1;
-
- /* Copy user specified effects into the real efftab */
+ /* Copy user specified effects into the real effects */
for (i = 0; i <= nuser_effects; i++) {
/* If reducing channels, it's faster to do so before all other effects: */
if ((int)i > user_mchan && need_chan && combiner.channels > ofile->desc->signal.channels) {
@@ -1445,432 +1290,164 @@
/* If reducing rate, it's faster to do so before all other effects
* (except reducing channels): */
if (need_rate)
- if (i == nuser_effects || (channels <= 2 && combiner.rate > ofile->desc->signal.rate)) {
+ if (i == nuser_effects || combiner.rate > ofile->desc->signal.rate) {
add_default_effect("resample", &effects_mask);
need_rate = sox_false;
}
- if (i < nuser_effects) {
- memcpy(&efftab[neffects], &user_efftab[i], sizeof(efftab[0]));
- add_effect(&effects_mask);
- }
+ if (i < nuser_effects)
+ add_effect(&user_efftab[i], &combiner, &ofile->desc->signal, &effects_mask);
}
if (need_chan)
add_default_effect("mixer", &effects_mask);
-}
-static int start_all_effects(void)
-{
- unsigned i, j;
- int ret = SOX_SUCCESS;
-
- for (i = 1; i < neffects; i++) {
- struct sox_effect * e = &efftab[i];
- sox_bool is_always_null = (e->h->flags & SOX_EFF_NULL) != 0;
- int (*start)(eff_t effp) = e->h->start? e->h->start : sox_effect_nothing;
-
- if (is_always_null)
- sox_report("'%s' has no effect (is a proxy effect)", e->name);
- else {
- e->clips = 0;
- ret = start(e);
- if (ret == SOX_EFF_NULL)
- sox_warn("'%s' has no effect in this configuration", e->name);
- else if (ret != SOX_SUCCESS)
- return SOX_EOF;
- }
- if (is_always_null || ret == SOX_EFF_NULL) { /* remove from the chain */
- int (*kill)(eff_t effp) = e->h->kill? e->h->kill: sox_effect_nothing;
-
- /* No left & right kill as there is no left & right getopts */
- kill(e);
- --neffects;
- for (j = i--; j < neffects; ++j) {
- efftab[j] = efftab[j + 1];
- efftabR[j] = efftabR[j + 1];
- }
- }
- /* No null checks here; the left channel looks after this */
- else if (efftabR[i].name) {
- efftabR[i].clips = 0;
- if (start(&efftabR[i]) != SOX_SUCCESS)
- return SOX_EOF;
- }
- }
- for (i = 1; i < neffects; ++i) {
- struct sox_effect * e = &efftab[i];
- if (e->ininfo.channels > 2 && !(e->h->flags & SOX_EFF_MCHAN)) {
- sox_fail("Sorry, effect '%s' cannot handle multiple channels, and SoX can only help out in the case of 2 channels", e->name);
- return SOX_EOF;
- }
- sox_report("Effects chain: %-10s %-6s %uHz", e->name,
- e->ininfo.channels < 2 ? "mono" :
- (e->h->flags & SOX_EFF_MCHAN)? "multi" : "stereo", e->ininfo.rate);
- }
- return SOX_SUCCESS;
+ eff.h = output_effect_fn();
+ eff.name = eff.h->name;
+ add_effect(&eff, &combiner, &ofile->desc->signal, &effects_mask);
}
-static int flow_effect_out(void)
-{
- int havedata, flowstatus = 0;
- size_t e, len, total;
+/*
+ * Process: Input(s) -> Balancing -> Combiner -> Effects -> Output
+ */
- do {
- /* run entire chain BACKWARDS: pull, don't push.*/
- /* this is because buffering system isn't a nice queueing system */
- for (e = neffects - 1; e && (int)e >= (int)input_eff; e--) {
- /* Do not call flow effect on input if it has reported
- * EOF already as that's a waste of time and may
- * do bad things.
- */
- if (e == input_eff && input_eff_eof)
- continue;
+static int process(void) {
+ int flowstatus = 0;
+ sox_size_t i;
+ sox_bool known_length = combine_method != sox_sequence;
+ sox_size_t olen = 0;
- /* flow_effect returns SOX_EOF when it will not process
- * any more samples. This is used to bail out early.
- * Since we are "pulling" data, it is OK that we are not
- * calling any more previous effects since their output
- * would not be looked at anyways.
- */
- flowstatus = flow_effect(e);
- if (flowstatus == SOX_EOF) {
- input_eff = e;
- /* Assume next effect hasn't reach EOF yet */
- input_eff_eof = sox_false;
- }
+ combiner = files[current_input]->desc->signal;
+ if (combine_method == sox_sequence) {
+ if (!current_input) for (i = 0; i < input_count; i++)
+ report_file_info(files[i]);
+ } else {
+ sox_size_t total_channels = 0;
+ sox_size_t min_channels = SOX_SIZE_MAX;
+ sox_size_t max_channels = 0;
+ sox_size_t min_rate = SOX_SIZE_MAX;
+ sox_size_t max_rate = 0;
- /* If this buffer contains more input data then break out
- * of this loop now. This will allow us to loop back around
- * and reprocess the rest of this input buffer: we finish each
- * effect before moving on to the next, so that each effect
- * starts with an empty output buffer.
- */
- if (efftab[e].odone < efftab[e].olen) {
- sox_debug_more("Breaking out of loop to flush buffer");
- break;
- }
+ for (i = 0; i < input_count; i++) { /* Report all inputs, then check */
+ report_file_info(files[i]);
+ total_channels += files[i]->desc->signal.channels;
+ min_channels = min(min_channels, files[i]->desc->signal.channels);
+ max_channels = max(max_channels, files[i]->desc->signal.channels);
+ min_rate = min(min_rate, files[i]->desc->signal.rate);
+ max_rate = max(max_rate, files[i]->desc->signal.rate);
+ known_length = known_length && files[i]->desc->length != 0;
+ if (combine_method == sox_concatenate)
+ olen += files[i]->desc->length / files[i]->desc->signal.channels;
+ else
+ olen = max(olen, files[i]->desc->length / files[i]->desc->signal.channels);
}
-
- /* If outputting and output data was generated then write it */
- if (efftab[neffects - 1].olen > efftab[neffects - 1].odone) {
- total = 0;
- do {
- /* Do not do any more writing during user aborts as
- * we may be stuck in an infinite writing loop.
- */
- if (user_abort)
- return SOX_EOF;
-
- len = sox_write(ofile->desc,
- &efftab[neffects - 1].obuf[total],
- efftab[neffects - 1].olen - total);
-
- if (len == 0) {
- sox_warn("Error writing: %s", ofile->desc->sox_errstr);
- return SOX_EOF;
- }
- total += len;
- } while (total < efftab[neffects-1].olen);
- output_samples += (total / ofile->desc->signal.channels);
- } else {
- /* Make it look like everything was consumed */
- output_samples += (efftab[neffects-1].olen /
- ofile->desc->signal.channels);
+ if (min_rate != max_rate)
+ sox_fail("Input files must have the same sample-rate");
+ if (min_channels != max_channels) {
+ if (combine_method == sox_concatenate) {
+ sox_fail("Input files must have the same # channels");
+ exit(1);
+ } else if (combine_method == sox_mix)
+ sox_warn("Input files don't have the same # channels");
}
- efftab[neffects-1].odone = efftab[neffects-1].olen = 0;
+ if (min_rate != max_rate)
+ exit(1);
- /* if stuff still in pipeline, set up to flow effects again */
- /* When all effects have reported SOX_EOF then this check will
- * show no more data.
- */
- havedata = 0;
- for (e = neffects - 1; (int)e >= (int)input_eff; e--) {
- /* If odone and olen are the same then this buffer
- * can be reused.
- */
- if (efftab[e].odone == efftab[e].olen)
- efftab[e].odone = efftab[e].olen = 0;
-
- if (efftab[e].odone < efftab[e].olen) {
- /* Only mark that we have more data if a full
- * frame that can be written.
- * FIXME: If this error case happens for the
- * input buffer then the data will be lost and
- * will cause stereo channels to be inversed.
- */
- if ((efftab[e].olen - efftab[e].odone) >=
- ofile->desc->signal.channels)
- havedata = 1;
- else
- sox_warn("Received buffer with incomplete amount of samples.");
- /* Don't break out because other things are being
- * done in loop.
- */
- }
- }
-
- if (!havedata && input_eff > 0) {
- /* When EOF has been detected, skip to the next input
- * before looking for more data.
- */
- if (input_eff_eof) {
- input_eff++;
- input_eff_eof = sox_false;
- }
-
- /* If the input file is not returning data then
- * we must prime the pump using the drain effect.
- * After it's primed, the loop will suck the data
- * through. Once an input_eff stops reporting samples,
- * we will continue to the next until all are drained.
- */
- while (input_eff < neffects) {
- int rc = drain_effect(input_eff);
-
- if (efftab[input_eff].olen == 0) {
- input_eff++;
- /* Assume next effect hasn't reached EOF yet. */
- input_eff_eof = sox_false;
- } else {
- havedata = 1;
- input_eff_eof = rc == SOX_EOF;
- break;
- }
- }
- }
- } while (havedata);
-
- /* If input_eff isn't pointing at fake first entry then there
- * is no need to read any more data from disk. Return this
- * fact to caller.
- */
- if (input_eff > 0) {
- sox_debug("Effect return SOX_EOF");
- return SOX_EOF;
+ combiner.channels =
+ combine_method == sox_merge? total_channels : max_channels;
}
- return SOX_SUCCESS;
-}
+ ofile->signal = ofile_signal;
+ if (ofile->signal.rate == 0)
+ ofile->signal.rate = combiner.rate;
+ if (ofile->signal.size == -1)
+ ofile->signal.size = combiner.size;
+ if (ofile->signal.encoding == SOX_ENCODING_UNKNOWN)
+ ofile->signal.encoding = combiner.encoding;
+ if (ofile->signal.channels == 0)
+ ofile->signal.channels = combiner.channels;
-static int flow_effect(unsigned e)
-{
- sox_size_t i, done, idone, odone, idonel, odonel, idoner, odoner;
- const sox_ssample_t *ibuf;
- sox_ssample_t *obuf;
- int effstatus, effstatusl, effstatusr;
- int (*flow)(eff_t, sox_ssample_t const*, sox_ssample_t*, sox_size_t*, sox_size_t*) =
- efftab[e].h->flow? efftab[e].h->flow : sox_effect_nothing_flow;
+ combiner.rate = combiner.rate * effects_global_info.speed + .5;
- /* Do not attempt to do any more effect processing during
- * user aborts as we may be stuck in an infinite flow loop.
- */
- if (user_abort)
- return SOX_EOF;
+ for (i = 0; i < nuser_effects; i++)
+ known_length = known_length && !(user_efftab[i].h->flags & SOX_EFF_LENGTH);
- /* I have no input data ? */
- if (efftab[e - 1].odone == efftab[e - 1].olen) {
- sox_debug_more("%s no data to pull to me!", efftab[e].name);
- return 0;
- }
+ if (!known_length)
+ olen = 0;
- if (!efftabR[e].name) {
- /* No stereo data, or effect can handle stereo data so
- * run effect over entire buffer.
- */
- idone = efftab[e - 1].olen - efftab[e - 1].odone;
- odone = sox_bufsiz - efftab[e].olen;
- sox_debug_more("pre %s idone=%d, odone=%d", efftab[e].name, idone, odone);
- sox_debug_more("pre %s odone1=%d, olen1=%d odone=%d olen=%d", efftab[e].name, efftab[e-1].odone, efftab[e-1].olen, efftab[e].odone, efftab[e].olen);
+ {
+ sox_loopinfo_t loops[SOX_MAX_NLOOPS];
+ double factor;
+ int i;
+ char const *comment = NULL;
- effstatus = flow(&efftab[e],
- &efftab[e - 1].obuf[efftab[e - 1].odone],
- &efftab[e].obuf[efftab[e].olen],
- (sox_size_t *)&idone,
- (sox_size_t *)&odone);
+ if (ofile->comment == NULL)
+ comment = files[0]->desc->comment ? files[0]->desc->comment : "Processed by SoX";
+ else if (*ofile->comment != '\0')
+ comment = ofile->comment;
- efftab[e - 1].odone += idone;
- /* Don't update efftab[e].odone as we didn't consume data */
- efftab[e].olen += odone;
- sox_debug_more("post %s idone=%d, odone=%d", efftab[e].name, idone, odone);
- sox_debug_more("post %s odone1=%d, olen1=%d odone=%d olen=%d", efftab[e].name, efftab[e-1].odone, efftab[e-1].olen, efftab[e].odone, efftab[e].olen);
-
- done = idone + odone;
- } else {
- /* Put stereo data in two separate buffers and run effect
- * on each of them.
+ /*
+ * copy loop info, resizing appropriately
+ * it's in samples, so # channels don't matter
+ * FIXME: This doesn't work for multi-file processing or
+ * effects that change file length.
*/
- idone = efftab[e - 1].olen - efftab[e - 1].odone;
- odone = sox_bufsiz - efftab[e].olen;
-
- ibuf = &efftab[e - 1].obuf[efftab[e - 1].odone];
- for (i = 0; i < idone; i += 2) {
- ibufl[i / 2] = *ibuf++;
- ibufr[i / 2] = *ibuf++;
+ factor = (double) ofile->signal.rate / combiner.rate;
+ for (i = 0; i < SOX_MAX_NLOOPS; i++) {
+ loops[i].start = files[0]->desc->loops[i].start * factor;
+ loops[i].length = files[0]->desc->loops[i].length * factor;
+ loops[i].count = files[0]->desc->loops[i].count;
+ loops[i].type = files[0]->desc->loops[i].type;
}
- /* left */
- idonel = (idone + 1) / 2; /* odd-length logic */
- odonel = odone / 2;
- sox_debug_more("pre %s idone=%d, odone=%d", efftab[e].name, idone, odone);
- sox_debug_more("pre %s odone1=%d, olen1=%d odone=%d olen=%d", efftab[e].name, efftab[e - 1].odone, efftab[e - 1].olen, efftab[e].odone, efftab[e].olen);
+ ofile->desc = sox_open_write(overwrite_permitted,
+ ofile->filename,
+ &ofile->signal,
+ ofile->filetype,
+ comment,
+ olen,
+ &files[0]->desc->instr,
+ loops);
- effstatusl = flow(&efftab[e],
- ibufl, obufl, (sox_size_t *)&idonel,
- (sox_size_t *)&odonel);
+ if (!ofile->desc)
+ /* sox_open_write() will call sox_warn for most errors.
+ * Rely on that printing something. */
+ exit(2);
- /* right */
- idoner = idone / 2; /* odd-length logic */
- odoner = odone / 2;
- effstatusr = flow(&efftabR[e],
- ibufr, obufr, (sox_size_t *)&idoner,
- (sox_size_t *)&odoner);
+ /* When writing to an audio device, auto turn on the
+ * progress display to match behavior of ogg123,
+ * unless the user requested us not to display anything. */
+ if (show_progress == SOX_OPTION_DEFAULT)
+ show_progress = (ofile->desc->h->flags & SOX_FILE_DEVICE) != 0 &&
+ (ofile->desc->h->flags & SOX_FILE_PHONY) == 0;
- obuf = &efftab[e].obuf[efftab[e].olen];
- /* This loop implies left and right effect will always output
- * the same amount of data.
- */
- for (i = 0; i < odoner; i++) {
- *obuf++ = obufl[i];
- *obuf++ = obufr[i];
- }
- efftab[e-1].odone += idonel + idoner;
- /* Don't zero efftab[e].odone since nothing has been consumed yet */
- efftab[e].olen += odonel + odoner;
- sox_debug_more("post %s idone=%d, odone=%d", efftab[e].name, idone, odone);
- sox_debug_more("post %s odone1=%d, olen1=%d odone=%d olen=%d", efftab[e].name, efftab[e - 1].odone, efftab[e - 1].olen, efftab[e].odone, efftab[e].olen);
-
- done = idonel + idoner + odonel + odoner;
-
- if (effstatusl)
- effstatus = effstatusl;
- else
- effstatus = effstatusr;
+ report_file_info(ofile);
}
- if (effstatus == SOX_EOF)
- return SOX_EOF;
- if (done == 0) {
- sox_fail("'%s' effect took & gave no samples!", efftab[e].name);
- exit(2);
- }
- return SOX_SUCCESS;
-}
-static int drain_effect_out(void)
-{
- /* Skip past input effect since we know thats not needed */
- if (input_eff == 0) {
- input_eff = 1;
- /* Assuming next effect hasn't reached EOF yet. */
- input_eff_eof = sox_false;
- }
+ add_effects();
+ if (start_effects() != SOX_SUCCESS)
+ exit(2); /* The failing effect should have displayed an error message */
- /* Try to prime the pump with some data */
- while (input_eff < neffects) {
- int rc = drain_effect(input_eff);
+ optimize_trim();
- if (efftab[input_eff].olen == 0) {
- input_eff++;
- /* Assuming next effect hasn't reached EOF yet. */
- input_eff_eof = sox_false;
- } else {
- input_eff_eof = rc == SOX_EOF;
- break;
- }
- }
+ signal(SIGINT, sigint);
+ signal(SIGTERM, sigint);
- /* Just do standard flow routines after the priming. */
- return flow_effect_out();
-}
+ flowstatus = flow_effects(update_status, &user_abort);
-static int drain_effect(unsigned e)
-{
- sox_ssize_t i, olen, olenl, olenr;
- sox_ssample_t *obuf;
- int rc;
- int (*drain)(eff_t effp, sox_ssample_t *obuf, sox_size_t *osamp) =
- efftab[e].h->drain? efftab[e].h->drain : sox_effect_nothing_drain;
-
- if (! efftabR[e].name) {
- efftab[e].olen = sox_bufsiz;
- rc = drain(&efftab[e],efftab[e].obuf, &efftab[e].olen);
- efftab[e].odone = 0;
- } else {
- int rc_l, rc_r;
-
- olen = sox_bufsiz;
-
- /* left */
- olenl = olen/2;
- rc_l = drain(&efftab[e], obufl, (sox_size_t *)&olenl);
-
- /* right */
- olenr = olen/2;
- rc_r = drain(&efftabR[e], obufr, (sox_size_t *)&olenr);
-
- if (rc_l == SOX_EOF || rc_r == SOX_EOF)
- rc = SOX_EOF;
- else
- rc = SOX_SUCCESS;
-
- obuf = efftab[e].obuf;
- /* This loop implies left and right effect will always output
- * the same amount of data.
- */
- for (i = 0; i < olenr; i++) {
- *obuf++ = obufl[i];
- *obuf++ = obufr[i];
- }
- efftab[e].olen = olenl + olenr;
- efftab[e].odone = 0;
- }
- return rc;
+ stop_effects();
+ delete_effects();
+ return flowstatus;
}
-static void stop_effects(void)
-{
- unsigned e;
-
- for (e = 1; e < neffects; e++) {
- sox_size_t clips;
- int (*stop)(eff_t effp) =
- efftab[e].h->stop? efftab[e].h->stop : sox_effect_nothing;
-
- stop(&efftab[e]);
- clips = efftab[e].clips;
-
- if (efftabR[e].name) {
- stop(&efftabR[e]);
- clips += efftab[e].clips;
- }
- if (clips != 0)
- sox_warn("'%s' clipped %u samples; decrease volume?",efftab[e].name,clips);
- }
-}
-
-static void kill_effects(void)
-{
- unsigned e;
-
- for (e = 1; e < neffects; e++) {
- int (*kill)(eff_t effp) =
- efftab[e].h->kill? efftab[e].h->kill : sox_effect_nothing;
-
- /* No left & right kill as there is no left & right getopts */
- kill(&efftab[e]);
- }
-}
-
static sox_size_t total_clips(void)
{
- unsigned i;
+ unsigned i, f;
sox_size_t clips = 0;
for (i = 0; i < file_count; ++i)
clips += files[i]->desc->clips + files[i]->volume_clips;
clips += mixing_clips;
- for (i = 1; i < neffects; ++i) {
- clips += efftab[i].clips;
- if (efftabR[i].name)
- clips += efftab[i].clips;
- }
+ for (i = 1; i < neffects - 1; ++i)
+ for (f = 1; f < effects[i][0].flows; ++f)
+ clips += effects[i][f].clips;
return clips;
}
@@ -1934,6 +1511,53 @@
size_t i, formats;
const char **format_list;
const sox_effect_t *e;
+ static char const * lines[] = {
+"SPECIAL FILENAMES:",
+"- stdin (infile) or stdout (outfile)",
+"-n use the null file handler; for use with e.g. synth & stat",
+"",
+"GLOBAL OPTIONS (gopts) (can be specified at any point before the first effect):",
+"--buffer BYTES set the buffer size (default 8192)",
+"--combine concatenate concatenate multiple input files (default for sox, rec)",
+"--combine sequence sequence multiple input files (default for play)",
+"-h, --help display version number and usage information",
+"--help-effect NAME display usage of specified effect; use `all' to display all",
+"--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)",
+"--octave generate Octave commands 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)",
+"-R use default random numbers (same on each run of SoX)",
+"-S, --show-progress display progress while processing audio data",
+"--version display version number of SoX and exit",
+"-V[LEVEL] increment or set verbosity level (default 2); levels are:",
+" 1: failure messages",
+" 2: warnings",
+" 3: details of processing",
+" 4-6: increasing levels of debug messages",
+"",
+"FORMAT OPTIONS (fopts):",
+"Format options only need to be supplied for input files that are headerless,",
+"otherwise they are obtained automatically. Output files will default to the",
+"same format options as the input file unless otherwise specified.",
+"",
+"-c, --channels CHANNELS number of channels in audio data",
+"-C, --compression FACTOR compression factor for output format",
+"--comment TEXT Specify comment text for the output file",
+"--comment-file FILENAME file containing comment text for the output file",
+"--endian little|big|swap set endianness; swap means opposite to default",
+"-r, --rate RATE sample rate of audio",
+"-t, --type FILETYPE file type of audio",
+"-x invert auto-detected endianness",
+"-N, --reverse-nibbles nibble-order",
+"-X, --reverse-bits bit-order of data",
+"-B/-L force endianness to big/little",
+"-s/-u/-U/-A/ sample encoding: signed/unsigned/u-law/A-law",
+" -a/-i/-g/-f ADPCM/IMA_ADPCM/GSM/floating point",
+"-1/-2/-3/-4/-8 sample size in bytes",
+"-v, --volume FACTOR volume input file volume adjustment factor (real number)",
+""};
printf("%s: ", myname);
printf("SoX Version %s\n\n", PACKAGE_VERSION);
@@ -1941,52 +1565,9 @@
fprintf(stderr, "Failed: %s\n\n", message);
printf("Usage summary: [gopts] [[fopts] infile]... [fopts]%s [effect [effopts]]...\n\n",
play? "" : " outfile");
- printf("SPECIAL FILENAMES:\n"
- "- stdin (infile) or stdout (outfile)\n"
- "-n use the null file handler; for use with e.g. synth & stat\n"
- "\n"
- "GLOBAL OPTIONS (gopts) (can be specified at any point before the first effect):\n"
- "--buffer BYTES set the buffer size (default 8192)\n"
- "--combine concatenate concatenate multiple input files (default for sox, rec)\n"
- "--combine sequence sequence multiple input files (default for play)\n"
- "-h, --help display version number and usage information\n"
- "--help-effect NAME display usage of specified effect; use `all' to display all\n"
- "--interactive prompt to overwrite output file\n"
- "-m, --combine mix mix multiple input files (instead of concatenating)\n"
- "-M, --combine merge merge multiple input files (instead of concatenating)\n"
- "--octave generate Octave commands to plot response of filter effect\n"
- "-q, --no-show-progress run in quiet mode; opposite of -S\n"
- "--replay-gain track|album|off default: off (sox, rec), track (play)\n"
- "-R use default random numbers (same on each run of SoX)\n"
- "-S, --show-progress display progress while processing audio data\n"
- "--version display version number of SoX and exit\n"
- "-V[LEVEL] increment or set verbosity level (default 2); levels are:\n"
- " 1: failure messages\n"
- " 2: warnings\n"
- " 3: details of processing\n"
- " 4-6: increasing levels of debug messages\n"
- "\n"
- "FORMAT OPTIONS (fopts):\n"
- "Format options only need to be supplied for input files that are headerless,\n"
- "otherwise they are obtained automatically. Output files will default to the\n"
- "same format options as the input file unless otherwise specified.\n"
- "\n"
- "-c, --channels CHANNELS number of channels in audio data\n"
- "-C, --compression FACTOR compression factor for output format\n"
- "--comment TEXT Specify comment text for the output file\n"
- "--comment-file FILENAME file containing comment text for the output file\n"
- "--endian little|big|swap set endianness; swap means opposite to default\n"
- "-r, --rate RATE sample rate of audio\n"
- "-t, --type FILETYPE file type of audio\n"
- "-x invert auto-detected endianness\n"
- "-N, --reverse-nibbles nibble-order\n"
- "-X, --reverse-bits bit-order of data\n"
- "-B/-L force endianness to big/little\n"
- "-s/-u/-U/-A/ sample encoding: signed/unsigned/u-law/A-law\n"
- " -a/-i/-g/-f ADPCM/IMA_ADPCM/GSM/floating point\n"
- "-1/-2/-3/-4/-8 sample size in bytes\n"
- "-v, --volume FACTOR volume input file volume adjustment factor (real number)\n"
- "\n");
+
+ for (i = 0; i < array_length(lines); ++i)
+ puts(lines[i]);
printf("SUPPORTED FILE FORMATS:");
for (i = 0, formats = 0; i < sox_formats; i++) {
--- a/src/sox.h
+++ b/src/sox.h
@@ -392,6 +392,7 @@
struct sox_effect
{
char const *name; /* effect name */
+ sox_size_t flows;
struct sox_effects_global_info * global_info;/* global parameters */
struct sox_signalinfo ininfo; /* input signal specifications */
struct sox_signalinfo outinfo; /* output signal specifications */
@@ -432,6 +433,17 @@
int sox_geteffect(eff_t effp, const char *effect_name);
sox_bool is_effect_name(char const *text);
int sox_updateeffect(eff_t effp, const sox_signalinfo_t *in, const sox_signalinfo_t *out, int effect_mask);
+
+#define MAX_EFFECTS 20
+extern struct sox_effect * effects[MAX_EFFECTS];
+extern unsigned neffects; /* # of effects to run on data */
+void add_effect(struct sox_effect * e, sox_signalinfo_t * in, sox_signalinfo_t * out, int * effects_mask);
+int start_effects(void);
+int flow_effects(void (* update_status)(sox_bool), sox_bool * user_abort);
+void stop_effects(void);
+void kill_effects(void);
+void delete_effects(void);
+
int sox_gettype(ft_t, sox_bool);
ft_t sox_initformat(void);
char const * sox_parsesamples(sox_rate_t rate, const char *str, sox_size_t *samples, int def);
--- a/src/util.c
+++ b/src/util.c
@@ -188,6 +188,8 @@
{
int i;
+ memset(effp, 0, sizeof(*effp));
+
for(i = 0; sox_effect_fns[i]; i++) {
const sox_effect_t *e = sox_effect_fns[i]();