ref: 9721e52b1f639cbea9e4d130523bbc915e2c3dea
parent: 190032090e875cbe49893c7a8d3cf6c1c91c6a1a
author: robs <robs>
date: Sat Jan 27 11:18:54 EST 2007
Combiner clean-ups and fixes
--- a/src/alsa.c
+++ b/src/alsa.c
@@ -481,7 +481,7 @@
break;
default:
st_fail_errno(ft,ST_EFMT,"Do not support this encoding for this data size");
- return ST_EOF;
+ return 0;
}
break;
case ST_SIZE_16BIT:
@@ -495,12 +495,12 @@
break;
default:
st_fail_errno(ft,ST_EFMT,"Do not support this encoding for this data size");
- return ST_EOF;
+ return 0;
}
break;
default:
st_fail_errno(ft,ST_EFMT,"Do not support this data size for this handler");
- return ST_EOF;
+ return 0;
}
/* Prevent overflow */
@@ -518,7 +518,7 @@
if (xrun_recovery(alsa->pcm_handle, err) < 0)
{
st_fail_errno(ft, ST_EPERM, "ALSA write error");
- return ST_EOF;
+ return 0;
}
}
else
@@ -635,6 +635,8 @@
err = snd_pcm_writei(alsa->pcm_handle,
alsa->buf + (len * ft->signal.size),
(osamp - len) / ft->signal.channels);
+ if (errno == EAGAIN) /* Happens naturally; don't report it */
+ errno = 0;
if (err < 0) {
if (xrun_recovery(alsa->pcm_handle, err) < 0) {
st_fail_errno(ft, ST_EPERM, "ALSA write error");
--- a/src/sox.c
+++ b/src/sox.c
@@ -56,14 +56,14 @@
#endif
static st_bool play = st_false, rec = st_false;
-static enum {SOX_SEQUENCE, SOX_CONCAT, SOX_MIX, SOX_MERGE} combine_method = SOX_CONCAT;
+static enum {SOX_sequence, SOX_concatenate, SOX_mix, SOX_merge} combine_method = SOX_concatenate;
static st_size_t mixing_clips = 0;
static st_bool repeatable_random = st_false; /* Whether to invoke srand. */
static st_bool interactive = st_false;
static st_globalinfo_t globalinfo = {st_false, 1};
static st_bool uservolume = st_false;
-typedef enum {RG_OFF, RG_TRACK, RG_ALBUM} rg_t;
-static rg_t replay_gain_mode;
+typedef enum {RG_off, RG_track, RG_album} rg_mode;
+static rg_mode replay_gain_mode = RG_off;
static st_bool user_abort = st_false;
static st_bool user_skip = st_false;
@@ -97,7 +97,7 @@
static void usage_effect(char *) NORET;
static void process(void);
static void update_status(void);
-static void balance_input(st_sample_t * buf, st_ssize_t len, file_t);
+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);
@@ -150,7 +150,6 @@
static char *myname = NULL;
-
static void sox_output_message(int level, const char *filename, const char *fmt, va_list ap)
{
if (st_output_verbosity_level >= level) {
@@ -247,13 +246,13 @@
static void set_replay_gain(char const * comment, file_t f)
{
- rg_t rg = replay_gain_mode;
- int try = 2;
+ rg_mode rg = replay_gain_mode;
+ int try = 2; /* Will try to find the other GAIN if preferred one not found */
- if (rg != RG_OFF) while (try--) {
+ if (rg != RG_off) while (try--) {
char const * p = comment;
char const * target =
- rg == RG_TRACK? "REPLAYGAIN_TRACK_GAIN=" : "REPLAYGAIN_ALBUM_GAIN=";
+ rg == RG_track? "REPLAYGAIN_TRACK_GAIN=" : "REPLAYGAIN_ALBUM_GAIN=";
do {
if (strncasecmp(p, target, strlen(target)) == 0) {
f->replay_gain = atof(p + strlen(target));
@@ -262,7 +261,7 @@
while (*p && *p!= '\n') ++p;
while (*p && strchr("\r\n\t\f ", *p)) ++p;
} while (*p);
- rg ^= RG_TRACK ^ RG_ALBUM;
+ rg ^= RG_track ^ RG_album;
}
}
@@ -340,8 +339,8 @@
if (i >= sizeof("play") - 1 &&
strcmp(myname + i - (sizeof("play") - 1), "play") == 0) {
play = st_true;
- replay_gain_mode = RG_TRACK;
- combine_method = SOX_SEQUENCE;
+ replay_gain_mode = RG_track;
+ combine_method = SOX_sequence;
} else if (i >= sizeof("rec") - 1 &&
strcmp(myname + i - (sizeof("rec") - 1), "rec") == 0) {
rec = st_true;
@@ -350,7 +349,7 @@
/* 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_CONCAT ? 1 : 2))
+ if (input_count < (combine_method <= SOX_concatenate ? 1 : 2))
usage("Not enough input filenames specified");
/* Check for misplaced input/output-specific options */
@@ -371,7 +370,7 @@
/* When mixing audio, default to input side volume adjustments that will
* make sure no clipping will occur. Users probably won't be happy with
* this, and will override it, possibly causing clipping to occur. */
- if (combine_method == SOX_MIX && !uservolume)
+ if (combine_method == SOX_mix && !uservolume)
f->volume = 1.0 / input_count;
if (rec && !j) { /* Set the recording sample rate & # of channels: */
@@ -400,6 +399,14 @@
/* Loop through the rest of the arguments looking for effects */
parse_effects(argc, argv);
+ /* Not the greatest way for users to do this perhaps, but they're used
+ * to it, so it ought to stay until we replace it with something better. */
+ if (!nuser_effects && ofile->filetype && !strcmp(ofile->filetype, "null")) {
+ for (i = 0; i < input_count; i++)
+ report_file_info(files[i]);
+ exit(0);
+ }
+
if (repeatable_random)
st_debug("Not reseeding PRNG; randomness is repeatable");
else {
@@ -406,11 +413,11 @@
time_t t;
time(&t);
- srand(t);
+ srand((unsigned)t);
}
ofile_signal = ofile->signal;
- if (combine_method == SOX_SEQUENCE) do {
+ if (combine_method == SOX_sequence) do {
if (ofile->desc)
st_close(ofile->desc);
free(ofile->desc);
@@ -440,7 +447,7 @@
static char * read_comment_file(char const * const filename)
{
st_bool file_error;
- long file_length = 0;
+ int file_length = 0;
char * result;
FILE * file = fopen(filename, "rt");
@@ -448,14 +455,14 @@
st_fail("Cannot open comment file %s", filename);
exit(1);
}
- file_error = fseeko(file, 0, SEEK_END);
+ file_error = fseeko(file, (off_t)0, SEEK_END);
if (!file_error) {
file_length = ftello(file);
file_error |= file_length < 0;
if (!file_error) {
- result = xmalloc(file_length + 1);
+ result = xmalloc((unsigned)file_length + 1);
rewind(file);
- file_error |= fread(result, file_length, 1, file) != 1;
+ file_error |= fread(result, (unsigned)file_length, 1, file) != 1;
}
}
if (file_error) {
@@ -474,6 +481,7 @@
static struct option long_options[] =
{
+ {"combine" , required_argument, NULL, 0},
{"comment-file" , required_argument, NULL, 0},
{"comment" , required_argument, NULL, 0},
{"endian" , required_argument, NULL, 0},
@@ -481,14 +489,11 @@
{"help-effect" , required_argument, NULL, 0},
{"octave" , no_argument, NULL, 0},
{"replay-gain" , required_argument, NULL, 0},
- {"sequence" , no_argument, NULL, 0},
{"version" , no_argument, NULL, 0},
{"channels" , required_argument, NULL, 'c'},
{"compression" , required_argument, NULL, 'C'},
{"help" , no_argument, NULL, 'h'},
- {"merge" , no_argument, NULL, 'M'},
- {"mix" , no_argument, NULL, 'm'},
{"no-show-progress", no_argument, NULL, 'q'},
{"rate" , required_argument, NULL, 'r'},
{"reverse-bits" , no_argument, NULL, 'X'},
@@ -500,6 +505,45 @@
{NULL, 0, NULL, 0}
};
+static enum_item const combine_methods[] = {
+ ENUM_ITEM(SOX_,sequence)
+ ENUM_ITEM(SOX_,concatenate)
+ ENUM_ITEM(SOX_,mix)
+ ENUM_ITEM(SOX_,merge)
+ {0, 0}};
+
+static enum_item const rg_modes[] = {
+ ENUM_ITEM(RG_,off)
+ ENUM_ITEM(RG_,track)
+ ENUM_ITEM(RG_,album)
+ {0, 0}};
+
+enum {ENDIAN_little, ENDIAN_big, ENDIAN_swap};
+static enum_item const endian_options[] = {
+ ENUM_ITEM(ENDIAN_,little)
+ ENUM_ITEM(ENDIAN_,big)
+ ENUM_ITEM(ENDIAN_,swap)
+ {0, 0}};
+
+static int enum_option(int option_index, enum_item const * items)
+{
+ enum_item const * p = find_enum_text(optarg, items);
+ if (p == NULL) {
+ unsigned len = 1;
+ char * set = xmalloc(len);
+ *set = 0;
+ for (p = items; p->text; ++p) {
+ set = xrealloc(set, len += 2 + strlen(p->text));
+ strcat(set, ", "); strcat(set, p->text);
+ }
+ st_fail("--%s: '%s' is not one of: %s.",
+ long_options[option_index].name, optarg, set + 2);
+ free(set);
+ exit(1);
+ }
+ return p->value;
+}
+
static st_bool doopts(file_t f, int argc, char **argv)
{
while (st_true) {
@@ -514,53 +558,39 @@
case 0: /* Long options with no short equivalent. */
switch (option_index) {
case 0:
- f->comment = read_comment_file(optarg);
+ combine_method = enum_option(option_index, combine_methods);
break;
case 1:
- f->comment = xstrdup(optarg);
+ f->comment = read_comment_file(optarg);
break;
case 2:
- if (!strcmp(optarg, "little"))
- f->signal.reverse_bytes = ST_IS_BIGENDIAN;
- else if (!strcmp(optarg, "big"))
- f->signal.reverse_bytes = ST_IS_LITTLEENDIAN;
- else if (!strcmp(optarg, "swap"))
- f->signal.reverse_bytes = st_true;
- else {
- st_fail("Endian type '%s' is not little|big|swap", optarg);
- exit(1);
- }
+ f->comment = xstrdup(optarg);
break;
case 3:
- interactive = st_true;
+ switch (enum_option(option_index, endian_options)) {
+ case ENDIAN_little: f->signal.reverse_bytes = ST_IS_BIGENDIAN; break;
+ case ENDIAN_big: f->signal.reverse_bytes = ST_IS_LITTLEENDIAN; break;
+ case ENDIAN_swap: f->signal.reverse_bytes = st_true; break;
+ }
break;
case 4:
- usage_effect(optarg);
+ interactive = st_true;
break;
case 5:
- globalinfo.octave_plot_effect = st_true;
+ usage_effect(optarg);
break;
case 6:
- if (!strcmp(optarg, "track"))
- replay_gain_mode = RG_TRACK;
- else if (!strcmp(optarg, "album"))
- replay_gain_mode = RG_ALBUM;
- else if (!strcmp(optarg, "off"))
- replay_gain_mode = RG_OFF;
- else {
- st_fail("Replay gain '%s' is not track|album|off", optarg);
- exit(1);
- }
+ globalinfo.octave_plot_effect = st_true;
break;
case 7:
- combine_method = SOX_SEQUENCE;
+ replay_gain_mode = enum_option(option_index, rg_modes);
break;
case 8:
@@ -571,11 +601,11 @@
break;
case 'm':
- combine_method = SOX_MIX;
+ combine_method = SOX_mix;
break;
case 'M':
- combine_method = SOX_MERGE;
+ combine_method = SOX_merge;
break;
case 'R': /* Useful for regression testing. */
@@ -678,6 +708,16 @@
}
}
+static char const * str_time(double duration)
+{
+ static char string[16][50];
+ static int i;
+ int mins = duration / 60;
+ i = (i+1) & 15;
+ sprintf(string[i], "%02i:%05.2f", mins, duration - mins * 60);
+ return string[i];
+}
+
static void display_file_info(file_t f, st_bool full)
{
static char const * const no_yes[] = {"no", "yes"};
@@ -697,7 +737,14 @@
f->desc->signal.channels,
f->desc->signal.rate);
- if (full)
+ if (full) {
+ if (f->desc->length && f->desc->signal.channels && f->desc->signal.rate) {
+ st_size_t ws = f->desc->length / f->desc->signal.channels;
+ fprintf(stderr,
+ "Duration : %s = %u samples = %g CDDA sectors\n",
+ str_time((double)ws / f->desc->signal.rate),
+ ws, (double)ws / 588);
+ }
fprintf(stderr,
"Endian Type : %s\n"
"Reverse Nibbles: %s\n"
@@ -706,6 +753,7 @@
f->desc->signal.reverse_bytes != ST_IS_BIGENDIAN? "big" : "little",
no_yes[f->desc->signal.reverse_nibbles],
no_yes[f->desc->signal.reverse_bits]);
+ }
if (f->replay_gain != HUGE_VAL)
fprintf(stderr, "Replay gain : %+g dB\n" , f->replay_gain);
@@ -729,13 +777,16 @@
static void progress_to_file(file_t f)
{
+ read_wide_samples = 0;
+ input_wide_samples = f->desc->length / f->desc->signal.channels;
if (show_progress && (st_output_verbosity_level < 3 ||
- (combine_method <= SOX_CONCAT && input_count > 1)))
+ (combine_method <= SOX_concatenate && input_count > 1)))
display_file_info(f, st_false);
if (f->volume == HUGE_VAL)
f->volume = 1;
if (f->replay_gain != HUGE_VAL)
- f->volume *= pow(10, f->replay_gain / 20);
+ f->volume *= pow(10.0, f->replay_gain / 20);
+ f->desc->st_errno = errno = 0;
}
static void sigint(int s)
@@ -745,7 +796,7 @@
time_t secs;
gettimeofday(&now, NULL);
secs = now.tv_sec - then.tv_sec;
- if (show_progress && s == SIGINT && combine_method <= SOX_CONCAT &&
+ if (show_progress && s == SIGINT && combine_method <= SOX_concatenate &&
(secs > 1 || 1000000 * secs + now.tv_usec - then.tv_usec > 999999))
user_skip = st_true;
else
@@ -753,6 +804,33 @@
then = now;
}
+static st_bool can_segue(st_size_t i)
+{
+ return
+ files[i]->desc->signal.channels == files[i - 1]->desc->signal.channels &&
+ files[i]->desc->signal.rate == files[i - 1]->desc->signal.rate;
+}
+
+static st_size_t st_read_wide(ft_t desc, st_sample_t * buf)
+{
+ st_size_t len = ST_BUFSIZ / combiner.channels;
+ len = st_read(desc, buf, len * desc->signal.channels) / desc->signal.channels;
+ if (!len && desc->st_errno)
+ st_fail("%s: %s (%s)", desc->filename, desc->st_errstr, strerror(desc->st_errno));
+ return len;
+}
+
+static void balance_input(st_sample_t * buf, st_size_t ws, file_t f)
+{
+ st_size_t s = ws * f->desc->signal.channels;
+
+ if (f->volume != 1)
+ while (s--) {
+ double d = f->volume * *buf;
+ *buf++ = ST_ROUND_CLIP_COUNT(d, f->volume_clips);
+ }
+}
+
/*
* Process input file -> effect table -> output file one buffer at a time
*/
@@ -760,11 +838,14 @@
static void process(void) {
int e, flowstatus = 0;
st_size_t ws, s, i;
- st_ssize_t ilen[MAX_INPUT_FILES];
+ st_size_t ilen[MAX_INPUT_FILES];
st_sample_t *ibuf[MAX_INPUT_FILES];
combiner = files[current_input]->desc->signal;
- if (combine_method != SOX_SEQUENCE) {
+ if (combine_method == SOX_sequence) {
+ if (!current_input) for (i = 0; i < input_count; i++)
+ report_file_info(files[i]);
+ } else {
st_size_t total_channels = 0;
st_size_t min_channels = ST_SIZE_MAX;
st_size_t max_channels = 0;
@@ -780,19 +861,19 @@
max_rate = max(max_rate, files[i]->desc->signal.rate);
}
if (min_rate != max_rate)
- st_fail("Input files do not have the same sample-rate");
+ st_fail("Input files must have the same sample-rate");
if (min_channels != max_channels) {
- if (combine_method == SOX_CONCAT) {
- st_fail("Input files do not have the same # channels");
+ if (combine_method == SOX_concatenate) {
+ st_fail("Input files must have the same # channels");
exit(1);
- } else if (combine_method == SOX_MIX)
- st_warn("Input files do not have the same # channels");
+ } else if (combine_method == SOX_mix)
+ st_warn("Input files don't have the same # channels");
}
if (min_rate != max_rate)
exit(1);
combiner.channels =
- combine_method == SOX_MERGE? total_channels : max_channels;
+ combine_method == SOX_merge? total_channels : max_channels;
}
ofile->signal = ofile_signal;
@@ -867,27 +948,18 @@
efftabR[e].obuf = (st_sample_t *)xmalloc(ST_BUFSIZ * sizeof(st_sample_t));
}
- if (combine_method <= SOX_CONCAT) {
- input_wide_samples = files[current_input]->desc->length / files[current_input]->desc->signal.channels;
+ if (combine_method <= SOX_concatenate)
progress_to_file(files[current_input]);
- } else {
+ else {
+ ws = 0;
for (i = 0; i < input_count; i++) {
- /* Treat overall length the same as longest input file. */
- size_t wide_samples = files[i]->desc->length / files[i]->desc->signal.channels;
- if (wide_samples > input_wide_samples)
- input_wide_samples = wide_samples;
ibuf[i] = (st_sample_t *)xmalloc(ST_BUFSIZ * sizeof(st_sample_t));
progress_to_file(files[i]);
+ ws = max(ws, input_wide_samples);
}
+ input_wide_samples = ws; /* Output length is that of longest input file. */
}
- /*
- * Just like errno, we must set st_errno to known values before
- * calling I/O operations.
- */
- for (i = 0; i < file_count; i++)
- files[i]->desc->st_errno = 0;
-
input_eff = 0;
input_eff_eof = 0;
@@ -895,93 +967,57 @@
for(e = 1; e < neffects; e++)
efftab[e].odone = efftab[e].olen = 0;
- /* Run input data through effects and get more until olen == 0
- * (or ST_EOF) or user-abort.
- */
signal(SIGINT, sigint);
signal(SIGTERM, sigint);
+ /* Run input data through effects until EOF (olen == 0) or user-abort. */
do {
- if (combine_method <= SOX_CONCAT) {
+ efftab[0].olen = 0;
+ if (combine_method <= SOX_concatenate) {
if (user_skip) {
- ilen[0] = ST_EOF;
user_skip = st_false;
fprintf(stderr, "\nSkipped.");
- } else {
- ilen[0] = st_read(files[current_input]->desc, efftab[0].obuf,
- (st_ssize_t)ST_BUFSIZ);
- if (ilen[0] > ST_BUFSIZ) {
- st_warn("WARNING: Corrupt value of %d! Assuming 0 bytes read.", ilen);
- ilen[0] = 0;
- }
-
- if (ilen[0] == ST_EOF) {
- efftab[0].olen = 0;
- if (files[current_input]->desc->st_errno)
- fprintf(stderr, files[current_input]->desc->st_errstr);
- } else
- efftab[0].olen = ilen[0];
-
- read_wide_samples += efftab[0].olen / combiner.channels;
- }
- /* Some file handlers claim 0 bytes instead of returning
- * ST_EOF. In either case, attempt to go to the next
- * input file.
- */
- if (ilen[0] == ST_EOF || efftab[0].olen == 0) {
+ } else efftab[0].olen =
+ st_read_wide(files[current_input]->desc, efftab[0].obuf);
+ if (efftab[0].olen == 0) { /* If EOF, go to the next input file. */
if (++current_input < input_count) {
- input_wide_samples = files[current_input]->desc->length / files[current_input]->desc->signal.channels;
- read_wide_samples = 0;
- if (combine_method == SOX_CONCAT ||
- (files[current_input]->desc->signal.channels == files[current_input - 1]->desc->signal.channels &&
- files[current_input]->desc->signal.rate == files[current_input - 1]->desc->signal.rate )) {
- progress_to_file(files[current_input]);
- continue;
- }
- else break;
+ 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 {
st_sample_t * p = efftab[0].obuf;
- efftab[0].olen = 0;
for (i = 0; i < input_count; ++i) {
- ilen[i] = st_read(files[i]->desc, ibuf[i], ST_BUFSIZ / combiner.channels * files[i]->desc->signal.channels);
- if (ilen[i] == ST_EOF) {
- ilen[i] = 0;
- if (files[i]->desc->st_errno)
- fprintf(stderr, files[i]->desc->st_errstr);
- }
+ ilen[i] = st_read_wide(files[i]->desc, ibuf[i]);
balance_input(ibuf[i], ilen[i], files[i]);
- ilen[i] /= files[i]->desc->signal.channels;
- if ((st_size_t)ilen[i] > efftab[0].olen)
- efftab[0].olen = ilen[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) {
+ 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 < (st_size_t)ilen[i] && s < files[i]->desc->signal.channels) {
+ 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 = ST_ROUND_CLIP_COUNT(sample, mixing_clips);
}
- ++p;
}
- } else { /* SOX_MERGE: like a multi-track recorder */
+ } 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 < (st_size_t)ilen[i]) * ibuf[i][ws * files[i]->desc->signal.channels + s];
+ *p++ = (ws < ilen[i]) * ibuf[i][ws * files[i]->desc->signal.channels + s];
}
- read_wide_samples += efftab[0].olen;
- efftab[0].olen *= combiner.channels;
}
-
- efftab[0].odone = 0;
-
if (efftab[0].olen == 0)
break;
+ efftab[0].odone = 0;
+ read_wide_samples += efftab[0].olen;
+ efftab[0].olen *= combiner.channels;
+
flowstatus = flow_effect_out();
if (show_progress)
@@ -1004,7 +1040,7 @@
if (show_progress)
fputs("\n\n", stderr);
- if (combine_method > SOX_CONCAT)
+ if (combine_method > SOX_concatenate)
/* Free input buffers now that they are not used */
for (i = 0; i < input_count; i++)
free(ibuf[i]);
@@ -1070,8 +1106,9 @@
/* If this effect can't handle multiple channels then account for this. */
if (e->ininfo.channels > 1 && !(e->h->flags & ST_EFF_MCHAN))
memcpy(&efftabR[neffects], e, sizeof(*e));
+ else memset(&efftabR[neffects], 0, sizeof(*e));
- st_report("Effects chain:%10s %-6s %uHz", e->name,
+ st_report("Effects chain: %-10s %-6s %uHz", e->name,
e->ininfo.channels < 2? "mono" :
(e->h->flags & ST_EFF_MCHAN)? "multi" : "stereo", e->ininfo.rate);
@@ -1522,21 +1559,13 @@
static void update_status(void)
{
- int read_min, left_min, in_min;
- double read_sec, left_sec, in_sec;
double read_time, left_time, in_time;
float completed;
double out_size;
char unit;
- /* Currently, for all sox modes, all input files must have
- * the same sample rate. So we can always just use the rate
- * of the first input file to compute time. */
read_time = (double)read_wide_samples / combiner.rate;
- read_min = read_time / 60;
- read_sec = (double)read_time - 60.0f * (double)read_min;
-
out_size = output_samples / 1000000000.0;
if (out_size >= 1.0)
unit = 'G';
@@ -1568,24 +1597,11 @@
completed = 0;
}
- left_min = left_time / 60;
- left_sec = (double)left_time - 60.0f * (double)left_min;
-
- in_min = in_time / 60;
- in_sec = (double)in_time - 60.0f * (double)in_min;
-
- fprintf(stderr, "\rTime: %02i:%05.2f [%02i:%05.2f] of %02i:%05.2f (% 5.1f%%) Output Buffer:% 7.2f%c", read_min, read_sec, left_min, left_sec, in_min, in_sec, completed, out_size, unit);
+ fprintf(stderr, "\rTime: %s [%s] of %s (% 5.1f%%) Output Buffer:% 7.2f%c",
+ str_time(read_time), str_time(left_time), str_time(in_time),
+ completed, out_size, unit);
}
-static void balance_input(st_sample_t * buf, st_ssize_t len, file_t f)
-{
- if (f->volume != 1)
- while (len--) {
- double d = f->volume * *buf;
- *buf++ = ST_ROUND_CLIP_COUNT(d, f->volume_clips);
- }
-}
-
static int strcmp_p(const void *p1, const void *p2)
{
return strcmp(*(const char **)p1, *(const char **)p2);
@@ -1608,11 +1624,13 @@
"-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"
+ "--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, --mix mix multiple input files (instead of concatenating)\n"
- "-M, --merge merge multiple input files (instead of concatenating)\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"
--- a/src/stio.c
+++ b/src/stio.c
@@ -298,13 +298,10 @@
return NULL;
}
-st_size_t st_read(ft_t ft, st_sample_t *buf, st_size_t len)
+st_size_t st_read(ft_t f, st_sample_t * buf, st_size_t len)
{
- len -= len % ft->signal.channels; /* We need a whole number of "wide" samples */
- len = (*ft->h->read)(ft, buf, len);
- if (len != 0)
- len -= len % ft->signal.channels; /* Belt & braces */
- return len;
+ st_size_t actual = (*f->h->read)(f, buf, len);
+ return (actual > len? 0 : actual);
}
st_size_t st_write(ft_t ft, const st_sample_t *buf, st_size_t len)