ref: 24d144e4f7a11ff2b2359eadcd3e6ae96bfb9733
parent: 3a5f9247329b4001bf88128d7c047da7ab916ffa
author: robs <robs>
date: Sat Jul 19 16:33:42 EDT 2008
Allow k suffix with rate effect. Allow effect to specify output rate. Documentation: Fix rate rej. specs. --input-buffer Add some comments to main process() function; break it up a bit. Fix muddled rate and # channels when playing mixed rate/channels files.
--- a/ChangeLog
+++ b/ChangeLog
@@ -98,6 +98,7 @@
o Command line support for multiple file comments. (robs)
o New --combine=mix-power option to mix combine using 1/sqrt(n) scaling
instead of 1/n [FR 2012856]. (robs)
+ o New --input-buffer option to specify (only) input buffer size. (robs)
o New `soxi' utility to extract/display file header fields. (robs)
o Pkg-config support. (Pascal Giard)
o Simple VU meter. (robs)
--- a/sox.1
+++ b/sox.1
@@ -552,8 +552,19 @@
option is strongly recommended; a `shell' alias, script, or batch file
may be an appropriate way of permanently enabling it.
.TP
-\fB\-\-buffer\fR \fBBYTES\fR
-Set the size in bytes of the buffers used for reading and writing sound data (default 8192).
+\fB\-\-buffer\fR \fBBYTES\fR, \fB\-\-input\-buffer\fR \fBBYTES\fR
+Set the size in bytes of the buffers used for processing audio (default 8192).
+.B \-\-buffer
+applies to input, effects, and output processing;
+.B \-\-input\-buffer
+applies only to input processing (for which it overrides
+.B \-\-buffer
+if both are given).
+.SP
+Be aware that large values for
+.B \-\-buffer
+will cause SoX to be become slow to respond to requests to terminate or to skip
+the current input file.
.TP
\fB\-m\fR\^|\^\fB\-M\fR\^|\^\fB\-\-combine concatenate\fR\^|\^\fBmerge\fR\^|\^\fBmix\fR\^|\^\fBmix\-power\fR\^|\^\fBsequence\fR
Select the input file combining method;
--- a/soxeffect.7
+++ b/soxeffect.7
@@ -731,7 +731,7 @@
(\fB\-t\fR). The decay should be less than 0\*d5 to avoid
feedback. Gain-out is the volume of the output.
.TP
-\fBrate\fR [\fB\-q\fR\^|\^\fB\-l\fR\^|\^\fB\-m\fR\^|\^\fB\-h\fR\^|\^\fB\-v\fR]
+\fBrate\fR [\fB\-q\fR\^|\^\fB\-l\fR\^|\^\fB\-m\fR\^|\^\fB\-h\fR\^|\^\fB\-v\fR] [\fIRATE\fR[\fBk\fR]]
Change the audio sampling rate (i.e. resample the audio)
using a quality level as follows:
.TS
@@ -750,11 +750,11 @@
playback on old hardware
T}
\-m medium 99 100 audio playback
-\-h high 99 120 T{
+\-h high 99 125 T{
.na
16-bit mastering (use with dither)
T}
-\-v very high 99 150 24-bit mastering\
+\-v very high 99 175 24-bit mastering\
.TE
.DT
.SP
@@ -765,6 +765,22 @@
.B Rej dB
is the level of noise rejection.
The default quality level is `high' (\fB\-h\fR).
+.SP
+This effect is invoked automatically if SoX's \fB\-r\fR option specifies a
+rate that is different to that of the input file(s). Alternatively, this
+effect may be invoked with the output rate parameter
+.I RATE
+and SoX's
+.B \-r
+option need not be given. For example, the following two commands are
+equivalent:
+.EX
+ sox input.au -r 48k output.au bass -3
+ sox input.au output.au bass -3 rate 48k
+.EE
+though the second command is more flexible as it allows a
+.B rate
+quality option to be given, and it allows the effects to be ordered arbitrarily.
.SP
See also
.BR resample ,
--- a/src/rate.c
+++ b/src/rate.c
@@ -489,7 +489,7 @@
static int create(sox_effect_t * effp, int argc, char **argv)
{
priv_t * p = (priv_t *) effp->priv;
- char dummy, * found_at, * bws = "-qlmhvu", * nums = "123";
+ char * dummy_p, * found_at, * bws = "-qlmhvu", * nums = "123";
p->quality = p->coef_interp = -1;
p->shared_ptr = &p->shared;
@@ -503,9 +503,10 @@
argc--; argv++;
}
if (argc) {
- if (sscanf(*argv, "%lf %c", &p->out_rate, &dummy) != 1 || p->out_rate <= 0)
+ if ((p->out_rate = lsx_parse_frequency(*argv, &dummy_p)) <= 0 || *dummy_p)
return lsx_usage(effp);
argc--; argv++;
+ effp->out_signal.rate = p->out_rate;
}
return argc? lsx_usage(effp) : SOX_SUCCESS;
}
@@ -562,13 +563,14 @@
sox_effect_handler_t const * sox_rate_effect_fn(void)
{
static sox_effect_handler_t handler = {
- "rate", "[-q|-l|-m|-h|-v]\n"
- " Quality BW % Rej dB Typical Use\n"
- " -q quick & dirty n/a ~30 @ Fs/4 playback on ancient hardware\n"
- " -l low 80 100 playback on old hardware\n"
- " -m medium 99 100 audio playback\n"
- " -h high 99 120 16-bit mastering (use with dither)\n"
- " -v very high 99 150 24-bit mastering"
+ "rate", "[-q|-l|-m|-h|-v] [RATE[k]]"
+ "\n\tQuality BW % Rej dB Typical Use"
+ "\n -q\tquick & dirty n/a ~30 @ Fs/4 playback on ancient hardware"
+ "\n -l\tlow 80 100 playback on old hardware"
+ "\n -m\tmedium 99 100 audio playback"
+ "\n -h\thigh 99 125 16-bit mastering (use with dither)"
+ "\n -v\tvery high 99 175 24-bit mastering"
+ /* "\n -u\tultra high 99.7 175 " */
, SOX_EFF_RATE, create, start, flow, drain, stop, NULL, sizeof(priv_t)
};
return &handler;
--- a/src/sox.c
+++ b/src/sox.c
@@ -115,8 +115,8 @@
/* Effects */
/* We parse effects into a temporary effects table and then place into
- * the real effects table. This makes it easier to reorder some effects
- * as needed. For instance, we can run a resampling effect before
+ * the real effects table. This makes it easier to auto-add some effects
+ * as appropriate. For instance, we can run a resampling effect before
* converting a mono file to stereo. This allows the resampling to work
* on half the data.
*
@@ -125,7 +125,7 @@
* a resampling effect, a channel mixing effect, the input, and the output.
*/
#define MAX_USER_EFF (SOX_MAX_EFFECTS - 4)
-static sox_effect_t * user_efftab[MAX_USER_EFF];
+static sox_effect_t * user_efftab[MAX_USER_EFF], efftab_options[MAX_USER_EFF];
static unsigned nuser_effects;
static sox_effects_chain_t ofile_effects_chain;
@@ -693,25 +693,21 @@
static void optimize_trim(void)
{
- /* Speed hack. If the "trim" effect is the first effect then
- * peek inside its "effect descriptor" and see what the
- * start location is. This has to be done after its start()
- * is called to have the correct location.
- * Also, only do this when only working with one input file.
- * This is because the logic to do it for multiple files is
- * complex and problably never used.
- * This hack is a huge time savings when trimming
- * gigs of audio data into managable chunks
- */
+ /* Speed hack. If the "trim" effect is the first effect then peek inside its
+ * "effect descriptor" and see what the start location is. This has to be
+ * done after its start() is called to have the correct location. Also, only
+ * do this when only working with one input file. This is because the logic
+ * to do it for multiple files is complex and problably never used. This
+ * hack is a huge time savings when trimming gigs of audio data into
+ * managable chunks. */
if (input_count == 1 && ofile_effects_chain.length > 1 && strcmp(ofile_effects_chain.effects[1][0].handler.name, "trim") == 0) {
if (files[0]->ft->handler.seek && files[0]->ft->seekable){
sox_size_t offset = sox_trim_get_start(&ofile_effects_chain.effects[1][0]);
if (offset && sox_seek(files[0]->ft, offset, SOX_SEEK_SET) == SOX_SUCCESS) {
read_wide_samples = offset / files[0]->ft->signal.channels;
- /* Assuming a failed seek stayed where it was. If the
- * seek worked then reset the start location of
- * trim so that it thinks user didn't request a skip.
- */
+ /* Assuming a failed seek stayed where it was. If the seek worked then
+ * reset the start location of trim so that it thinks user didn't
+ * request a skip. */
sox_trim_clear_start(&ofile_effects_chain.effects[1][0]);
sox_debug("optimize_trim successful");
}
@@ -754,12 +750,9 @@
sox_append_comment(&oob.comments, *p++);
}
- /*
- * 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.
- */
+ /* 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_signal.rate;
for (i = 0; i < SOX_MAX_NLOOPS; i++) {
oob.loops[i].start = oob.loops[i].start * factor;
@@ -775,9 +768,9 @@
* 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 whether to enable the progress display (similar to that of ogg123) has
+ * not been specified by the user, auto turn on when outputting to an audio
+ * device: */
if (show_progress == SOX_OPTION_DEFAULT)
show_progress = (ofile->ft->handler.flags & SOX_FILE_DEVICE) != 0 &&
(ofile->ft->handler.flags & SOX_FILE_PHONY) == 0;
@@ -794,19 +787,21 @@
else user_abort = sox_true;
}
-/*
- * Process: Input(s) -> Balancing -> Combiner -> Effects -> Output
- */
-
-static int process(void) {
- int flowstatus = 0;
- sox_size_t i;
+static void calculate_combiner_and_output_signal_parameters(void)
+{
sox_bool known_length = combine_method != sox_sequence;
- sox_size_t olen = 0;
+ sox_size_t i, olen = 0;
+ /* Set the combiner output signal attributes to those of the 1st/next input
+ * file. If we are in sox_sequence mode then we don't need to check the
+ * attributes of the other inputs, otherwise, it is mandatory that all input
+ * files have the same sample rate, and for sox_concatenate, it is mandatory
+ * that they have the same number of channels, otherwise, the number of
+ * channels at the output of the combiner is calculated according to the
+ * combiner mode. */
combiner_signal = files[current_input]->ft->signal;
- combiner_encoding = files[current_input]->ft->encoding;
if (combine_method == sox_sequence) {
+ /* Report all input files; do this only the 1st time process() is called: */
if (!current_input) for (i = 0; i < input_count; i++)
report_file_info(files[i]);
} else {
@@ -816,13 +811,15 @@
sox_size_t min_rate = SOX_SIZE_MAX;
sox_size_t max_rate = 0;
- for (i = 0; i < input_count; i++) { /* Report all inputs, then check */
+ /* Report all input files and gather info on differing rates & numbers of
+ * channels, and on the resulting output audio length: */
+ for (i = 0; i < input_count; i++) {
report_file_info(files[i]);
total_channels += files[i]->ft->signal.channels;
min_channels = min(min_channels, files[i]->ft->signal.channels);
max_channels = max(max_channels, files[i]->ft->signal.channels);
- min_rate = min(min_rate, files[i]->ft->signal.rate);
- max_rate = max(max_rate, files[i]->ft->signal.rate);
+ min_rate = min(min_rate , files[i]->ft->signal.rate);
+ max_rate = max(max_rate , files[i]->ft->signal.rate);
known_length = known_length && files[i]->ft->signal.length != 0;
if (combine_method == sox_concatenate)
olen += files[i]->ft->signal.length / files[i]->ft->signal.channels;
@@ -829,42 +826,74 @@
else
olen = max(olen, files[i]->ft->signal.length / files[i]->ft->signal.channels);
}
+
+ /* Check for invalid/unusual rate or channel combinations: */
if (min_rate != max_rate)
sox_fail("Input files must have the same sample-rate");
+ /* Don't exit quite yet; give the user any other message 1st */
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 || combine_method == sox_mix_power ||
- combine_method == sox_multiply)
+ } else if (combine_method != sox_merge)
sox_warn("Input files don't have the same # channels");
}
if (min_rate != max_rate)
exit(1);
+ /* Store the calculated # of combined channels: */
combiner_signal.channels =
combine_method == sox_merge? total_channels : max_channels;
}
+ /* Determine the output file signal attributes; set from user options
+ * if given: */
ofile->signal = ofile_signal_options;
- if (ofile->signal.rate == 0)
+
+ /* If no user option for output rate or # of channels, set from the last
+ * effect that sets these, or from the input combiner if there is none such */
+ for (i = 0; i < nuser_effects && !ofile->signal.rate; ++i)
+ ofile->signal.rate = user_efftab[nuser_effects - 1 - i]->out_signal.rate;
+ for (i = 0; i < nuser_effects && !ofile->signal.channels; ++i)
+ ofile->signal.channels = user_efftab[nuser_effects - 1 - i]->out_signal.channels;
+ if (!ofile->signal.rate)
ofile->signal.rate = combiner_signal.rate;
- if (ofile->signal.channels == 0) {
- unsigned j;
- for (j = 0; j < nuser_effects && !ofile->signal.channels; ++j)
- ofile->signal.channels = user_efftab[nuser_effects - 1 - j]->out_signal.channels;
- if (ofile->signal.channels == 0)
- ofile->signal.channels = combiner_signal.channels;
- }
+ if (!ofile->signal.channels)
+ ofile->signal.channels = combiner_signal.channels;
+
+ /* FIXME: comment this: */
ofile->signal.precision = combiner_signal.precision;
+ /* Now take account of any net speed change specified by user effects by
+ * adjusting the nominal sample rate at the output of the combiner: */
combiner_signal.rate *= sox_effects_globals.speed;
+ /* If any given user effect modifies the audio length, then we assume that
+ * we don't know what the output length will be. FIXME: in most cases,
+ * an effect that modifies length will be able to determine by how much from
+ * its getopts parameters, so olen should be calculable. */
+ for (i = 0; i < nuser_effects; i++)
+ known_length = known_length && !(user_efftab[i]->handler.flags & SOX_EFF_LENGTH);
+
+ if (!known_length)
+ olen = 0;
+ ofile->signal.length = (sox_size_t)(olen * ofile->signal.channels * ofile->signal.rate / combiner_signal.rate + .5);
+}
+
+static void set_combiner_and_output_encoding_parameters(void)
+{
+ /* The input encoding parameters passed to the effects chain are those of
+ * the first input file (for each segued block if sox_sequence):*/
+ combiner_encoding = files[current_input]->ft->encoding;
+
+ /* Determine the output file encoding attributes; set from user options
+ * if given: */
ofile->encoding = ofile_encoding_options;
- /* Get unspecified output file encoding attributes from the input file
- * and set the output file to the resultant encoding if this is
- * supported by the output file type. */
+ /* Get unspecified output file encoding attributes from the input file and
+ * set the output file to the resultant encoding if this is supported by the
+ * output file type; if not, the output file handler should select an
+ * encoding suitable for the output signal and its precision. */
{
sox_encodinginfo_t t = ofile->encoding;
if (!t.encoding)
@@ -874,17 +903,23 @@
if (sox_format_supports_encoding(ofile->filename, ofile->filetype, &t))
ofile->encoding = t;
}
+}
- for (i = 0; i < nuser_effects; i++)
- known_length = known_length && !(user_efftab[i]->handler.flags & SOX_EFF_LENGTH);
+static int process(void)
+{ /* Input(s) -> Balancing -> Combiner -> Effects -> Output */
+ int i;
- if (!known_length)
- olen = 0;
- ofile->signal.length = (sox_size_t)(olen * ofile->signal.channels * ofile->signal.rate / combiner_signal.rate + .5);
+ for (i = 0; i < nuser_effects; ++i)
+ *user_efftab[i] = efftab_options[i];
+
+ calculate_combiner_and_output_signal_parameters();
+ set_combiner_and_output_encoding_parameters();
open_output_file();
- ofile_effects_chain.length = 0; /* FIXME: needs proper cleanup of effects
- chain memory. `kill' auto effects? */
+ /* 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;
ofile_effects_chain.in_enc = &combiner_encoding;
ofile_effects_chain.out_enc = &ofile->ft->encoding;
@@ -892,13 +927,9 @@
optimize_trim();
- signal(SIGINT, sigint);
- /* FIXME: For SIGTERM at least we really should guarantee to stop quickly */
- signal(SIGTERM, sigint); /* Stop gracefully even in extremis */
-
- flowstatus = sox_flow_effects(&ofile_effects_chain, update_status);
-
- return flowstatus;
+ 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);
}
static void display_SoX_version(FILE * file)
@@ -1768,8 +1799,13 @@
srand((unsigned)t);
}
+ /* Save things that sox_sequence needs to be reinitialised for each segued
+ * block of input files: */
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 (ofile->ft)
sox_close(ofile->ft);