1 /* SoX - The Swiss Army Knife of Audio Manipulation.
3 * This is the main function for the SoX command line programs:
4 * sox, play, rec, soxi.
6 * Copyright 1998-2009 Chris Bagwell and SoX contributors
7 * Copyright 1991 Lance Norskog And Sundry Contributors
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17 * Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "soxconfig.h"
37 #include <sys/types.h>
40 #if defined(HAVE_GLOB_H)
48 #ifdef HAVE_SYS_TIME_H
52 #ifdef HAVE_SYS_TIMEB_H
53 #include <sys/timeb.h>
56 #ifdef HAVE_SYS_UTSNAME_H
57 #include <sys/utsname.h>
64 #ifdef HAVE_SYS_IOCTL_H
65 #include <sys/ioctl.h>
68 #ifdef HAVE_GETTIMEOFDAY
72 #define gettimeofday(a,b) ftime(a)
74 #define tv_usec millitm
78 #if !defined(HAVE_CONIO_H) && !defined(HAVE_TERMIOS_H) && (defined(_MSC_VER) || defined(__MINGW32__))
79 #define HAVE_CONIO_H 1
83 /* _kbhit and _getch */
88 /*#define MORE_INTERACTIVE 1*/
90 #define SOX_OPTS "SOX_OPTS"
91 static lsx_getopt_t optstate
;
95 static char const * myname
= NULL
;
96 static enum {sox_sox
, sox_play
, sox_rec
, sox_soxi
} sox_mode
;
102 sox_sequence
, sox_concatenate
, sox_mix
, sox_mix_power
,
103 sox_merge
, sox_multiply
, sox_default
104 } combine_method
= sox_default
;
105 static enum { sox_single
, sox_multiple
} output_method
= sox_single
;
106 #define is_serial(m) ((m) <= sox_concatenate)
107 #define is_parallel(m) (!is_serial(m))
108 static sox_bool no_clobber
= sox_false
, interactive
= sox_false
;
109 static sox_bool uservolume
= sox_false
;
110 typedef enum {RG_off
, RG_track
, RG_album
, RG_default
} rg_mode
;
111 static lsx_enum_item
const rg_modes
[] = {
112 LSX_ENUM_ITEM(RG_
,off
)
113 LSX_ENUM_ITEM(RG_
,track
)
114 LSX_ENUM_ITEM(RG_
,album
)
116 static rg_mode replay_gain_mode
= RG_default
;
117 static sox_option_t show_progress
= sox_option_default
;
120 /* Input & output files */
126 char const * filetype
;
127 sox_signalinfo_t signal
;
128 sox_encodinginfo_t encoding
;
134 sox_format_t
* ft
; /* libSoX file descriptor */
135 uint64_t volume_clips
;
136 rg_mode replay_gain_mode
;
139 static file_t
* * files
= NULL
; /* Array tracking input and output files */
140 #define ofile files[file_count - 1]
141 static size_t file_count
= 0;
142 static size_t input_count
= 0;
143 static size_t output_count
= 0;
147 /* We parse effects into a temporary effects table and then place into
148 * the real effects chain. This allows scanning all effects to give
149 * hints to what input effect options should be as well as determining
150 * when mixer or resample effects need to be auto-inserted as well.
152 static sox_effect_t
**user_efftab
= NULL
;
153 static size_t user_efftab_size
= 0;
154 static sox_effects_chain_t
*effects_chain
= NULL
;
155 static sox_effect_t
*save_output_eff
= NULL
;
157 static struct { char *name
; int argc
; char **argv
; size_t argv_size
; } **user_effargs
= NULL
;
158 static size_t *user_effargs_size
= NULL
; /* array: size of user_effargs for each chain */
159 /* Size of memory structures related to effects arguments (user_effargs[i],
160 * user_effargs[i][j].argv) to be extended in steps of EFFARGS_STEP */
161 #define EFFARGS_STEP 8
162 static size_t *nuser_effects
= NULL
; /* array: number of effects in each chain */
163 static size_t current_eff_chain
= 0;
164 static size_t eff_chain_count
= 0;
165 static sox_bool very_first_effchain
= sox_true
;
166 /* Indicates that not only the first effects chain is in effect (hrm), but
167 also that it has never been restarted. Only then we may use the
168 optimize_trim() hack. */
169 static char *effects_filename
= NULL
;
170 static char * play_rate_arg
= NULL
;
171 static char *norm_level
= NULL
;
175 static sox_signalinfo_t combiner_signal
, ofile_signal_options
;
176 static sox_encodinginfo_t combiner_encoding
, ofile_encoding_options
;
177 static uint64_t mixing_clips
= 0;
178 static size_t current_input
= 0;
179 static uint64_t input_wide_samples
= 0;
180 static uint64_t read_wide_samples
= 0;
181 static uint64_t output_samples
= 0;
182 static sox_bool input_eof
= sox_false
;
183 static sox_bool output_eof
= sox_false
;
184 static sox_bool user_abort
= sox_false
;
185 static sox_bool user_skip
= sox_false
;
186 static sox_bool user_restart_eff
= sox_false
;
187 static int success
= 0;
188 static int cleanup_called
= 0;
189 static sox_sample_t omax
[2], omin
[2];
191 #ifdef HAVE_TERMIOS_H
193 static struct termios original_termios
;
194 static sox_bool original_termios_saved
= sox_false
;
197 static sox_bool stdin_is_a_tty
, is_player
, is_guarded
, do_guarded_norm
, no_dither
, reported_sox_opts
;
199 struct timeval load_timeofday
;
201 static void cleanup(void)
205 if (!success
&& !reported_sox_opts
) {
206 char const * env_opts
= getenv(SOX_OPTS
);
207 if (env_opts
&& *env_opts
)
208 lsx_report("used "SOX_OPTS
"=%s", env_opts
);
210 /* Close the input and output files before exiting. */
211 for (i
= 0; i
< input_count
; i
++) {
213 sox_close(files
[i
]->ft
);
215 free(files
[i
]->filename
);
221 if (!success
&& ofile
->ft
->io_type
== lsx_io_file
) { /* If we failed part way through */
222 struct stat st
; /* writing a normal file, remove it. */
223 if (!stat(ofile
->ft
->filename
, &st
) &&
224 (st
.st_mode
& S_IFMT
) == S_IFREG
)
225 unlink(ofile
->ft
->filename
);
227 sox_close(ofile
->ft
); /* Assume we can unlink a file before closing it. */
229 free(ofile
->filename
);
235 #ifdef HAVE_TERMIOS_H
236 if (original_termios_saved
)
237 tcsetattr(fileno(stdin
), TCSANOW
, &original_termios
);
242 free(sox_globals
.tmp_path
);
243 sox_globals
.tmp_path
= NULL
;
246 free(effects_filename
);
254 /* Cleanup atexit() function, hence always called. */
255 static void atexit_cleanup(void)
257 /* Do not call cleanup using atexit() if possible. pthread's can
258 * act unpredictable if called outside of main().
264 static char const * str_time(double seconds
)
266 static char string
[16][50];
268 int hours
, mins
= seconds
/ 60;
269 seconds
-= mins
* 60;
273 sprintf(string
[i
], "%02i:%02i:%05.2f", hours
, mins
, seconds
);
277 static char const * size_and_bitrate(sox_format_t
* ft
, char const * * text
)
279 off_t size
= lsx_filelength(ft
);
280 if (ft
->signal
.length
&& ft
->signal
.channels
&& ft
->signal
.rate
&& text
) {
281 double secs
= ft
->signal
.length
/ ft
->signal
.channels
/ ft
->signal
.rate
;
282 *text
= lsx_sigfigs3(8. * size
/ secs
);
284 return lsx_sigfigs3((double)size
);
287 static void play_file_info(sox_format_t
* ft
, file_t
* f
, sox_bool full
)
289 FILE * const output
= sox_mode
== sox_soxi
? stdout
: stderr
;
290 char const * text
, * text2
= NULL
;
292 uint64_t ws
= ft
->signal
.length
/ ft
->signal
.channels
;
295 fprintf(output
, "\n");
296 if (ft
->filename
[0]) {
297 fprintf(output
, "%s:", ft
->filename
);
298 if (strcmp(ft
->filename
, "-") == 0 || (ft
->handler
.flags
& SOX_FILE_DEVICE
))
299 fprintf(output
, " (%s)", ft
->handler
.names
[0]);
300 fprintf(output
, "\n\n");
303 if ((text
= size_and_bitrate(ft
, &text2
))) {
304 fprintf(output
, " File Size: %-10s", text
);
306 fprintf(output
, "Bit Rate: %s", text2
);
307 fprintf(output
, "\n");
310 fprintf(output
, " Encoding: %-14s", sox_encodings_info
[ft
->encoding
.encoding
].name
);
311 text
= sox_find_comment(f
->ft
->oob
.comments
, "Comment");
313 text
= sox_find_comment(f
->ft
->oob
.comments
, "Description");
315 text
= sox_find_comment(f
->ft
->oob
.comments
, "Year");
317 fprintf(output
, "Info: %s", text
);
318 fprintf(output
, "\n");
320 sprintf(buffer
, " Channels: %u @ %u-bit", ft
->signal
.channels
, ft
->signal
.precision
);
321 fprintf(output
, "%-25s", buffer
);
322 text
= sox_find_comment(f
->ft
->oob
.comments
, "Tracknumber");
324 fprintf(output
, "Track: %s", text
);
325 text
= sox_find_comment(f
->ft
->oob
.comments
, "Tracktotal");
327 fprintf(output
, " of %s", text
);
329 fprintf(output
, "\n");
331 sprintf(buffer
, "Samplerate: %gHz", ft
->signal
.rate
);
332 fprintf(output
, "%-25s", buffer
);
333 text
= sox_find_comment(f
->ft
->oob
.comments
, "Album");
335 fprintf(output
, "Album: %s", text
);
336 fprintf(output
, "\n");
338 if (f
&& f
->replay_gain
!= HUGE_VAL
){
339 sprintf(buffer
, "%s gain: %+.1fdB", lsx_find_enum_value(f
->replay_gain_mode
, rg_modes
)->text
, f
->replay_gain
);
340 buffer
[0] += 'A' - 'a';
341 fprintf(output
, "%-24s", buffer
);
343 fprintf(output
, "%-24s", "Replaygain: off");
344 text
= sox_find_comment(f
->ft
->oob
.comments
, "Artist");
346 fprintf(output
, "Artist: %s", text
);
347 fprintf(output
, "\n");
349 fprintf(output
, " Duration: %-13s", ft
->signal
.length
? str_time((double)ws
/ ft
->signal
.rate
) : "unknown");
350 text
= sox_find_comment(f
->ft
->oob
.comments
, "Title");
352 fprintf(output
, "Title: %s", text
);
353 fprintf(output
, "\n\n");
356 static void display_file_info(sox_format_t
* ft
, file_t
* f
, sox_bool full
)
358 static char const * const no_yes
[] = {"no", "yes"};
359 FILE * const output
= sox_mode
== sox_soxi
? stdout
: stderr
;
360 char const * filetype
= lsx_find_file_extension(ft
->filename
);
361 sox_bool show_type
= sox_true
;
364 if (is_player
&& sox_globals
.verbosity
< 3) {
365 play_file_info(ft
, f
, full
);
369 fprintf(output
, "\n%s: '%s'",
370 ft
->mode
== 'r'? "Input File " : "Output File ", ft
->filename
);
371 if (filetype
) for (i
= 0; ft
->handler
.names
[i
] && show_type
; ++i
)
372 if (!strcasecmp(filetype
, ft
->handler
.names
[i
]))
373 show_type
= sox_false
;
375 fprintf(output
, " (%s)", ft
->handler
.names
[0]);
376 fprintf(output
, "\n");
381 "Precision : %u-bit\n",
384 ft
->signal
.precision
);
386 if (ft
->signal
.length
&& ft
->signal
.channels
&& ft
->signal
.rate
) {
387 uint64_t ws
= ft
->signal
.length
/ ft
->signal
.channels
;
388 char const * text
, * text2
= NULL
;
390 "Duration : %s = %" PRIu64
" samples %c %g CDDA sectors\n",
391 str_time((double)ws
/ ft
->signal
.rate
),
392 ws
, "~="[ft
->signal
.rate
== 44100],
393 (double)ws
/ ft
->signal
.rate
* 44100 / 588);
394 if (ft
->mode
== 'r' && (text
= size_and_bitrate(ft
, &text2
))) {
395 fprintf(output
, "File Size : %s\n", text
);
397 fprintf(output
, "Bit Rate : %s\n", text2
);
401 if (ft
->encoding
.encoding
) {
402 char buffer
[20] = {'\0'};
403 if (ft
->encoding
.bits_per_sample
)
404 sprintf(buffer
, "%u-bit ", ft
->encoding
.bits_per_sample
);
406 fprintf(output
, "Sample Encoding: %s%s\n", buffer
,
407 sox_encodings_info
[ft
->encoding
.encoding
].desc
);
411 if (ft
->encoding
.bits_per_sample
> 8 || (ft
->handler
.flags
& SOX_FILE_ENDIAN
))
412 fprintf(output
, "Endian Type : %s\n",
413 ft
->encoding
.reverse_bytes
!= MACHINE_IS_BIGENDIAN
? "big" : "little");
414 if (ft
->encoding
.bits_per_sample
)
416 "Reverse Nibbles: %s\n"
417 "Reverse Bits : %s\n",
418 no_yes
[ft
->encoding
.reverse_nibbles
],
419 no_yes
[ft
->encoding
.reverse_bits
]);
422 if (f
&& f
->replay_gain
!= HUGE_VAL
)
423 fprintf(output
, "Replay gain : %+g dB (%s)\n" , f
->replay_gain
,
424 lsx_find_enum_value(f
->replay_gain_mode
, rg_modes
)->text
);
425 if (f
&& f
->volume
!= HUGE_VAL
)
426 fprintf(output
, "Level adjust : %g (linear gain)\n" , f
->volume
);
428 if (!(ft
->handler
.flags
& SOX_FILE_DEVICE
) && ft
->oob
.comments
) {
429 if (sox_num_comments(ft
->oob
.comments
) > 1) {
430 sox_comments_t p
= ft
->oob
.comments
;
431 fprintf(output
, "Comments : \n");
432 do fprintf(output
, "%s\n", *p
);
435 else fprintf(output
, "Comment : '%s'\n", ft
->oob
.comments
[0]);
437 fprintf(output
, "\n");
440 static void report_file_info(file_t
* f
)
442 if (sox_globals
.verbosity
> 2)
443 display_file_info(f
->ft
, f
, sox_true
);
446 static void progress_to_next_input_file(file_t
* f
, sox_effect_t
* effp
)
449 user_skip
= sox_false
;
450 fprintf(stderr
, "\nSkipped (Ctrl-C twice to quit).\n");
452 read_wide_samples
= 0;
453 input_wide_samples
= f
->ft
->signal
.length
/ f
->ft
->signal
.channels
;
454 if (show_progress
&& (sox_globals
.verbosity
< 3 ||
455 (is_serial(combine_method
) && input_count
> 1)))
456 display_file_info(f
->ft
, f
, sox_false
);
457 if (f
->volume
== HUGE_VAL
)
459 if (f
->replay_gain
!= HUGE_VAL
)
460 f
->volume
*= pow(10.0, f
->replay_gain
/ 20);
461 if (effp
&& f
->volume
!= floor(f
->volume
))
462 effp
->out_signal
.precision
= SOX_SAMPLE_PRECISION
;
463 f
->ft
->sox_errno
= errno
= 0;
466 /* Read up to max `wide' samples. A wide sample contains one sample per channel
467 * from the input audio. */
468 static size_t sox_read_wide(sox_format_t
* ft
, sox_sample_t
* buf
, size_t max
)
470 size_t len
= max
/ combiner_signal
.channels
;
471 len
= sox_read(ft
, buf
, len
* ft
->signal
.channels
) / ft
->signal
.channels
;
472 if (!len
&& ft
->sox_errno
)
473 lsx_fail("`%s' %s: %s",
474 ft
->filename
, ft
->sox_errstr
, sox_strerror(ft
->sox_errno
));
478 static void balance_input(sox_sample_t
* buf
, size_t ws
, file_t
* f
)
480 size_t s
= ws
* f
->ft
->signal
.channels
;
482 if (f
->volume
!= 1) while (s
--) {
483 double d
= f
->volume
* *buf
;
484 *buf
++ = SOX_ROUND_CLIP_COUNT(d
, f
->volume_clips
);
488 /* The input combiner: contains one sample buffer per input file, but only
489 * needed if is_parallel(combine_method) */
491 sox_sample_t
* * ibuf
;
495 static int combiner_start(sox_effect_t
*effp
)
497 input_combiner_t
* z
= (input_combiner_t
*) effp
->priv
;
501 if (is_serial(combine_method
))
502 progress_to_next_input_file(files
[current_input
], effp
);
505 z
->ibuf
= lsx_malloc(input_count
* sizeof(*z
->ibuf
));
506 for (i
= 0; i
< input_count
; i
++) {
507 z
->ibuf
[i
] = lsx_malloc(sox_globals
.bufsiz
* sizeof(sox_sample_t
));
508 progress_to_next_input_file(files
[i
], effp
);
509 ws
= max(ws
, input_wide_samples
);
511 input_wide_samples
= ws
; /* Output length is that of longest input file. */
513 z
->ilen
= lsx_malloc(input_count
* sizeof(*z
->ilen
));
517 static sox_bool
can_segue(size_t i
)
520 files
[i
]->ft
->signal
.channels
== files
[i
- 1]->ft
->signal
.channels
&&
521 files
[i
]->ft
->signal
.rate
== files
[i
- 1]->ft
->signal
.rate
;
524 static int combiner_drain(sox_effect_t
*effp
, sox_sample_t
* obuf
, size_t * osamp
)
526 input_combiner_t
* z
= (input_combiner_t
*) effp
->priv
;
530 if (is_serial(combine_method
)) {
533 olen
= sox_read_wide(files
[current_input
]->ft
, obuf
, *osamp
);
534 if (olen
== 0) { /* If EOF, go to the next input file. */
535 if (++current_input
< input_count
) {
536 if (combine_method
== sox_sequence
&& !can_segue(current_input
))
538 progress_to_next_input_file(files
[current_input
], NULL
);
542 balance_input(obuf
, olen
, files
[current_input
]);
545 } /* is_serial */ else { /* else is_parallel() */
546 sox_sample_t
* p
= obuf
;
547 for (i
= 0; i
< input_count
; ++i
) {
548 z
->ilen
[i
] = sox_read_wide(files
[i
]->ft
, z
->ibuf
[i
], *osamp
);
549 balance_input(z
->ibuf
[i
], z
->ilen
[i
], files
[i
]);
550 olen
= max(olen
, z
->ilen
[i
]);
552 for (ws
= 0; ws
< olen
; ++ws
) { /* wide samples */
553 if (combine_method
== sox_mix
|| combine_method
== sox_mix_power
) {
554 for (s
= 0; s
< effp
->in_signal
.channels
; ++s
, ++p
) { /* sum samples */
556 for (i
= 0; i
< input_count
; ++i
)
557 if (ws
< z
->ilen
[i
] && s
< files
[i
]->ft
->signal
.channels
) {
558 /* Cast to double prevents integer overflow */
559 double sample
= *p
+ (double)z
->ibuf
[i
][ws
* files
[i
]->ft
->signal
.channels
+ s
];
560 *p
= SOX_ROUND_CLIP_COUNT(sample
, mixing_clips
);
563 } /* sox_mix */ else if (combine_method
== sox_multiply
) {
564 for (s
= 0; s
< effp
->in_signal
.channels
; ++s
, ++p
) { /* multiply samples */
566 *p
= ws
< z
->ilen
[i
] && s
< files
[i
]->ft
->signal
.channels
?
567 z
->ibuf
[i
][ws
* files
[i
]->ft
->signal
.channels
+ s
] : 0;
568 for (++i
; i
< input_count
; ++i
) {
569 double sample
= *p
* (-1. / SOX_SAMPLE_MIN
) * (ws
< z
->ilen
[i
] && s
< files
[i
]->ft
->signal
.channels
? z
->ibuf
[i
][ws
* files
[i
]->ft
->signal
.channels
+ s
] : 0);
570 *p
= SOX_ROUND_CLIP_COUNT(sample
, mixing_clips
);
573 } /* sox_multiply */ else { /* sox_merge: like a multi-track recorder */
574 for (i
= 0; i
< input_count
; ++i
)
575 for (s
= 0; s
< files
[i
]->ft
->signal
.channels
; ++s
)
576 *p
++ = (ws
< z
->ilen
[i
]) * z
->ibuf
[i
][ws
* files
[i
]->ft
->signal
.channels
+ s
];
580 read_wide_samples
+= olen
;
581 olen
*= effp
->in_signal
.channels
;
584 input_eof
= olen
? sox_false
: sox_true
;
586 if (input_eof
&& is_parallel(combine_method
))
587 current_input
+= input_count
;
589 return olen
? SOX_SUCCESS
: SOX_EOF
;
592 static int combiner_stop(sox_effect_t
*effp
)
594 input_combiner_t
* z
= (input_combiner_t
*) effp
->priv
;
597 if (is_parallel(combine_method
)) {
598 /* Free input buffers now that they are not used */
599 for (i
= 0; i
< input_count
; i
++)
608 static sox_effect_handler_t
const * input_combiner_effect_fn(void)
610 static sox_effect_handler_t handler
= { "input", 0, SOX_EFF_MCHAN
|
611 SOX_EFF_MODIFY
, 0, combiner_start
, 0, combiner_drain
,
612 combiner_stop
, 0, sizeof(input_combiner_t
)
617 static int ostart(sox_effect_t
*effp
)
619 unsigned prec
= effp
->out_signal
.precision
;
620 if (effp
->in_signal
.mult
&& effp
->in_signal
.precision
> prec
)
621 *effp
->in_signal
.mult
*= 1 - (1 << (31 - prec
)) * (1. / SOX_SAMPLE_MAX
);
625 static int output_flow(sox_effect_t
*effp
, sox_sample_t
const * ibuf
,
626 sox_sample_t
* obuf
, size_t * isamp
, size_t * osamp
)
630 (void)effp
, (void)obuf
;
631 if (show_progress
) for (len
= 0; len
< *isamp
; len
+= effp
->in_signal
.channels
) {
632 omax
[0] = max(omax
[0], ibuf
[len
]);
633 omin
[0] = min(omin
[0], ibuf
[len
]);
634 if (effp
->in_signal
.channels
> 1) {
635 omax
[1] = max(omax
[1], ibuf
[len
+ 1]);
636 omin
[1] = min(omin
[1], ibuf
[len
+ 1]);
644 len
= *isamp
? sox_write(ofile
->ft
, ibuf
, *isamp
) : 0;
645 output_samples
+= len
/ ofile
->ft
->signal
.channels
;
646 output_eof
= (len
!= *isamp
) ? sox_true
: sox_false
;
648 if (ofile
->ft
->sox_errno
)
649 lsx_fail("`%s' %s: %s", ofile
->ft
->filename
,
650 ofile
->ft
->sox_errstr
, sox_strerror(ofile
->ft
->sox_errno
));
656 static sox_effect_handler_t
const * output_effect_fn(void)
658 static sox_effect_handler_t handler
= {"output", 0, SOX_EFF_MCHAN
|
659 SOX_EFF_MODIFY
| SOX_EFF_PREC
, NULL
, ostart
, output_flow
, NULL
, NULL
, NULL
, 0
664 static void auto_effect(sox_effects_chain_t
*, char const *, int, char **,
665 sox_signalinfo_t
*, int *);
667 static int add_effect(sox_effects_chain_t
* chain
, sox_effect_t
* effp
,
668 sox_signalinfo_t
* in
, sox_signalinfo_t
const * out
, int * guard
) {
671 case 0: if (!(effp
->handler
.flags
& SOX_EFF_GAIN
)) {
673 auto_effect(chain
, "gain", 1, &arg
, in
, &no_guard
);
677 case 1: if (effp
->handler
.flags
& SOX_EFF_GAIN
) {
679 auto_effect(chain
, "gain", 1, &arg
, in
, &no_guard
);
683 case 2: if (!(effp
->handler
.flags
& SOX_EFF_MODIFY
)) {
684 lsx_warn("%s: effects that modify audio should not follow dither",
689 return sox_add_effect(chain
, effp
, in
, out
);
692 static void auto_effect(sox_effects_chain_t
*chain
, char const *name
, int argc
,
693 char *argv
[], sox_signalinfo_t
*signal
, int * guard
)
697 effp
= sox_create_effect(sox_find_effect(name
)); /* Should always succeed. */
699 if (sox_effect_options(effp
, argc
, argv
) == SOX_EOF
)
700 exit(1); /* The failing effect should have displayed an error message */
702 if (add_effect(chain
, effp
, signal
, &ofile
->ft
->signal
, guard
) != SOX_SUCCESS
)
703 exit(2); /* The effects chain should have displayed an error message */
707 /* add_eff_chain() - NOTE: this only adds memory for one
708 * additional effects chain beyond value of eff_chain_count. It
709 * does not unconditionally increase size of effects chain.
711 static void add_eff_chain(void)
713 lsx_revalloc(user_effargs
, eff_chain_count
+1);
714 user_effargs
[eff_chain_count
] = lsx_malloc(sizeof(**user_effargs
));
716 lsx_revalloc(user_effargs_size
, eff_chain_count
+1);
717 user_effargs_size
[eff_chain_count
] = 0;
718 lsx_revalloc(nuser_effects
, eff_chain_count
+1);
719 nuser_effects
[eff_chain_count
] = 0;
720 } /* add_eff_chain */
722 /* free_eff_chain() - the inverse of add_eff_chain(). Frees
723 * one effects chain (with index eff_chain_count) such that
724 * there are eff_chain_count left, the last having index
727 static void free_eff_chain(void)
731 for (j
= 0; j
< nuser_effects
[eff_chain_count
]; j
++)
733 free(user_effargs
[eff_chain_count
][j
].name
);
734 user_effargs
[eff_chain_count
][j
].name
= NULL
;
735 for (k
= 0; k
< user_effargs
[eff_chain_count
][j
].argc
; k
++)
737 free(user_effargs
[eff_chain_count
][j
].argv
[k
]);
738 user_effargs
[eff_chain_count
][j
].argv
[k
] = NULL
;
740 user_effargs
[eff_chain_count
][j
].argc
= 0;
741 free(user_effargs
[eff_chain_count
][j
].argv
);
742 user_effargs
[eff_chain_count
][j
].argv
= NULL
;
743 user_effargs
[eff_chain_count
][j
].argv_size
= 0;
745 nuser_effects
[eff_chain_count
] = 0;
746 free(user_effargs
[eff_chain_count
]);
747 } /* free_eff_chain */
749 static void delete_eff_chains(void)
751 while (eff_chain_count
> 0) {
756 free(user_effargs_size
);
759 user_effargs_size
= NULL
;
760 nuser_effects
= NULL
;
761 } /* delete_eff_chains */
763 static sox_bool
is_pseudo_effect(const char *s
)
766 if (strcmp("newfile", s
) == 0 ||
767 strcmp("restart", s
) == 0 ||
771 } /* is_pseudo_effect */
773 static void parse_effects(int argc
, char ** argv
)
775 while (optstate
.ind
< argc
) {
776 size_t eff_offset
, j
;
777 int newline_mode
= 0;
779 eff_offset
= nuser_effects
[eff_chain_count
];
780 if (eff_offset
== user_effargs_size
[eff_chain_count
]) {
781 size_t i
= user_effargs_size
[eff_chain_count
];
782 user_effargs_size
[eff_chain_count
] += EFFARGS_STEP
;
783 lsx_revalloc(user_effargs
[eff_chain_count
], user_effargs_size
[eff_chain_count
]);
784 for (; i
< user_effargs_size
[eff_chain_count
]; i
++) {
785 user_effargs
[eff_chain_count
][i
].argv
= NULL
;
786 user_effargs
[eff_chain_count
][i
].argv_size
= 0;
790 /* pseudo-effect ":" is used to create a new effects chain */
791 if (strcmp(argv
[optstate
.ind
], ":") == 0)
793 /* Only create a new chain if current one has effects.
794 * Error checking will be done when loop is restarted.
796 if (nuser_effects
[eff_chain_count
] != 0)
805 if (strcmp(argv
[optstate
.ind
], "newfile") == 0)
807 /* Start a new effect chain for newfile if user doesn't
808 * manually do it. Restart loop without advancing
809 * optstate.ind to do error checking.
811 if (nuser_effects
[eff_chain_count
] != 0)
818 output_method
= sox_multiple
;
820 else if (strcmp(argv
[optstate
.ind
], "restart") == 0)
822 /* Start a new effect chain for restart if user doesn't
823 * manually do it. Restart loop without advancing
824 * optstate.ind to do error checking.
826 if (nuser_effects
[eff_chain_count
] != 0)
835 /* Name should always be correct! */
836 user_effargs
[eff_chain_count
][eff_offset
].name
= lsx_strdup(argv
[optstate
.ind
]);
838 for (j
= 0; j
< (size_t)(argc
- optstate
.ind
) && !sox_find_effect(argv
[optstate
.ind
+ j
]) &&
839 !is_pseudo_effect(argv
[optstate
.ind
+ j
]); ++j
) {
840 if (j
>= user_effargs
[eff_chain_count
][eff_offset
].argv_size
) {
841 user_effargs
[eff_chain_count
][eff_offset
].argv_size
+= EFFARGS_STEP
;
842 lsx_revalloc(user_effargs
[eff_chain_count
][eff_offset
].argv
,
843 user_effargs
[eff_chain_count
][eff_offset
].argv_size
);
845 user_effargs
[eff_chain_count
][eff_offset
].argv
[j
] = lsx_strdup(argv
[optstate
.ind
+ j
]);
847 user_effargs
[eff_chain_count
][eff_offset
].argc
= j
;
849 optstate
.ind
+= j
; /* Skip past the effect arguments */
850 nuser_effects
[eff_chain_count
]++;
857 } /* parse_effects */
859 static char * * strtoargv(char * s
, int * argc
)
861 sox_bool squote
= sox_false
; /* Single quote mode (') is in effect. */
862 sox_bool dquote
= sox_false
; /* Double quote mode (") is in effect. */
863 sox_bool esc
= sox_false
; /* Escape mode (\) is in effect. */
864 char * t
, * * argv
= NULL
;
866 for (*argc
= 0; *s
;) {
867 for (; isspace(*s
); ++s
); /* Skip past any (more) white space. */
868 if (*s
) { /* Found an arg. */
869 lsx_revalloc(argv
, *argc
+ 1);
870 argv
[(*argc
)++] = s
; /* Store pointer to start of arg. */
871 /* Find the end of the arg: */
872 for (t
= s
; *s
&& (esc
|| squote
|| dquote
|| !isspace(*s
)); ++s
)
873 if (!esc
&& !squote
&& *s
== '"')
874 dquote
= !dquote
; /* Toggle double quote mode. */
875 else if (!esc
&& !dquote
&& *s
== '\'')
876 squote
= !squote
; /* Toggle single quote mode. */
877 else if (!(esc
= !esc
&& *s
== '\\' && s
[1] &&
878 (!squote
&& (s
[1] == '"' || !dquote
))))
879 *t
++ = *s
; /* Only copy if not an active ', ", or \ */
880 s
= *s
? s
+ 1 : s
; /* Skip the 1st white space char. */
881 *t
= '\0'; /* Terminate the arg. */
887 static void read_user_effects(char const *filename
)
889 FILE *file
= fopen(filename
, "r");
890 const size_t buffer_size_step
= 1024;
891 size_t buffer_size
= buffer_size_step
;
892 char *s
= lsx_malloc(buffer_size
); /* buffer for one input line */
896 sox_bool last_was_colon
= sox_false
; /* last line read consisted of ":" only */
898 /* Free any command line options and then re-initialize to
899 * starter user_effargs.
902 current_eff_chain
= 0;
906 lsx_fail("Cannot open effects file `%s': %s", filename
, strerror(errno
));
910 lsx_report("Reading effects from file `%s'", filename
);
912 while(fgets(s
+ pos
, (int) (buffer_size
- pos
), file
)) {
913 int len
= strlen(s
+ pos
);
914 if (len
&& s
[pos
+len
-1] == '\n')
915 s
[pos
+len
-1] = '\0', pos
= 0; /* we've read a complete line */
916 else if (len
== (int)(buffer_size
- pos
- 1)) {
917 /* line was longer than buffer size */
918 buffer_size
+= buffer_size_step
;
919 s
= lsx_realloc(s
, buffer_size
);
921 continue; /* read next part */
923 /* something strange happened; the file might have ended
924 without a '\n', might contain '\0', or a read error
927 break; /* use error reporting after loop */
928 lsx_fail("Error reading effects file `%s' (not a text file?)", filename
);
932 last_was_colon
= sox_false
;
934 argv
= strtoargv(s
, &argc
);
936 if (argv
&& argc
== 1 && strcmp(argv
[0], ":") == 0)
937 last_was_colon
= sox_true
;
940 /* Make sure first option is an effect name. */
941 if (!sox_find_effect(argv
[0]) && !is_pseudo_effect(argv
[0]))
943 lsx_fail("Cannot find an effect called `%s'.", argv
[0]);
947 /* parse_effects normally parses options from command line.
948 * Reset opt index so it thinks its back at beginning of
952 parse_effects(argc
, argv
);
954 /* Advance to next effect but only if current chain has been
955 * filled in. This recovers from side affects of pseudo-effects.
957 if (nuser_effects
[eff_chain_count
] > 0) {
966 lsx_fail("Error reading effects file `%s': %s", filename
, strerror(errno
));
972 if (last_was_colon
|| eff_chain_count
== 0) {
973 /* user explicitly wanted an empty last effects chain,
974 or didn't specify any chains at all */
977 /* there's one unneeded effects chain */
980 } /* read_user_effects */
982 /* Creates users effects and passes in user specified options.
983 * This is done without putting anything into the effects chain
984 * because an effect may set the effp->in_format and we may want
985 * to copy that back into the input/combiner before opening and
987 * Similarly, we may want to use effp->out_format to override the
988 * default values of output file before we open it.
989 * To keep things simple, we create all user effects. Later, when
990 * we add them, some may already be in the chain and we will need to free
993 static void create_user_effects(void)
997 size_t num_effects
= nuser_effects
[current_eff_chain
];
999 /* extend user_efftab, if needed */
1000 if (user_efftab_size
< num_effects
) {
1001 user_efftab_size
= num_effects
;
1002 lsx_revalloc(user_efftab
, num_effects
);
1005 for (i
= 0; i
< num_effects
; i
++) {
1006 effp
= sox_create_effect(sox_find_effect(user_effargs
[current_eff_chain
][i
].name
));
1008 if (effp
->handler
.flags
& SOX_EFF_DEPRECATED
)
1009 lsx_warn("effect `%s' is deprecated; see sox(1) for an alternative",
1010 effp
->handler
.name
);
1011 else if (effp
->handler
.flags
& SOX_EFF_ALPHA
)
1012 lsx_warn("effect `%s' is experimental/incomplete", effp
->handler
.name
);
1013 else if (effp
->handler
.flags
& SOX_EFF_INTERNAL
) {
1014 lsx_fail("`%s' is a libSoX-only effect", effp
->handler
.name
);
1018 /* The failing effect should have displayed an error message */
1019 if (sox_effect_options(effp
, user_effargs
[current_eff_chain
][i
].argc
,
1020 user_effargs
[current_eff_chain
][i
].argv
) == SOX_EOF
)
1023 user_efftab
[i
] = effp
;
1027 /* Add all user effects to the chain. If the output effect's rate or
1028 * channel count do not match the end of the effects chain then
1029 * insert effects to correct this.
1031 * This can be called with the input effect already in the effects
1032 * chain from a previous run. Also, it use a pre-existing
1033 * output effect if its been saved into save_output_eff.
1035 static void add_effects(sox_effects_chain_t
*chain
)
1037 sox_signalinfo_t signal
= combiner_signal
;
1038 int guard
= is_guarded
- 1;
1040 sox_effect_t
* effp
;
1041 char * rate_arg
= is_player
? (play_rate_arg
? play_rate_arg
: "-l") : NULL
;
1043 /* 1st `effect' in the chain is the input combiner_signal.
1044 * add it only if its not there from a previous run. */
1045 if (chain
->length
== 0) {
1046 effp
= sox_create_effect(input_combiner_effect_fn());
1047 sox_add_effect(chain
, effp
, &signal
, &ofile
->ft
->signal
);
1051 /* Add user specified effects; stop before `dither' */
1052 for (i
= 0; i
< nuser_effects
[current_eff_chain
] &&
1053 strcmp(user_efftab
[i
]->handler
.name
, "dither"); i
++) {
1054 if (add_effect(chain
, user_efftab
[i
], &signal
, &ofile
->ft
->signal
,
1055 &guard
) != SOX_SUCCESS
)
1056 exit(2); /* Effects chain should have displayed an error message */
1057 free(user_efftab
[i
]);
1060 /* Add auto effects if still needed at this point */
1061 if (signal
.channels
< ofile
->ft
->signal
.channels
&&
1062 signal
.rate
!= ofile
->ft
->signal
.rate
)
1063 auto_effect(chain
, "rate", rate_arg
!= NULL
, &rate_arg
, &signal
, &guard
);
1064 if (signal
.channels
!= ofile
->ft
->signal
.channels
)
1065 auto_effect(chain
, "channels", 0, NULL
, &signal
, &guard
);
1066 if (signal
.rate
!= ofile
->ft
->signal
.rate
)
1067 auto_effect(chain
, "rate", rate_arg
!= NULL
, &rate_arg
, &signal
, &guard
);
1069 if (is_guarded
&& (do_guarded_norm
|| !(signal
.mult
&& *signal
.mult
== 1))) {
1072 args
[0] = do_guarded_norm
? "-nh" : guard
? "-rh" : "-h";
1073 args
[1] = norm_level
;
1074 auto_effect(chain
, "gain", norm_level
? 2 : 1, args
, &signal
, &no_guard
);
1078 if (i
== nuser_effects
[current_eff_chain
] && !no_dither
&& signal
.precision
>
1079 ofile
->ft
->signal
.precision
&& ofile
->ft
->signal
.precision
< 24)
1080 auto_effect(chain
, "dither", 0, NULL
, &signal
, &guard
);
1082 /* Add user specified effects from `dither' onwards */
1083 for (; i
< nuser_effects
[current_eff_chain
]; i
++, guard
= 2) {
1084 if (add_effect(chain
, user_efftab
[i
], &signal
, &ofile
->ft
->signal
,
1085 &guard
) != SOX_SUCCESS
)
1086 exit(2); /* Effects chain should have displayed an error message */
1087 free(user_efftab
[i
]);
1090 if (!save_output_eff
)
1092 /* Last `effect' in the chain is the output file */
1093 effp
= sox_create_effect(output_effect_fn());
1094 if (sox_add_effect(chain
, effp
, &signal
, &ofile
->ft
->signal
) != SOX_SUCCESS
)
1100 sox_push_effect_last(chain
, save_output_eff
);
1101 save_output_eff
= NULL
;
1104 for (i
= 0; i
< chain
->length
; ++i
) {
1105 char const * format
= sox_globals
.verbosity
> 3?
1106 "effects chain: %-10s %7gHz %2u channels %7s %2u bits %s" :
1107 "effects chain: %-10s %7gHz %2u channels";
1108 sox_effect_t
const * effp
= &chain
->effects
[i
][0];
1109 lsx_report(format
, effp
->handler
.name
, effp
->out_signal
.rate
,
1110 effp
->out_signal
.channels
,
1111 (effp
->handler
.flags
& SOX_EFF_MCHAN
)? "(multi)" : "",
1112 effp
->out_signal
.precision
,
1113 effp
->out_signal
.length
!= SOX_UNKNOWN_LEN
?
1114 str_time(effp
->out_signal
.length
/effp
->out_signal
.channels
/effp
->out_signal
.rate
) :
1120 static int advance_eff_chain(void)
1122 sox_bool reuse_output
= sox_true
;
1124 very_first_effchain
= sox_false
;
1126 /* If input file reached EOF then delete all effects in current
1127 * chain and restart the current chain.
1129 * This is only used with sox_sequence combine mode even though
1130 * we do not specifically check for that method.
1133 sox_delete_effects(effects_chain
);
1136 /* If user requested to restart this effect chain then
1137 * do not advance to next. Usually, this is because
1138 * an option to current effect was changed.
1140 if (user_restart_eff
)
1141 user_restart_eff
= sox_false
;
1142 /* Effect chain stopped so advance to next effect chain but
1143 * quite if no more chains exist.
1145 else if (++current_eff_chain
>= eff_chain_count
)
1148 while (nuser_effects
[current_eff_chain
] == 1 &&
1149 is_pseudo_effect(user_effargs
[current_eff_chain
][0].name
))
1151 if (strcmp("newfile", user_effargs
[current_eff_chain
][0].name
) == 0)
1153 if (++current_eff_chain
>= eff_chain_count
)
1155 reuse_output
= sox_false
;
1157 else if (strcmp("restart", user_effargs
[current_eff_chain
][0].name
) == 0)
1158 current_eff_chain
= 0;
1162 save_output_eff
= sox_pop_effect_last(effects_chain
);
1164 while (effects_chain
->length
> 1)
1165 sox_delete_effect_last(effects_chain
);
1168 } /* advance_eff_chain */
1170 static uint64_t total_clips(void)
1174 for (i
= 0; i
< file_count
; ++i
)
1175 clips
+= files
[i
]->ft
->clips
+ files
[i
]->volume_clips
;
1176 return clips
+ mixing_clips
+ sox_effects_clips(effects_chain
);
1179 static sox_bool
since(struct timeval
* then
, double secs
, sox_bool always_reset
)
1184 gettimeofday(&now
, NULL
);
1185 d
= now
.tv_sec
- then
->tv_sec
;
1186 ret
= d
> ceil(secs
) || now
.tv_usec
- then
->tv_usec
+ d
* TIME_FRAC
>= secs
* TIME_FRAC
;
1187 if (ret
|| always_reset
)
1192 static int termwidth
= 80;
1195 static void get_termwidth(int s
)
1199 if (!ioctl(2, TIOCGWINSZ
, &w
))
1200 termwidth
= w
.ws_col
;
1204 #define MIN_HEADROOM 6.
1205 static double min_headroom
= MIN_HEADROOM
;
1207 static char const * vu(unsigned channel
)
1209 static struct timeval then
;
1210 static char const * const text
[][2] = {
1211 /* White: 2dB steps */
1212 {"", ""}, {"-", "-"}, {"=", "="}, {"-=", "=-"},
1213 {"==", "=="}, {"-==", "==-"}, {"===", "==="}, {"-===", "===-"},
1214 {"====", "===="}, {"-====", "====-"}, {"=====", "====="},
1215 {"-=====", "=====-"}, {"======", "======"},
1216 /* Red: 1dB steps */
1217 {"!=====", "=====!"},
1219 int const red
= 1, white
= array_length(text
) - red
;
1220 double const MAX
= SOX_SAMPLE_MAX
, MIN
= SOX_SAMPLE_MIN
;
1221 double linear
= max(omax
[channel
] / MAX
, omin
[channel
] / MIN
);
1222 double dB
= linear_to_dB(linear
);
1223 int vu_dB
= linear
? floor(2 * white
+ red
+ dB
) : 0;
1224 int index
= vu_dB
< 2 * white
? max(vu_dB
/ 2, 0) : min(vu_dB
- white
, red
+ white
- 1);
1225 omax
[channel
] = omin
[channel
] = 0;
1226 if (-dB
< min_headroom
) {
1227 gettimeofday(&then
, NULL
);
1230 else if (since(&then
, 3., sox_false
))
1233 return text
[index
][channel
];
1236 static char * headroom(void)
1238 if (min_headroom
< MIN_HEADROOM
) {
1239 static char buff
[16];
1240 unsigned h
= (unsigned)(min_headroom
* 10);
1241 snprintf(buff
, sizeof(buff
), "Hd:%u.%u", h
/10, h
% 10);
1247 static void display_status(sox_bool all_done
)
1249 static struct timeval then
;
1252 if (all_done
|| since(&then
, .1, sox_false
)) {
1253 double read_time
= (double)read_wide_samples
/ combiner_signal
.rate
;
1254 double left_time
= 0, in_time
= 0, percentage
= 0;
1257 if (input_wide_samples
) {
1258 in_time
= (double)input_wide_samples
/ combiner_signal
.rate
;
1259 left_time
= max(in_time
- read_time
, 0);
1260 percentage
= max(100. * read_wide_samples
/ input_wide_samples
, 0);
1262 snprintf(buf
, min(termwidth
+ 2, sizeof(buf
)),
1263 "\rIn:%-5s %s [%s] Out:%-5s [%6s|%-6s] %s Clip:%-5s",
1264 lsx_sigfigs3p(percentage
), str_time(read_time
), str_time(left_time
),
1265 lsx_sigfigs3((double)output_samples
),
1266 vu(0), vu(1), headroom(), lsx_sigfigs3((double)total_clips()));
1270 fputc('\n', stderr
);
1273 #ifdef HAVE_TERMIOS_H
1274 static int kbhit(void)
1276 struct timeval time_val
= {0, 0};
1280 FD_SET(fileno(stdin
), &fdset
);
1281 select(fileno(stdin
) + 1, &fdset
, NULL
, NULL
, &time_val
);
1282 return FD_ISSET(fileno(stdin
), &fdset
);
1284 #elif !defined(HAVE_CONIO_H)
1288 static int update_status(sox_bool all_done
, void * client_data
)
1291 if (interactive
) while (kbhit()) {
1299 #ifdef MORE_INTERACTIVE
1300 if (files
[current_input
]->ft
->handler
.seek
&&
1301 files
[current_input
]->ft
->seekable
)
1305 uint64_t jump
= files
[current_input
]->ft
->signal
.rate
*30; /* 30 sec. */
1306 if (input_wide_samples
== 0 ||
1307 read_wide_samples
+jump
< input_wide_samples
) {
1308 read_wide_samples
+= jump
;
1309 sox_seek(files
[current_input
]->ft
, read_wide_samples
,
1311 /* FIXME: Do something if seek fails. */
1316 uint64_t jump
= files
[current_input
]->ft
->signal
.rate
*30; /* 30 sec. */
1317 read_wide_samples
= jump
< read_wide_samples
?
1318 read_wide_samples
-jump
: 0;
1319 sox_seek(files
[current_input
]->ft
, read_wide_samples
,
1321 /* FIXME: Do something if seek fails. */
1326 /* Not very useful, eh! Sample though of the place you
1327 * could change the value to effects options
1328 * like vol or speed or remix.
1329 * Modify values in user_effargs[current_eff_chain][xxx]
1330 * and then chain will be drain()ed and restarted whence
1331 * this function is existed.
1333 user_restart_eff
= sox_true
;
1338 display_status(all_done
|| user_abort
);
1339 return (user_abort
|| user_restart_eff
) ? SOX_EOF
: SOX_SUCCESS
;
1342 static void optimize_trim(void)
1344 /* Speed hack. If the "trim" effect is the first effect then peek inside its
1345 * "effect descriptor" and see what the start location is. This has to be
1346 * done after its start() is called to have the correct location. Also, only
1347 * do this when only working with one input file. This is because the logic
1348 * to do it for multiple files is complex and probably never used. The same
1349 * is true for a restarted or additional effects chain (relative positioning
1350 * within the file and possible samples still buffered in the input effect
1351 * would have to be taken into account). This hack is a huge time savings
1352 * when trimming gigs of audio data into managable chunks. */
1353 if (input_count
== 1 && very_first_effchain
&& effects_chain
->length
> 1 &&
1354 strcmp(effects_chain
->effects
[1][0].handler
.name
, "trim") == 0) {
1355 if (files
[0]->ft
->handler
.seek
&& files
[0]->ft
->seekable
){
1356 uint64_t offset
= sox_trim_get_start(&effects_chain
->effects
[1][0]);
1357 if (offset
&& sox_seek(files
[0]->ft
, offset
, SOX_SEEK_SET
) == SOX_SUCCESS
) {
1358 read_wide_samples
= offset
/ files
[0]->ft
->signal
.channels
;
1359 /* Assuming a failed seek stayed where it was. If the seek worked then
1360 * reset the start location of trim so that it thinks user didn't
1361 * request a skip. */
1362 sox_trim_clear_start(&effects_chain
->effects
[1][0]);
1363 lsx_debug("optimize_trim successful");
1369 static sox_bool
overwrite_permitted(char const * filename
)
1374 lsx_report("Overwriting `%s'", filename
);
1377 lsx_warn("Output file `%s' already exists", filename
);
1378 if (!stdin_is_a_tty
)
1380 do fprintf(stderr
, "%s sox: overwrite `%s' (y/n)? ", myname
, filename
);
1381 while (scanf(" %c%*[^\n]", &c
) != 1 || !strchr("yYnN", c
));
1382 return c
== 'y' || c
== 'Y';
1385 static char *fndup_with_count(const char *filename
, size_t count
)
1387 char *expand_fn
, *efn
;
1388 const char *fn
, *ext
, *end
;
1389 sox_bool found_marker
= sox_false
;
1393 efn
= expand_fn
= lsx_malloc((size_t)FILENAME_MAX
);
1395 /* Find extension in case user didn't specify a substitution
1398 end
= ext
= filename
+ strlen(filename
);
1399 while (ext
> filename
&& *ext
!= '.')
1402 /* In case extension not found, point back to end of string to do less
1410 /* Look for %n. If found, replace with count. Can specify an
1411 * option width of 1-9.
1417 if (*fn
>= '1' && *fn
<= '9')
1425 found_marker
= sox_true
;
1429 sprintf(format
, "%%0%cd", width
);
1433 strcpy(format
, "%02d");
1436 efn
+= sprintf(efn
, format
, count
);
1448 /* If user didn't tell us what to do then default to putting
1449 * the count right before file extension.
1453 efn
-= strlen (ext
);
1455 sprintf(efn
, "%03lu", (unsigned long)count
);
1463 static void open_output_file(void)
1467 sox_comments_t p
= ofile
->oob
.comments
;
1468 sox_oob_t oob
= files
[0]->ft
->oob
;
1471 /* Skip opening file if we are not recreating output effect */
1472 if (save_output_eff
)
1475 oob
.comments
= sox_copy_comments(files
[0]->ft
->oob
.comments
);
1477 if (!oob
.comments
&& !p
)
1478 sox_append_comment(&oob
.comments
, "Processed by SoX");
1481 sox_delete_comments(&oob
.comments
);
1485 sox_append_comment(&oob
.comments
, *p
++);
1488 /* Copy loop info, resizing appropriately it's in samples, so # channels
1489 * don't matter FIXME: This doesn't work for multi-file processing or effects
1490 * that change file length. */
1491 factor
= (double) ofile
->signal
.rate
/ combiner_signal
.rate
;
1492 for (i
= 0; i
< SOX_MAX_NLOOPS
; i
++) {
1493 oob
.loops
[i
].start
= oob
.loops
[i
].start
* factor
;
1494 oob
.loops
[i
].length
= oob
.loops
[i
].length
* factor
;
1497 if (output_method
== sox_multiple
)
1498 expand_fn
= fndup_with_count(ofile
->filename
, ++output_count
);
1500 expand_fn
= lsx_strdup(ofile
->filename
);
1501 ofile
->ft
= sox_open_write(expand_fn
, &ofile
->signal
, &ofile
->encoding
,
1502 ofile
->filetype
, &oob
, overwrite_permitted
);
1503 sox_delete_comments(&oob
.comments
);
1507 /* sox_open_write() will call lsx_warn for most errors.
1508 * Rely on that printing something. */
1511 /* If whether to enable the progress display (similar to that of ogg123) has
1512 * not been specified by the user, auto turn on when outputting to an audio
1514 if (show_progress
== sox_option_default
)
1515 show_progress
= (ofile
->ft
->handler
.flags
& SOX_FILE_DEVICE
) != 0 &&
1516 (ofile
->ft
->handler
.flags
& SOX_FILE_PHONY
) == 0;
1518 report_file_info(ofile
);
1521 static void setsig(int sig
, void (*handler
)(int))
1523 #ifdef HAVE_SIGACTION
1524 struct sigaction sa
;
1526 sa
.sa_handler
= handler
;
1527 sigemptyset(&sa
.sa_mask
);
1530 sigaction(sig
, &sa
, NULL
);
1532 signal(sig
, handler
);
1536 static void sigint(int s
)
1538 static struct timeval then
;
1539 if (input_count
> 1 && show_progress
&& s
== SIGINT
&&
1540 is_serial(combine_method
) && since(&then
, 1.0, sox_true
))
1542 setsig(SIGINT
, sigint
);
1543 user_skip
= sox_true
;
1545 else user_abort
= sox_true
;
1548 static void calculate_combiner_signal_parameters(void)
1552 /* If user didn't specify # of channels then see if an effect
1553 * is specifying them. This is of most use currently with the
1554 * synth effect were user can use null input handler and specify
1555 * channel counts directly in effect. Forcing to use -c with
1556 * -n isn't as convenient.
1558 for (i
= 0; i
< input_count
; i
++) {
1560 for (j
=0; j
< nuser_effects
[current_eff_chain
] &&
1561 !files
[i
]->ft
->signal
.channels
; ++j
)
1562 files
[i
]->ft
->signal
.channels
= user_efftab
[j
]->in_signal
.channels
;
1563 /* For historical reasons, default to one channel if not specified. */
1564 if (!files
[i
]->ft
->signal
.channels
)
1565 files
[i
]->ft
->signal
.channels
= 1;
1568 /* Set the combiner output signal attributes to those of the 1st/next input
1569 * file. If we are in sox_sequence mode then we don't need to check the
1570 * attributes of the other inputs, otherwise, it is mandatory that all input
1571 * files have the same sample rate, and for sox_concatenate, it is mandatory
1572 * that they have the same number of channels, otherwise, the number of
1573 * channels at the output of the combiner is calculated according to the
1575 combiner_signal
= files
[current_input
]->ft
->signal
;
1576 if (combine_method
== sox_sequence
) {
1577 /* Report all input files; do this only the 1st time process() is called: */
1578 if (!current_input
) for (i
= 0; i
< input_count
; i
++)
1579 report_file_info(files
[i
]);
1580 combiner_signal
.length
= SOX_UNKNOWN_LEN
;
1582 size_t total_channels
= 0;
1583 size_t min_channels
= SOX_SIZE_MAX
;
1584 size_t max_channels
= 0;
1585 size_t min_rate
= SOX_SIZE_MAX
;
1586 size_t max_rate
= 0;
1587 uint64_t total_length
= 0, max_length_ws
= 0;
1589 /* Report all input files and gather info on differing rates & numbers of
1590 * channels, and on the resulting output audio length: */
1591 for (i
= 0; i
< input_count
; i
++) {
1592 report_file_info(files
[i
]);
1593 total_channels
+= files
[i
]->ft
->signal
.channels
;
1594 min_channels
= min(min_channels
, files
[i
]->ft
->signal
.channels
);
1595 max_channels
= max(max_channels
, files
[i
]->ft
->signal
.channels
);
1596 min_rate
= min(min_rate
, files
[i
]->ft
->signal
.rate
);
1597 max_rate
= max(max_rate
, files
[i
]->ft
->signal
.rate
);
1598 max_length_ws
= files
[i
]->ft
->signal
.length
?
1599 max(max_length_ws
, files
[i
]->ft
->signal
.length
/ files
[i
]->ft
->signal
.channels
) :
1601 if (total_length
!= SOX_UNKNOWN_LEN
&& files
[i
]->ft
->signal
.length
)
1602 total_length
+= files
[i
]->ft
->signal
.length
;
1604 total_length
= SOX_UNKNOWN_LEN
;
1607 /* Check for invalid/unusual rate or channel combinations: */
1608 if (min_rate
!= max_rate
)
1609 lsx_fail("Input files must have the same sample-rate");
1610 /* Don't exit quite yet; give the user any other message 1st */
1611 if (min_channels
!= max_channels
) {
1612 if (combine_method
== sox_concatenate
) {
1613 lsx_fail("Input files must have the same # channels");
1615 } else if (combine_method
!= sox_merge
)
1616 lsx_warn("Input files don't have the same # channels");
1618 if (min_rate
!= max_rate
)
1621 /* Store the calculated # of combined channels: */
1622 combiner_signal
.channels
=
1623 combine_method
== sox_merge
? total_channels
: max_channels
;
1625 if (combine_method
== sox_concatenate
)
1626 combiner_signal
.length
= total_length
;
1627 else if (is_parallel(combine_method
))
1628 combiner_signal
.length
= max_length_ws
!= SOX_UNKNOWN_LEN
?
1629 max_length_ws
* combiner_signal
.channels
: SOX_UNKNOWN_LEN
;
1631 } /* calculate_combiner_signal_parameters */
1633 static void calculate_output_signal_parameters(void)
1635 sox_bool known_length
= combine_method
!= sox_sequence
;
1639 /* Report all input files and gather info on differing rates & numbers of
1640 * channels, and on the resulting output audio length: */
1641 for (i
= 0; i
< input_count
; i
++) {
1642 known_length
= known_length
&& files
[i
]->ft
->signal
.length
!= SOX_UNSPEC
;
1643 if (combine_method
== sox_concatenate
)
1644 olen
+= files
[i
]->ft
->signal
.length
/ files
[i
]->ft
->signal
.channels
;
1646 olen
= max(olen
, files
[i
]->ft
->signal
.length
/ files
[i
]->ft
->signal
.channels
);
1649 /* Determine the output file signal attributes; set from user options
1651 ofile
->signal
= ofile_signal_options
;
1653 /* If no user option for output rate or # of channels, set from the last
1654 * effect that sets these, or from the input combiner if there is none such */
1655 for (i
= 0; i
< nuser_effects
[current_eff_chain
] && !ofile
->signal
.rate
; ++i
)
1656 ofile
->signal
.rate
= user_efftab
[nuser_effects
[current_eff_chain
] - 1 - i
]->out_signal
.rate
;
1657 for (i
= 0; i
< nuser_effects
[current_eff_chain
] && !ofile
->signal
.channels
; ++i
)
1658 ofile
->signal
.channels
= user_efftab
[nuser_effects
[current_eff_chain
] - 1 - i
]->out_signal
.channels
;
1659 if (!ofile
->signal
.rate
)
1660 ofile
->signal
.rate
= combiner_signal
.rate
;
1661 if (!ofile
->signal
.channels
)
1662 ofile
->signal
.channels
= combiner_signal
.channels
;
1664 /* FIXME: comment this: */
1665 ofile
->signal
.precision
= combiner_signal
.precision
;
1667 /* If any given user effect modifies the audio length, then we assume that
1668 * we don't know what the output length will be. FIXME: in most cases,
1669 * an effect that modifies length will be able to determine by how much from
1670 * its getopts parameters, so olen should be calculable. */
1671 for (i
= 0; i
< nuser_effects
[current_eff_chain
]; i
++)
1672 known_length
= known_length
&& !(user_efftab
[i
]->handler
.flags
& SOX_EFF_LENGTH
);
1676 ofile
->signal
.length
= (uint64_t)(olen
* ofile
->signal
.channels
* ofile
->signal
.rate
/ combiner_signal
.rate
+ .5);
1679 static void set_combiner_and_output_encoding_parameters(void)
1681 /* The input encoding parameters passed to the effects chain are those of
1682 * the first input file (for each segued block if sox_sequence):*/
1683 combiner_encoding
= files
[current_input
]->ft
->encoding
;
1685 /* Determine the output file encoding attributes; set from user options
1687 ofile
->encoding
= ofile_encoding_options
;
1689 /* Get unspecified output file encoding attributes from the input file and
1690 * set the output file to the resultant encoding if this is supported by the
1691 * output file type; if not, the output file handler should select an
1692 * encoding suitable for the output signal and its precision. */
1694 sox_encodinginfo_t t
= ofile
->encoding
;
1696 t
.encoding
= combiner_encoding
.encoding
;
1697 if (!t
.bits_per_sample
)
1698 t
.bits_per_sample
= combiner_encoding
.bits_per_sample
;
1699 if (sox_format_supports_encoding(ofile
->filename
, ofile
->filetype
, &t
))
1700 ofile
->encoding
= t
;
1704 static int process(void)
1705 { /* Input(s) -> Balancing -> Combiner -> Effects -> Output */
1708 create_user_effects();
1710 calculate_combiner_signal_parameters();
1711 set_combiner_and_output_encoding_parameters();
1712 calculate_output_signal_parameters();
1716 effects_chain
= sox_create_effects_chain(&combiner_encoding
,
1717 &ofile
->ft
->encoding
);
1718 add_effects(effects_chain
);
1720 if (very_first_effchain
)
1723 #if defined(HAVE_TERMIOS_H) || defined(HAVE_CONIO_H)
1724 if (stdin_is_a_tty
) {
1725 if (show_progress
&& is_player
&& !interactive
) {
1726 lsx_debug("automatically entering interactive mode");
1727 interactive
= sox_true
;
1729 } else if (interactive
) {
1730 /* User called for interactive mode, but ... */
1731 lsx_warn("Standard input has to be a terminal for interactive mode");
1732 interactive
= sox_false
;
1735 #ifdef HAVE_TERMIOS_H
1736 /* Prepare terminal for interactive mode and save the original termios
1737 settings. Do this only once, otherwise the "original" settings won't
1738 be original anymore after a second call to process() (next/restarted
1740 if (interactive
&& !original_termios_saved
) {
1741 struct termios modified_termios
;
1743 original_termios_saved
= sox_true
;
1744 tcgetattr(fileno(stdin
), &original_termios
);
1745 modified_termios
= original_termios
;
1746 modified_termios
.c_lflag
&= ~(ICANON
| ECHO
);
1747 modified_termios
.c_cc
[VMIN
] = modified_termios
.c_cc
[VTIME
] = 0;
1748 tcsetattr(fileno(stdin
), TCSANOW
, &modified_termios
);
1751 #if defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
1753 int fd
= fileno(stdin
);
1754 int flags
= fcntl(fd
, F_GETFL
);
1756 lsx_warn("error getting flags on stdin descriptor: %s", strerror(errno
));
1757 } else if (!(flags
& O_NONBLOCK
)) {
1758 if (fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
) == -1)
1759 lsx_warn("error setting non-blocking on stdin: %s", strerror(errno
));
1767 setsig(SIGWINCH
, get_termwidth
);
1771 setsig(SIGTERM
, sigint
); /* Stop gracefully, as soon as we possibly can. */
1772 setsig(SIGINT
, sigint
); /* Either skip current input or behave as SIGTERM. */
1773 if (very_first_effchain
) {
1776 gettimeofday(&now
, NULL
);
1777 d
= now
.tv_sec
- load_timeofday
.tv_sec
+ (now
.tv_usec
- load_timeofday
.tv_usec
) / TIME_FRAC
;
1778 lsx_debug("start-up time = %g", d
);
1780 flow_status
= sox_flow_effects(effects_chain
, update_status
, NULL
);
1782 /* Don't return SOX_EOF if
1783 * 1) input reach EOF and there are more input files to process or
1784 * 2) output didn't return EOF (disk full?) there are more
1786 * For case #2, something else must decide when to stop processing.
1788 if ((input_eof
&& current_input
< input_count
) ||
1789 (!output_eof
&& current_eff_chain
< eff_chain_count
))
1790 flow_status
= SOX_SUCCESS
;
1795 static void display_SoX_version(FILE * file
)
1797 #if HAVE_SYS_UTSNAME_H
1800 const sox_version_info_t
* info
= sox_version_info();
1802 fprintf(file
, "%s: SoX v%s%s%s\n",
1805 info
->version_extra
? "-" : "",
1806 info
->version_extra
? info
->version_extra
: "");
1808 if (sox_globals
.verbosity
> 3) {
1810 fprintf(file
, "time: %s\n", info
->time
);
1812 fprintf(file
, "issue: %s\n", info
->distro
);
1813 #if HAVE_SYS_UTSNAME_H
1815 fprintf(file
, "uname: %s %s %s %s %s\n", uts
.sysname
, uts
.nodename
,
1816 uts
.release
, uts
.version
, uts
.machine
);
1819 fprintf(file
, "compiler: %s\n", info
->compiler
);
1821 fprintf(file
, "arch: %s\n", info
->arch
);
1825 static int strcmp_p(const void *p1
, const void *p2
)
1827 return strcmp(*(const char **)p1
, *(const char **)p2
);
1830 static void display_supported_formats(void)
1833 char const * * format_list
;
1834 char const * const * names
;
1837 for (i
= formats
= 0; sox_format_fns
[i
].fn
; ++i
) {
1838 char const * const *names
= sox_format_fns
[i
].fn()->names
;
1842 format_list
= lsx_malloc(formats
* sizeof(*format_list
));
1844 printf("AUDIO FILE FORMATS:");
1845 for (i
= formats
= 0; sox_format_fns
[i
].fn
; ++i
) {
1846 sox_format_handler_t
const * handler
= sox_format_fns
[i
].fn();
1847 if (!(handler
->flags
& SOX_FILE_DEVICE
))
1848 for (names
= handler
->names
; *names
; ++names
)
1849 if (!strchr(*names
, '/'))
1850 format_list
[formats
++] = *names
;
1852 qsort((void*)format_list
, formats
, sizeof(*format_list
), strcmp_p
);
1853 for (i
= 0; i
< formats
; i
++)
1854 printf(" %s", format_list
[i
]);
1857 printf("PLAYLIST FORMATS: m3u pls\nAUDIO DEVICE DRIVERS:");
1858 for (i
= formats
= 0; sox_format_fns
[i
].fn
; ++i
) {
1859 sox_format_handler_t
const * handler
= sox_format_fns
[i
].fn();
1860 if ((handler
->flags
& SOX_FILE_DEVICE
) && !(handler
->flags
& SOX_FILE_PHONY
))
1861 for (names
= handler
->names
; *names
; ++names
)
1862 format_list
[formats
++] = *names
;
1864 qsort((void*)format_list
, formats
, sizeof(*format_list
), strcmp_p
);
1865 for (i
= 0; i
< formats
; i
++)
1866 printf(" %s", format_list
[i
]);
1869 free((void*)format_list
);
1872 static void display_supported_effects(void)
1875 const sox_effect_handler_t
*e
;
1878 for (i
= 0; sox_effect_fns
[i
]; i
++) {
1879 e
= sox_effect_fns
[i
]();
1881 printf(" %s%s", e
->name
, (e
->flags
& SOX_EFF_DEPRECATED
)? "*" : (e
->flags
& SOX_EFF_ALPHA
)? "+" : (e
->flags
& SOX_EFF_INTERNAL
)? "#" : "");
1883 puts("\n * Deprecated effect + Experimental effect # LibSoX-only effect");
1886 static void usage(char const * message
)
1888 const sox_version_info_t
* info
= sox_version_info();
1890 static char const * const lines1
[] = {
1891 "SPECIAL FILENAMES (infile, outfile):",
1892 "- Pipe/redirect input/output (stdin/stdout); may need -t",
1893 "-d, --default-device Use the default audio device (where available)",
1894 "-n, --null Use the `null' file handler; e.g. with synth effect",
1895 "-p, --sox-pipe Alias for `-t sox -'"
1897 static char const * const linesPopen
[] = {
1898 "\nSPECIAL FILENAMES (infile only):",
1899 "\"|program [options] ...\" Pipe input from external program (where supported)",
1900 "http://server/file Use the given URL as input file (where supported)"
1902 static char const * const lines2
[] = {
1904 "GLOBAL OPTIONS (gopts) (can be specified at any point before the first effect):",
1905 "--buffer BYTES Set the size of all processing buffers (default 8192)",
1906 "--clobber Don't prompt to overwrite output file (default)",
1907 "--combine concatenate Concatenate all input files (default for sox, rec)",
1908 "--combine sequence Sequence all input files (default for play)",
1909 "-D, --no-dither Don't dither automatically",
1910 "--dft-min NUM Minimum size (log2) for DFT processing (default 10)",
1911 "--effects-file FILENAME File containing effects and options",
1912 "-G, --guard Use temporary files to guard against clipping",
1913 "-h, --help Display version number and usage information",
1914 "--help-effect NAME Show usage of effect NAME, or NAME=all for all",
1915 "--help-format NAME Show info on format NAME, or NAME=all for all",
1916 "--i, --info Behave as soxi(1)",
1917 "--input-buffer BYTES Override the input buffer size (default: as --buffer)",
1918 "--no-clobber Prompt to overwrite output file",
1919 "-m, --combine mix Mix multiple input files (instead of concatenating)",
1920 "--combine mix-power Mix to equal power (instead of concatenating)",
1921 "-M, --combine merge Merge multiple input files (instead of concatenating)"
1923 static char const * const linesMagic
[] = {
1924 "--magic Use `magic' file-type detection"
1926 static char const * const linesThreads
[] = {
1927 "--multi-threaded Enable parallel effects channels processing"
1929 static char const * const lines3
[] = {
1930 "--norm Guard (see --guard) & normalise",
1931 "--play-rate-arg ARG Default `rate' argument for auto-resample with `play'",
1932 "--plot gnuplot|octave Generate script to plot response of filter effect",
1933 "-q, --no-show-progress Run in quiet mode; opposite of -S",
1934 "--replay-gain track|album|off Default: off (sox, rec), track (play)",
1935 "-R Use default random numbers (same on each run of SoX)",
1936 "-S, --show-progress Display progress while processing audio data",
1937 "--single-threaded Disable parallel effects channels processing",
1938 "--temp DIRECTORY Specify the directory to use for temporary files",
1939 "-T, --combine multiply Multiply samples of corresponding channels from all",
1940 " input files (instead of concatenating)",
1941 "--version Display version number of SoX and exit",
1942 "-V[LEVEL] Increment or set verbosity level (default 2); levels:",
1943 " 1: failure messages",
1945 " 3: details of processing",
1946 " 4-6: increasing levels of debug messages",
1947 "FORMAT OPTIONS (fopts):",
1948 "Input file format options need only be supplied for files that are headerless.",
1949 "Output files will have the same format as the input file where possible and not",
1950 "overridden by any of various means including providing output format options.",
1952 "-v|--volume FACTOR Input file volume adjustment factor (real number)",
1953 "--ignore-length Ignore input file length given in header; read to EOF",
1954 "-t|--type FILETYPE File type of audio",
1955 "-e|--encoding ENCODING Set encoding (ENCODING may be one of signed-integer,",
1956 " unsigned-integer, floating-point, mu-law, a-law,",
1957 " ima-adpcm, ms-adpcm, gsm-full-rate)",
1958 "-b|--bits BITS Encoded sample size in bits",
1959 "-N|--reverse-nibbles Encoded nibble-order",
1960 "-X|--reverse-bits Encoded bit-order",
1961 "--endian little|big|swap Encoded byte-order; swap means opposite to default",
1962 "-L/-B/-x Short options for the above",
1963 "-c|--channels CHANNELS Number of channels of audio data; e.g. 2 = stereo",
1964 "-r|--rate RATE Sample rate of audio",
1965 "-C|--compression FACTOR Compression factor for output format",
1966 "--add-comment TEXT Append output file comment",
1967 "--comment TEXT Specify comment text for the output file",
1968 "--comment-file FILENAME File containing comment text for the output file",
1970 "--no-glob Don't `glob' wildcard match the following filename",
1974 if (!(sox_globals
.verbosity
> 2)) {
1975 display_SoX_version(stdout
);
1980 lsx_fail("%s\n", message
); /* N.B. stderr */
1982 printf("Usage summary: [gopts] [[fopts] infile]... [fopts]%s [effect [effopt]]...\n\n",
1983 sox_mode
== sox_play
? "" : " outfile");
1984 for (i
= 0; i
< array_length(lines1
); ++i
)
1986 if (info
->flags
& sox_version_have_popen
)
1987 for (i
= 0; i
< array_length(linesPopen
); ++i
)
1988 puts(linesPopen
[i
]);
1989 for (i
= 0; i
< array_length(lines2
); ++i
)
1991 if (info
->flags
& sox_version_have_magic
)
1992 for (i
= 0; i
< array_length(linesMagic
); ++i
)
1993 puts(linesMagic
[i
]);
1994 if (info
->flags
& sox_version_have_threads
)
1995 for (i
= 0; i
< array_length(linesThreads
); ++i
)
1996 puts(linesThreads
[i
]);
1997 for (i
= 0; i
< array_length(lines3
); ++i
)
1999 display_supported_formats();
2000 display_supported_effects();
2001 printf("EFFECT OPTIONS (effopts): effect dependent; see --help-effect\n");
2002 exit(message
!= NULL
);
2005 static void usage_effect(char const * name
)
2009 display_SoX_version(stdout
);
2012 if (strcmp("all", name
) && !sox_find_effect(name
)) {
2013 printf("Cannot find an effect called `%s'.\n", name
);
2014 display_supported_effects();
2017 printf("Effect usage:\n\n");
2019 for (i
= 0; sox_effect_fns
[i
]; i
++) {
2020 const sox_effect_handler_t
*e
= sox_effect_fns
[i
]();
2021 if (e
&& e
->name
&& (!strcmp("all", name
) || !strcmp(e
->name
, name
))) {
2022 printf("%s %s\n", e
->name
, e
->usage
? e
->usage
: "");
2023 if (e
->flags
& (SOX_EFF_DEPRECATED
| SOX_EFF_ALPHA
| SOX_EFF_INTERNAL
))
2025 if (e
->flags
& SOX_EFF_DEPRECATED
)
2026 printf("`%s' is deprecated\n", e
->name
);
2027 if (e
->flags
& SOX_EFF_ALPHA
)
2028 printf("`%s' is experimental/incomplete\n", e
->name
);
2029 if (e
->flags
& SOX_EFF_INTERNAL
)
2030 printf("`%s' is libSoX-only\n", e
->name
);
2038 static void usage_format1(sox_format_handler_t
const * f
)
2040 char const * const * names
;
2042 printf("\nFormat: %s\n", f
->names
[0]);
2043 printf("Description: %s\n", f
->description
);
2045 printf("Also handles:");
2046 for (names
= f
->names
+ 1; *names
; ++names
)
2047 printf(" %s", *names
);
2050 if (f
->flags
& SOX_FILE_CHANS
) {
2051 printf("Channels restricted to:");
2052 if (f
->flags
& SOX_FILE_MONO
) printf(" mono");
2053 if (f
->flags
& SOX_FILE_STEREO
) printf(" stereo");
2054 if (f
->flags
& SOX_FILE_QUAD
) printf(" quad");
2057 if (f
->write_rates
) {
2058 sox_rate_t
const * p
= f
->write_rates
;
2059 printf("Sample-rate restricted to:");
2061 printf(" %g", *p
++);
2064 printf("Reads: %s\n", f
->startread
|| f
->read
? "yes" : "no");
2065 if (f
->startwrite
|| f
->write
) {
2066 if (f
->write_formats
) {
2069 #define enc_arg(T) (T)f->write_formats[i++]
2072 while ((e
= enc_arg(sox_encoding_t
)))
2074 s
= enc_arg(unsigned);
2075 if (sox_precision(e
, s
)) {
2078 printf("%2u-bit ", s
);
2079 printf("%s (%u-bit precision)\n", sox_encodings_info
[e
].desc
, sox_precision(e
, s
));
2083 else puts("Writes: yes");
2085 else puts("Writes: no");
2088 static void usage_format(char const * name
)
2090 sox_format_handler_t
const * f
;
2093 display_SoX_version(stdout
);
2095 if (strcmp("all", name
)) {
2096 if (!(f
= sox_find_format(name
, sox_false
))) {
2097 printf("Cannot find a format called `%s'.\n", name
);
2098 display_supported_formats();
2100 else usage_format1(f
);
2103 for (i
= 0; sox_format_fns
[i
].fn
; ++i
) {
2104 sox_format_handler_t
const * f
= sox_format_fns
[i
].fn();
2105 if (!(f
->flags
& SOX_FILE_PHONY
))
2112 static void read_comment_file(sox_comments_t
* comments
, char const * const filename
)
2115 size_t text_length
= 100;
2116 char * text
= lsx_malloc(text_length
+ 1);
2117 FILE * file
= fopen(filename
, "r");
2120 lsx_fail("Cannot open comment file `%s'", filename
);
2126 while ((c
= getc(file
)) != EOF
&& !strchr("\r\n", c
)) {
2127 if (i
== text_length
)
2128 text
= lsx_realloc(text
, (text_length
<<= 1) + 1);
2132 lsx_fail("Error reading comment file `%s'", filename
);
2137 sox_append_comment(comments
, text
);
2145 static char const * const getoptstr
=
2146 "+b:c:de:hmnpqr:t:v:xBC:DGLMNRSTV::X";
2148 static struct lsx_option_t
const long_options
[] = {
2149 {"add-comment" , lsx_option_arg_required
, NULL
, 0},
2150 {"buffer" , lsx_option_arg_required
, NULL
, 0},
2151 {"combine" , lsx_option_arg_required
, NULL
, 0},
2152 {"comment-file" , lsx_option_arg_required
, NULL
, 0},
2153 {"comment" , lsx_option_arg_required
, NULL
, 0},
2154 {"endian" , lsx_option_arg_required
, NULL
, 0},
2155 {"input-buffer" , lsx_option_arg_required
, NULL
, 0},
2156 {"interactive" , lsx_option_arg_none
, NULL
, 0},
2157 {"help-effect" , lsx_option_arg_required
, NULL
, 0},
2158 {"help-format" , lsx_option_arg_required
, NULL
, 0},
2159 {"no-glob" , lsx_option_arg_none
, NULL
, 0},
2160 {"plot" , lsx_option_arg_required
, NULL
, 0},
2161 {"replay-gain" , lsx_option_arg_required
, NULL
, 0},
2162 {"version" , lsx_option_arg_none
, NULL
, 0},
2163 {"output" , lsx_option_arg_required
, NULL
, 0},
2164 {"effects-file" , lsx_option_arg_required
, NULL
, 0},
2165 {"temp" , lsx_option_arg_required
, NULL
, 0},
2166 {"single-threaded" , lsx_option_arg_none
, NULL
, 0},
2167 {"ignore-length" , lsx_option_arg_none
, NULL
, 0},
2168 {"norm" , lsx_option_arg_optional
, NULL
, 0},
2169 {"magic" , lsx_option_arg_none
, NULL
, 0},
2170 {"play-rate-arg" , lsx_option_arg_required
, NULL
, 0},
2171 {"clobber" , lsx_option_arg_none
, NULL
, 0},
2172 {"no-clobber" , lsx_option_arg_none
, NULL
, 0},
2173 {"multi-threaded" , lsx_option_arg_none
, NULL
, 0},
2174 {"dft-min" , lsx_option_arg_required
, NULL
, 0},
2176 {"bits" , lsx_option_arg_required
, NULL
, 'b'},
2177 {"channels" , lsx_option_arg_required
, NULL
, 'c'},
2178 {"compression" , lsx_option_arg_required
, NULL
, 'C'},
2179 {"default-device" , lsx_option_arg_none
, NULL
, 'd'},
2180 {"no-dither" , lsx_option_arg_none
, NULL
, 'D'},
2181 {"encoding" , lsx_option_arg_required
, NULL
, 'e'},
2182 {"help" , lsx_option_arg_none
, NULL
, 'h'},
2183 {"null" , lsx_option_arg_none
, NULL
, 'n'},
2184 {"no-show-progress", lsx_option_arg_none
, NULL
, 'q'},
2185 {"pipe" , lsx_option_arg_none
, NULL
, 'p'},
2186 {"rate" , lsx_option_arg_required
, NULL
, 'r'},
2187 {"reverse-bits" , lsx_option_arg_none
, NULL
, 'X'},
2188 {"reverse-nibbles" , lsx_option_arg_none
, NULL
, 'N'},
2189 {"show-progress" , lsx_option_arg_none
, NULL
, 'S'},
2190 {"type" , lsx_option_arg_required
, NULL
, 't'},
2191 {"volume" , lsx_option_arg_required
, NULL
, 'v'},
2192 {"guard" , lsx_option_arg_none
, NULL
, 'G'},
2197 static int opt_index(int val
)
2200 for (i
= 0; long_options
[i
].name
; ++i
)
2201 if (long_options
[i
].val
== val
)
2206 static lsx_enum_item
const combine_methods
[] = {
2207 LSX_ENUM_ITEM(sox_
,sequence
)
2208 LSX_ENUM_ITEM(sox_
,concatenate
)
2209 LSX_ENUM_ITEM(sox_
,mix
)
2210 {"mix-power", sox_mix_power
},
2211 LSX_ENUM_ITEM(sox_
,merge
)
2212 LSX_ENUM_ITEM(sox_
,multiply
)
2215 enum {ENDIAN_little
, ENDIAN_big
, ENDIAN_swap
};
2216 static lsx_enum_item
const endian_options
[] = {
2217 LSX_ENUM_ITEM(ENDIAN_
,little
)
2218 LSX_ENUM_ITEM(ENDIAN_
,big
)
2219 LSX_ENUM_ITEM(ENDIAN_
,swap
)
2222 static lsx_enum_item
const plot_methods
[] = {
2223 LSX_ENUM_ITEM(sox_plot_
,off
)
2224 LSX_ENUM_ITEM(sox_plot_
,octave
)
2225 LSX_ENUM_ITEM(sox_plot_
,gnuplot
)
2226 LSX_ENUM_ITEM(sox_plot_
,data
)
2230 encoding_signed_integer
, encoding_unsigned_integer
, encoding_floating_point
,
2231 encoding_ms_adpcm
, encoding_ima_adpcm
, encoding_oki_adpcm
,
2232 encoding_gsm_full_rate
, encoding_u_law
, encoding_a_law
};
2234 static lsx_enum_item
const encodings
[] = {
2235 {"signed-integer", encoding_signed_integer
},
2236 {"unsigned-integer", encoding_unsigned_integer
},
2237 {"floating-point", encoding_floating_point
},
2238 {"ms-adpcm", encoding_ms_adpcm
},
2239 {"ima-adpcm", encoding_ima_adpcm
},
2240 {"oki-adpcm", encoding_oki_adpcm
},
2241 {"gsm-full-rate", encoding_gsm_full_rate
},
2242 {"u-law", encoding_u_law
},
2243 {"mu-law", encoding_u_law
},
2244 {"a-law", encoding_a_law
},
2247 static int enum_option(char const * arg
, int option_index
, lsx_enum_item
const * items
)
2249 lsx_enum_item
const * p
= lsx_find_enum_text(arg
, items
, 0);
2252 char * set
= lsx_malloc(len
);
2254 for (p
= items
; p
->text
; ++p
) {
2255 set
= lsx_realloc(set
, len
+= 2 + strlen(p
->text
));
2256 strcat(set
, ", "); strcat(set
, p
->text
);
2258 lsx_fail("--%s: `%s' is not one of: %s.",
2259 long_options
[option_index
].name
, arg
, set
+ 2);
2266 static char parse_gopts_and_fopts(file_t
* f
)
2268 const sox_version_info_t
* info
= sox_version_info();
2271 int i
; /* sscanf silently accepts negative numbers for %u :( */
2272 char dummy
; /* To check for extraneous chars in optarg. */
2274 switch (c
=lsx_getopt(&optstate
)) {
2275 case -1: /* @ one of: file-name, effect name, end of arg-list. */
2276 return '\0'; /* i.e. not device. */
2278 case 0: /* Long options with no short equivalent. */
2279 switch (optstate
.lngind
) {
2282 sox_append_comment(&f
->oob
.comments
, optstate
.arg
);
2286 #define SOX_BUFMIN 16
2287 if (sscanf(optstate
.arg
, "%i %c", &i
, &dummy
) != 1 || i
<= SOX_BUFMIN
) {
2288 lsx_fail("Buffer size `%s' must be > %d", optstate
.arg
, SOX_BUFMIN
);
2291 sox_globals
.bufsiz
= i
;
2295 combine_method
= enum_option(optstate
.arg
, optstate
.lngind
, combine_methods
);
2299 sox_append_comment(&f
->oob
.comments
, "");
2300 read_comment_file(&f
->oob
.comments
, optstate
.arg
);
2304 sox_append_comment(&f
->oob
.comments
, "");
2306 sox_append_comment(&f
->oob
.comments
, optstate
.arg
);
2310 if (f
->encoding
.reverse_bytes
!= sox_option_default
|| f
->encoding
.opposite_endian
)
2311 usage("only one endian option per file is allowed");
2312 switch (enum_option(optstate
.arg
, optstate
.lngind
, endian_options
)) {
2313 case ENDIAN_little
: f
->encoding
.reverse_bytes
= MACHINE_IS_BIGENDIAN
; break;
2314 case ENDIAN_big
: f
->encoding
.reverse_bytes
= MACHINE_IS_LITTLEENDIAN
; break;
2315 case ENDIAN_swap
: f
->encoding
.opposite_endian
= sox_true
; break;
2320 if (sscanf(optstate
.arg
, "%i %c", &i
, &dummy
) != 1 || i
<= SOX_BUFMIN
) {
2321 lsx_fail("Buffer size `%s' must be > %d", optstate
.arg
, SOX_BUFMIN
);
2324 sox_globals
.input_bufsiz
= i
;
2328 #if defined(HAVE_TERMIOS_H) || defined(HAVE_CONIO_H)
2329 interactive
= sox_true
; break;
2331 lsx_fail("Interactive mode has not been enabled at compile time.");
2334 case 8: usage_effect(optstate
.arg
); break;
2335 case 9: usage_format(optstate
.arg
); break;
2336 case 10: f
->no_glob
= sox_true
; break;
2338 sox_effects_globals
.plot
= enum_option(optstate
.arg
, optstate
.lngind
, plot_methods
);
2340 case 12: replay_gain_mode
= enum_option(optstate
.arg
, optstate
.lngind
, rg_modes
); break;
2341 case 13: display_SoX_version(stdout
); exit(0); break;
2343 case 15: effects_filename
= lsx_strdup(optstate
.arg
); break;
2344 case 16: sox_globals
.tmp_path
= lsx_strdup(optstate
.arg
); break;
2345 case 17: sox_globals
.use_threads
= sox_false
; break;
2346 case 18: f
->signal
.length
= SOX_IGNORE_LENGTH
; break;
2347 case 19: do_guarded_norm
= is_guarded
= sox_true
;
2348 norm_level
= lsx_strdup(optstate
.arg
);
2351 if (info
->flags
& sox_version_have_magic
)
2352 sox_globals
.use_magic
= sox_true
;
2354 lsx_warn("this build of SoX does not include `magic'");
2356 case 21: play_rate_arg
= lsx_strdup(optstate
.arg
); break;
2357 case 22: no_clobber
= sox_false
; break;
2358 case 23: no_clobber
= sox_true
; break;
2359 case 24: sox_globals
.use_threads
= sox_true
; break;
2361 if (sscanf(optstate
.arg
, "%i %c", &i
, &dummy
) != 1 || i
< 8 || i
> 16) {
2362 lsx_fail("Min DFT size must be in range 8 to 16");
2365 sox_globals
.log2_dft_min_size
= i
;
2370 case 'G': is_guarded
= sox_true
; break;
2371 case 'm': combine_method
= sox_mix
; break;
2372 case 'M': combine_method
= sox_merge
; break;
2373 case 'T': combine_method
= sox_multiply
; break;
2375 case 'R': /* Useful for regression testing. */
2376 sox_globals
.repeatable
= sox_true
;
2379 case 'd': case 'n': case 'p':
2380 optstate
.ind
= optstate
.ind
;
2388 usage("invalid option"); /* No return */
2392 f
->filetype
= optstate
.arg
;
2393 if (f
->filetype
[0] == '.')
2399 size_t n
= sscanf(optstate
.arg
, "%lf %c %c", &f
->signal
.rate
, &k
, &dummy
);
2400 if (n
< 1 || f
->signal
.rate
<= 0 || (n
> 1 && k
!= 'k') || n
> 2) {
2401 lsx_fail("Rate value `%s' is not a positive number", optstate
.arg
);
2404 f
->signal
.rate
*= k
== 'k'? 1000. : 1.;
2409 if (sscanf(optstate
.arg
, "%lf %c", &f
->volume
, &dummy
) != 1) {
2410 lsx_fail("Volume value `%s' is not a number", optstate
.arg
);
2413 uservolume
= sox_true
;
2414 if (f
->volume
< 0.0)
2415 lsx_report("Volume adjustment is negative; "
2416 "this will result in a phase change");
2420 if (sscanf(optstate
.arg
, "%d %c", &i
, &dummy
) != 1 || i
<= 0) {
2421 lsx_fail("Channels value `%s' is not a positive integer", optstate
.arg
);
2424 f
->signal
.channels
= i
;
2428 if (sscanf(optstate
.arg
, "%lf %c", &f
->encoding
.compression
, &dummy
) != 1) {
2429 lsx_fail("Compression value `%s' is not a number", optstate
.arg
);
2435 if (sscanf(optstate
.arg
, "%d %c", &i
, &dummy
) != 1 || i
<= 0) {
2436 lsx_fail("Bits value `%s' is not a positive integer", optstate
.arg
);
2439 f
->encoding
.bits_per_sample
= i
;
2442 case 'e': switch (enum_option(optstate
.arg
, opt_index('e'), encodings
)) {
2443 case encoding_signed_integer
: f
->encoding
.encoding
= SOX_ENCODING_SIGN2
; break;
2444 case encoding_unsigned_integer
: f
->encoding
.encoding
= SOX_ENCODING_UNSIGNED
; break;
2445 case encoding_floating_point
: f
->encoding
.encoding
= SOX_ENCODING_FLOAT
; break;
2446 case encoding_ms_adpcm
: f
->encoding
.encoding
= SOX_ENCODING_MS_ADPCM
; break;
2447 case encoding_ima_adpcm
: f
->encoding
.encoding
= SOX_ENCODING_IMA_ADPCM
; break;
2448 case encoding_oki_adpcm
: f
->encoding
.encoding
= SOX_ENCODING_OKI_ADPCM
; break;
2449 case encoding_gsm_full_rate
: f
->encoding
.encoding
= SOX_ENCODING_GSM
; break;
2450 case encoding_u_law
: f
->encoding
.encoding
= SOX_ENCODING_ULAW
;
2451 if (f
->encoding
.bits_per_sample
== 0)
2452 f
->encoding
.bits_per_sample
= 8;
2454 case encoding_a_law
: f
->encoding
.encoding
= SOX_ENCODING_ALAW
;
2455 if (f
->encoding
.bits_per_sample
== 0)
2456 f
->encoding
.bits_per_sample
= 8;
2461 case 'L': case 'B': case 'x':
2462 if (f
->encoding
.reverse_bytes
!= sox_option_default
|| f
->encoding
.opposite_endian
)
2463 usage("only one endian option per file is allowed");
2465 case 'L': f
->encoding
.reverse_bytes
= MACHINE_IS_BIGENDIAN
; break;
2466 case 'B': f
->encoding
.reverse_bytes
= MACHINE_IS_LITTLEENDIAN
; break;
2467 case 'x': f
->encoding
.opposite_endian
= sox_true
; break;
2470 case 'X': f
->encoding
.reverse_bits
= sox_option_yes
; break;
2471 case 'N': f
->encoding
.reverse_nibbles
= sox_option_yes
; break;
2473 case 'S': show_progress
= sox_option_yes
; break;
2474 case 'q': show_progress
= sox_option_no
; break;
2475 case 'D': no_dither
= sox_true
; break;
2478 if (optstate
.arg
== NULL
)
2479 ++sox_globals
.verbosity
;
2481 if (sscanf(optstate
.arg
, "%d %c", &i
, &dummy
) != 1 || i
< 0) {
2482 sox_globals
.verbosity
= 2;
2483 lsx_fail("Verbosity value `%s' is not a non-negative integer", optstate
.arg
);
2486 sox_globals
.verbosity
= (unsigned)i
;
2493 static char const * device_name(char const * const type
)
2495 char * name
= NULL
, * from_env
= getenv("AUDIODEV");
2501 || !strcmp(type
, "sunau")
2502 || !strcmp(type
, "oss" )
2503 || !strcmp(type
, "ossdsp")
2504 || !strcmp(type
, "alsa")
2505 || !strcmp(type
, "ao")
2506 || !strcmp(type
, "sndio")
2507 || !strcmp(type
, "coreaudio")
2508 || !strcmp(type
, "pulseaudio")
2509 || !strcmp(type
, "waveaudio")
2513 return name
? from_env
? from_env
: name
: NULL
;
2516 static char const * try_device(char const * name
)
2518 sox_format_handler_t
const * handler
= sox_find_format(name
, sox_false
);
2520 sox_format_t format
, * ft
= &format
;
2521 lsx_debug("Looking for a default device: trying format `%s'", name
);
2522 memset(ft
, 0, sizeof(*ft
));
2523 ft
->filename
= (char *)device_name(name
);
2524 ft
->priv
= lsx_calloc(1, handler
->priv_size
);
2525 if (handler
->startwrite(ft
) == SOX_SUCCESS
) {
2526 handler
->stopwrite(ft
);
2535 static char const * set_default_device(file_t
* f
)
2537 /* Default audio driver type in order of preference: */
2538 if (!f
->filetype
) f
->filetype
= getenv("AUDIODRIVER");
2539 if (!f
->filetype
) f
->filetype
= try_device("coreaudio");
2540 if (!f
->filetype
) f
->filetype
= try_device("pulseaudio");
2541 if (!f
->filetype
) f
->filetype
= try_device("alsa");
2542 if (!f
->filetype
) f
->filetype
= try_device("waveaudio");
2543 if (!f
->filetype
) f
->filetype
= try_device("sndio");
2544 if (!f
->filetype
) f
->filetype
= try_device("oss");
2545 if (!f
->filetype
) f
->filetype
= try_device("sunau");
2546 if (!f
->filetype
&& file_count
) /*!rec*/
2547 f
->filetype
= try_device("ao");
2550 lsx_fail("Sorry, there is no default audio device configured");
2553 return device_name(f
->filetype
);
2556 static int add_file(file_t
const * const opts
, char const * const filename
)
2558 file_t
* f
= lsx_malloc(sizeof(*f
));
2562 usage("missing filename"); /* No return */
2563 f
->filename
= lsx_strdup(filename
);
2564 files
= lsx_realloc(files
, (file_count
+ 1) * sizeof(*files
));
2565 files
[file_count
++] = f
;
2571 #define GLOB_BRACE 0
2574 #define GLOB_TILDE 0
2576 static int add_glob_file(file_t
const * const opts
, char const * const filename
)
2582 return add_file(opts
, filename
);
2584 if (glob(filename
, GLOB_BRACE
| GLOB_TILDE
| GLOB_NOCHECK
, NULL
, &globbuf
)) {
2585 lsx_fail("glob: %s", strerror(errno
));
2588 for (i
= 0; i
< globbuf
.gl_pathc
; ++i
)
2589 add_file(opts
, globbuf
.gl_pathv
[i
]);
2594 #define add_glob_file add_file
2597 static void init_file(file_t
* f
)
2599 memset(f
, 0, sizeof(*f
));
2600 sox_init_encodinginfo(&f
->encoding
);
2601 f
->volume
= HUGE_VAL
;
2602 f
->replay_gain
= HUGE_VAL
;
2605 static void parse_options_and_filenames(int argc
, char **argv
)
2607 char const * env_opts
= getenv(SOX_OPTS
);
2608 file_t opts
, opts_none
;
2609 init_file(&opts
), init_file(&opts_none
);
2611 if (sox_mode
== sox_rec
)
2612 add_file(&opts
, set_default_device(&opts
)), init_file(&opts
);
2614 if (env_opts
&& *env_opts
) {
2615 char * * argv2
, * str
= lsx_malloc(strlen(argv
[0]) + strlen(env_opts
) + 2);
2617 strcpy(str
, argv
[0]);
2619 strcat(str
, env_opts
);
2620 argv2
= strtoargv(str
, &argc2
);
2621 lsx_getopt_init(argc2
, argv2
, getoptstr
, long_options
, lsx_getopt_flag_opterr
, 1, &optstate
);
2622 if (parse_gopts_and_fopts(&opts
)) {
2623 lsx_fail("invalid option for "SOX_OPTS
);
2630 lsx_getopt_init(argc
, argv
, getoptstr
, long_options
, lsx_getopt_flag_opterr
, 1, &optstate
);
2631 for (; optstate
.ind
< argc
&& !sox_find_effect(argv
[optstate
.ind
]); init_file(&opts
)) {
2632 char c
= parse_gopts_and_fopts(&opts
);
2633 if (c
== 'n') { /* is null file? */
2634 if (opts
.filetype
!= NULL
&& strcmp(opts
.filetype
, "null") != 0)
2635 lsx_warn("ignoring `-t %s'.", opts
.filetype
);
2636 opts
.filetype
= "null";
2637 add_file(&opts
, "");
2639 else if (c
== 'd') /* is default device? */
2640 add_file(&opts
, set_default_device(&opts
));
2641 else if (c
== 'p') { /* is sox pipe? */
2642 if (opts
.filetype
!= NULL
&& strcmp(opts
.filetype
, "sox") != 0)
2643 lsx_warn("ignoring `-t %s'.", opts
.filetype
);
2644 opts
.filetype
= "sox";
2645 add_file(&opts
, "-");
2647 else if (optstate
.ind
>= argc
|| sox_find_effect(argv
[optstate
.ind
]))
2649 else if (!sox_is_playlist(argv
[optstate
.ind
]))
2650 add_glob_file(&opts
, argv
[optstate
.ind
++]);
2651 else if (sox_parse_playlist((sox_playlist_callback_t
)add_file
, &opts
, argv
[optstate
.ind
++]) != SOX_SUCCESS
)
2654 if (env_opts
&& *env_opts
) {
2655 lsx_report("using "SOX_OPTS
"=%s", env_opts
);
2656 reported_sox_opts
= sox_true
;
2658 if (sox_mode
== sox_play
)
2659 add_file(&opts
, set_default_device(&opts
));
2660 else if (memcmp(&opts
, &opts_none
, sizeof(opts
))) /* fopts but no file */
2661 add_file(&opts
, device_name(opts
.filetype
));
2664 static double soxi_total
;
2665 static size_t soxi_file_count
;
2667 typedef enum {Full
, Type
, Rate
, Channels
, Samples
, Duration
, Duration_secs
,
2668 Bits
, Bitrate
, Precision
, Encoding
, Annotation
} soxi_t
;
2670 static int soxi1(soxi_t
const * type
, char const * filename
)
2672 sox_format_t
* ft
= sox_open_read(filename
, NULL
, NULL
, NULL
);
2675 char const * text
= NULL
;
2679 ws
= ft
->signal
.length
/ max(ft
->signal
.channels
, 1);
2680 secs
= (double)ws
/ max(ft
->signal
.rate
, 1);
2682 if (soxi_total
>= 0 && !ws
)
2684 if (soxi_total
>= 0) soxi_total
+= *type
== Samples
? ws
: secs
;
2687 case Type
: printf("%s\n", ft
->filetype
); break;
2688 case Rate
: printf("%g\n", ft
->signal
.rate
); break;
2689 case Channels
: printf("%u\n", ft
->signal
.channels
); break;
2690 case Samples
: if (soxi_total
==-1) printf("%" PRIu64
"\n", ws
); break;
2691 case Duration
: if (soxi_total
==-1) printf("%s\n", str_time(secs
)); break;
2692 case Duration_secs
: if (soxi_total
==-1) printf("%f\n", secs
); break;
2693 case Bits
: printf("%u\n", ft
->encoding
.bits_per_sample
); break;
2694 case Bitrate
: size_and_bitrate(ft
, &text
); puts(text
? text
: "0"); break;
2695 case Precision
: printf("%u\n", ft
->signal
.precision
); break;
2696 case Encoding
: printf("%s\n", sox_encodings_info
[ft
->encoding
.encoding
].desc
); break;
2697 case Annotation
: if (ft
->oob
.comments
) {
2698 sox_comments_t p
= ft
->oob
.comments
;
2699 do printf("%s\n", *p
); while (*++p
);
2702 case Full
: display_file_info(ft
, NULL
, sox_false
); break;
2704 return !!sox_close(ft
);
2707 static void soxi_usage(int return_code
)
2709 display_SoX_version(stdout
);
2712 "Usage: soxi [-V[level]] [-T] [-t|-r|-c|-s|-d|-D|-b|-B|-p|-e|-a] infile1 ...\n"
2714 "-V[n]\tIncrement or set verbosity level (default is 2)\n"
2715 "-T\tWith -s, -d or -D, display the total across all given files\n"
2717 "-t\tShow detected file-type\n"
2718 "-r\tShow sample-rate\n"
2719 "-c\tShow number of channels\n"
2721 "-s\tShow number of samples (0 if unavailable)\n"
2722 "-d\tShow duration in hours, minutes and seconds (0 if unavailable)\n"
2723 "-D\tShow duration in seconds (0 if unavailable)\n"
2724 "-b\tShow number of bits per sample (0 if not applicable)\n"
2725 "-B\tShow the bitrate averaged over the whole file (0 if unavailable)\n"
2726 "-p\tShow estimated sample precision in bits\n"
2727 "-e\tShow the name of the audio encoding\n"
2728 "-a\tShow file comments (annotations) if available\n"
2730 "With no options, as much information as is available is shown for\n"
2731 "each given file.\n"
2736 static int soxi(int argc
, char * const * argv
)
2738 static char const opts
[] = "trcsdDbBpea?TV::";
2740 int opt
, num_errors
= 0;
2741 sox_bool do_total
= sox_false
;
2745 lsx_getopt_init(argc
, argv
, opts
, NULL
, lsx_getopt_flag_opterr
, 1, &optstate
);
2746 while ((opt
= lsx_getopt(&optstate
)) > 0) /* act only on last option */
2748 int i
; /* sscanf silently accepts negative numbers for %u :( */
2749 char dummy
; /* To check for extraneous chars in optstate.arg. */
2750 if (optstate
.arg
== NULL
)
2751 ++sox_globals
.verbosity
;
2753 if (sscanf(optstate
.arg
, "%d %c", &i
, &dummy
) != 1 || i
< 0) {
2754 sox_globals
.verbosity
= 2;
2755 lsx_fail("Verbosity value `%s' is not a non-negative integer", optstate
.arg
);
2758 sox_globals
.verbosity
= (unsigned)i
;
2761 else if (opt
== 'T')
2762 do_total
= sox_true
;
2763 else if ((type
= 1 + (strchr(opts
, opt
) - opts
)) > Annotation
)
2767 do_total
= sox_true
;
2768 else if (do_total
&& (type
< Samples
|| type
> Duration_secs
)) {
2769 fprintf(stderr
, "soxi: ignoring -T; n/a with other given option");
2770 do_total
= sox_false
;
2772 soxi_total
= -!do_total
;
2773 for (; optstate
.ind
< argc
; ++optstate
.ind
) {
2774 if (sox_is_playlist(argv
[optstate
.ind
]))
2775 num_errors
+= (sox_parse_playlist((sox_playlist_callback_t
)soxi1
, &type
, argv
[optstate
.ind
]) != SOX_SUCCESS
);
2776 else num_errors
+= soxi1(&type
, argv
[optstate
.ind
]);
2779 if (soxi_file_count
> 1 && soxi_total
> 0)
2780 printf("Total Duration of %u files: %s\n", (unsigned)soxi_file_count
, str_time(soxi_total
));
2782 else if (do_total
) {
2785 else if (type
== Duration
)
2786 printf("%s\n", str_time(soxi_total
));
2787 else printf("%f\n", soxi_total
);
2792 static void set_replay_gain(sox_comments_t comments
, file_t
* f
)
2794 rg_mode rg
= replay_gain_mode
;
2795 int try = 2; /* Will try to find the other GAIN if preferred one not found */
2796 size_t i
, n
= sox_num_comments(comments
);
2798 if (rg
!= RG_off
) while (try--) {
2799 char const * target
=
2800 rg
== RG_track
? "REPLAYGAIN_TRACK_GAIN=" : "REPLAYGAIN_ALBUM_GAIN=";
2801 for (i
= 0; i
< n
; ++i
) {
2802 if (strncasecmp(comments
[i
], target
, strlen(target
)) == 0) {
2803 f
->replay_gain
= atof(comments
[i
] + strlen(target
));
2804 f
->replay_gain_mode
= rg
;
2808 rg
^= RG_track
^ RG_album
;
2812 static void output_message(unsigned level
, const char *filename
, const char *fmt
, va_list ap
)
2814 char const * const str
[] = {"FAIL", "WARN", "INFO", "DBUG"};
2815 if (sox_globals
.verbosity
>= level
) {
2816 char base_name
[128];
2817 sox_basename(base_name
, sizeof(base_name
), filename
);
2818 fprintf(stderr
, "%s %s %s: ", myname
, str
[min(level
- 1, 3)], base_name
);
2819 vfprintf(stderr
, fmt
, ap
);
2820 fprintf(stderr
, "\n");
2824 static sox_bool
cmp_comment_text(char const * c1
, char const * c2
)
2826 return c1
&& c2
&& !strcasecmp(c1
, c2
);
2829 int main(int argc
, char **argv
)
2835 gettimeofday(&load_timeofday
, NULL
);
2837 sox_globals
.output_message_handler
= output_message
;
2839 if (0 != sox_basename(mybase
, sizeof(mybase
), myname
))
2841 if (0 == lsx_strcasecmp(mybase
, "play"))
2842 sox_mode
= sox_play
;
2843 else if (0 == lsx_strcasecmp(mybase
, "rec"))
2845 else if (0 == lsx_strcasecmp(mybase
, "soxi"))
2846 sox_mode
= sox_soxi
;
2849 if (!sox_mode
&& argc
> 1 &&
2850 (!strcmp(argv
[1], "--i") || !strcmp(argv
[1], "--info")))
2851 --argc
, ++argv
, sox_mode
= sox_soxi
;
2853 if (sox_init() != SOX_SUCCESS
)
2856 stdin_is_a_tty
= isatty(fileno(stdin
));
2857 errno
= 0; /* Both isatty & fileno may set errno. */
2859 atexit(atexit_cleanup
);
2861 if (sox_mode
== sox_soxi
)
2862 exit(soxi(argc
, argv
));
2864 parse_options_and_filenames(argc
, argv
);
2866 if (sox_globals
.verbosity
> 2)
2867 display_SoX_version(stderr
);
2869 input_count
= file_count
? file_count
- 1 : 0;
2872 sox_format_handler_t
const * handler
=
2873 sox_write_handler(ofile
->filename
, ofile
->filetype
, NULL
);
2874 is_player
= handler
&&
2875 (handler
->flags
& SOX_FILE_DEVICE
) && !(handler
->flags
& SOX_FILE_PHONY
);
2878 if (combine_method
== sox_default
)
2879 combine_method
= is_player
? sox_sequence
: sox_concatenate
;
2881 /* Allow e.g. known length processing in this case */
2882 if (combine_method
== sox_sequence
&& input_count
== 1)
2883 combine_method
= sox_concatenate
;
2885 /* Make sure we got at least the required # of input filenames */
2886 if (input_count
< 1)
2887 usage("No input filenames specified");
2889 /* Check for misplaced input/output-specific options */
2890 for (i
= 0; i
< input_count
; ++i
) {
2891 if (files
[i
]->encoding
.compression
!= HUGE_VAL
)
2892 usage("A compression factor can be given only for an output file");
2893 if (files
[i
]->oob
.comments
!= NULL
)
2894 usage("Comments can be given only for an output file");
2896 if (ofile
->volume
!= HUGE_VAL
)
2897 usage("-v can be given only for an input file;\n"
2898 "\tuse the `gain' or `vol' effect to set the output file volume");
2899 if (ofile
->signal
.length
!= SOX_UNSPEC
)
2900 usage("--ignore-length can be given only for an input file");
2902 setsig(SIGINT
, SIG_IGN
); /* So child pipes aren't killed by track skip */
2903 for (i
= 0; i
< input_count
; i
++) {
2904 size_t j
= input_count
- 1 - i
; /* Open in reverse order 'cos of rec (below) */
2905 file_t
* f
= files
[j
];
2907 /* When mixing audio, default to input side volume adjustments that will
2908 * make sure no clipping will occur. Users probably won't be happy with
2909 * this, and will override it, possibly causing clipping to occur. */
2910 if (combine_method
== sox_mix
&& !uservolume
)
2911 f
->volume
= 1.0 / input_count
;
2912 else if (combine_method
== sox_mix_power
&& !uservolume
)
2913 f
->volume
= 1.0 / sqrt((double)input_count
);
2915 if (sox_mode
== sox_rec
&& !j
) { /* Set the recording parameters: */
2916 if (input_count
> 1) { /* from the (just openned) next */
2917 f
->signal
= files
[1]->ft
->signal
; /* input file, or from the output */
2918 f
->encoding
= files
[1]->ft
->encoding
;
2920 f
->signal
= files
[1]->signal
; /* file (which is not open yet). */
2921 f
->encoding
= files
[1]->encoding
;
2924 files
[j
]->ft
= sox_open_read(f
->filename
, &f
->signal
, &f
->encoding
, f
->filetype
);
2926 /* sox_open_read() will call lsx_warn for most errors.
2927 * Rely on that printing something. */
2929 if (show_progress
== sox_option_default
&&
2930 (files
[j
]->ft
->handler
.flags
& SOX_FILE_DEVICE
) != 0 &&
2931 (files
[j
]->ft
->handler
.flags
& SOX_FILE_PHONY
) == 0)
2932 show_progress
= sox_option_yes
;
2935 if (replay_gain_mode
== RG_default
)
2936 replay_gain_mode
= is_player
?
2937 input_count
> 1 && /* Simple heuristic to determine if */
2938 cmp_comment_text( /* replay-gain should be in album mode */
2939 sox_find_comment(files
[0]->ft
->oob
.comments
, "artist"),
2940 sox_find_comment(files
[1]->ft
->oob
.comments
, "artist")) &&
2942 sox_find_comment(files
[0]->ft
->oob
.comments
, "album"),
2943 sox_find_comment(files
[1]->ft
->oob
.comments
, "album"))?
2944 RG_album
: RG_track
: RG_off
;
2946 for (i
= 0; i
< input_count
; i
++)
2947 set_replay_gain(files
[i
]->ft
->oob
.comments
, files
[i
]);
2949 setsig(SIGINT
, SIG_DFL
);
2951 /* Loop through the rest of the arguments looking for effects */
2953 parse_effects(argc
, argv
);
2955 /* Note: Purposely not calling add_eff_chain() to save some
2956 * memory although it would be more consistent to do so.
2959 /* Not the best way for users to do this; now deprecated in favour of soxi. */
2960 if (!show_progress
&& !nuser_effects
[current_eff_chain
] &&
2961 ofile
->filetype
&& !strcmp(ofile
->filetype
, "null")) {
2962 for (i
= 0; i
< input_count
; i
++)
2963 report_file_info(files
[i
]);
2967 if (!sox_globals
.repeatable
) {/* Re-seed PRNG? */
2969 gettimeofday(&now
, NULL
);
2970 sox_globals
.ranqd1
= (int32_t)(now
.tv_sec
- now
.tv_usec
);
2973 /* Save things that sox_sequence needs to be reinitialised for each segued
2974 * block of input files.*/
2975 ofile_signal_options
= ofile
->signal
;
2976 ofile_encoding_options
= ofile
->encoding
;
2978 /* If user specified an effects filename then use that file
2979 * to load user effects. Free any previously specified options
2980 * from the command line.
2982 if (effects_filename
)
2984 read_user_effects(effects_filename
);
2990 if (err
== SOX_EOF
|| user_abort
|| current_input
>= input_count
)
2993 if (advance_eff_chain() == SOX_EOF
)
2996 if (!save_output_eff
)
2998 sox_close(ofile
->ft
);
3003 sox_delete_effects_chain(effects_chain
);
3004 delete_eff_chains();
3006 for (i
= 0; i
< file_count
; ++i
)
3007 if (files
[i
]->ft
->clips
!= 0)
3008 lsx_warn(i
< input_count
?"`%s' input clipped %" PRIu64
" samples" :
3009 "`%s' output clipped %" PRIu64
" samples; decrease volume?",
3010 (files
[i
]->ft
->handler
.flags
& SOX_FILE_DEVICE
)?
3011 files
[i
]->ft
->handler
.names
[0] : files
[i
]->ft
->filename
,
3012 files
[i
]->ft
->clips
);
3014 if (mixing_clips
> 0)
3015 lsx_warn("mix-combining clipped %" PRIu64
" samples; decrease volume?", mixing_clips
);
3017 for (i
= 0; i
< file_count
; i
++)
3018 if (files
[i
]->volume_clips
> 0)
3019 lsx_warn("`%s' balancing clipped %" PRIu64
" samples; decrease volume?",
3020 files
[i
]->filename
, files
[i
]->volume_clips
);
3022 if (show_progress
) {
3024 fprintf(stderr
, "Aborted.\n");
3025 else if (user_skip
&& sox_mode
!= sox_rec
)
3026 fprintf(stderr
, "Skipped.\n");
3028 fprintf(stderr
, "Done.\n");
3031 success
= 1; /* Signal success to cleanup so the output file isn't removed. */