stats: improve bit depth reporting [bug #267]
[sox.git] / src / formats.c
blob6321a8ef585df1dd7e3929fe07a9a76e6d2db311
1 /* Implements the public API for using libSoX file formats.
2 * All public functions & data are prefixed with sox_ .
4 * (c) 2005-8 Chris Bagwell and SoX contributors
6 * This library is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or (at
9 * your option) any later version.
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this library; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #define _GNU_SOURCE
22 #include "sox_i.h"
24 #include <assert.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
33 #ifdef HAVE_IO_H
34 #include <io.h>
35 #endif
37 #if HAVE_MAGIC
38 #include <magic.h>
39 #endif
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
45 #define PIPE_AUTO_DETECT_SIZE 256 /* Only as much as we can rewind a pipe */
46 #define AUTO_DETECT_SIZE 4096 /* For seekable file, so no restriction */
48 static char const * auto_detect_format(sox_format_t * ft, char const * ext)
50 char data[AUTO_DETECT_SIZE];
51 size_t len = lsx_readbuf(ft, data, ft->seekable? sizeof(data) : PIPE_AUTO_DETECT_SIZE);
52 #define CHECK(type, p2, l2, d2, p1, l1, d1) if (len >= p1 + l1 && \
53 !memcmp(data + p1, d1, (size_t)l1) && !memcmp(data + p2, d2, (size_t)l2)) return #type;
54 CHECK(voc , 0, 0, "" , 0, 20, "Creative Voice File\x1a")
55 CHECK(smp , 0, 0, "" , 0, 17, "SOUND SAMPLE DATA")
56 CHECK(wve , 0, 0, "" , 0, 15, "ALawSoundFile**")
57 CHECK(gsrt , 0, 0, "" , 16, 9, "ring.bin")
58 CHECK(amr-wb, 0, 0, "" , 0, 9, "#!AMR-WB\n")
59 CHECK(prc , 0, 0, "" , 0, 8, "\x37\x00\x00\x10\x6d\x00\x00\x10")
60 CHECK(sph , 0, 0, "" , 0, 7, "NIST_1A")
61 CHECK(amr-nb, 0, 0, "" , 0, 6, "#!AMR\n")
62 CHECK(txw , 0, 0, "" , 0, 6, "LM8953")
63 CHECK(sndt , 0, 0, "" , 0, 6, "SOUND\x1a")
64 CHECK(vorbis, 0, 4, "OggS" , 29, 6, "vorbis")
65 CHECK(opus , 0, 4, "OggS" , 28, 8, "OpusHead")
66 CHECK(speex , 0, 4, "OggS" , 28, 6, "Speex")
67 CHECK(hcom ,65, 4, "FSSD" , 128,4, "HCOM")
68 CHECK(wav , 0, 4, "RIFF" , 8, 4, "WAVE")
69 CHECK(wav , 0, 4, "RIFX" , 8, 4, "WAVE")
70 CHECK(wav , 0, 4, "RF64" , 8, 4, "WAVE")
71 CHECK(aiff , 0, 4, "FORM" , 8, 4, "AIFF")
72 CHECK(aifc , 0, 4, "FORM" , 8, 4, "AIFC")
73 CHECK(8svx , 0, 4, "FORM" , 8, 4, "8SVX")
74 CHECK(maud , 0, 4, "FORM" , 8, 4, "MAUD")
75 CHECK(xa , 0, 0, "" , 0, 4, "XA\0\0")
76 CHECK(xa , 0, 0, "" , 0, 4, "XAI\0")
77 CHECK(xa , 0, 0, "" , 0, 4, "XAJ\0")
78 CHECK(au , 0, 0, "" , 0, 4, ".snd")
79 CHECK(au , 0, 0, "" , 0, 4, "dns.")
80 CHECK(au , 0, 0, "" , 0, 4, "\0ds.")
81 CHECK(au , 0, 0, "" , 0, 4, ".sd\0")
82 CHECK(flac , 0, 0, "" , 0, 4, "fLaC")
83 CHECK(avr , 0, 0, "" , 0, 4, "2BIT")
84 CHECK(caf , 0, 0, "" , 0, 4, "caff")
85 CHECK(wv , 0, 0, "" , 0, 4, "wvpk")
86 CHECK(paf , 0, 0, "" , 0, 4, " paf")
87 CHECK(sf , 0, 0, "" , 0, 4, "\144\243\001\0")
88 CHECK(sf , 0, 0, "" , 0, 4, "\0\001\243\144")
89 CHECK(sf , 0, 0, "" , 0, 4, "\144\243\002\0")
90 CHECK(sf , 0, 0, "" , 0, 4, "\0\002\243\144")
91 CHECK(sf , 0, 0, "" , 0, 4, "\144\243\003\0")
92 CHECK(sf , 0, 0, "" , 0, 4, "\0\003\243\144")
93 CHECK(sf , 0, 0, "" , 0, 4, "\144\243\004\0")
94 CHECK(sox , 0, 0, "" , 0, 4, ".SoX")
95 CHECK(sox , 0, 0, "" , 0, 4, "XoS.")
97 if (ext && !strcasecmp(ext, "snd"))
98 CHECK(sndr , 7, 1, "" , 0, 2, "\0")
99 #undef CHECK
101 #if HAVE_MAGIC
102 if (sox_globals.use_magic) {
103 static magic_t magic;
104 char const * filetype = NULL;
105 if (!magic) {
106 magic = magic_open(MAGIC_MIME | MAGIC_SYMLINK);
107 if (magic)
108 magic_load(magic, NULL);
110 if (magic)
111 filetype = magic_buffer(magic, data, len);
112 if (filetype && strncmp(filetype, "application/octet-stream", (size_t)24) &&
113 !lsx_strends(filetype, "/unknown") &&
114 strncmp(filetype, "text/plain", (size_t)10) )
115 return filetype;
116 else if (filetype)
117 lsx_debug("libmagic detected %s", filetype);
119 #endif
120 return NULL;
123 static sox_encodings_info_t const s_sox_encodings_info[] = {
124 {sox_encodings_none , "n/a" , "Unknown or not applicable"},
125 {sox_encodings_none , "Signed PCM" , "Signed Integer PCM"},
126 {sox_encodings_none , "Unsigned PCM" , "Unsigned Integer PCM"},
127 {sox_encodings_none , "F.P. PCM" , "Floating Point PCM"},
128 {sox_encodings_none , "F.P. PCM" , "Floating Point (text) PCM"},
129 {sox_encodings_none , "FLAC" , "FLAC"},
130 {sox_encodings_none , "HCOM" , "HCOM"},
131 {sox_encodings_none , "WavPack" , "WavPack"},
132 {sox_encodings_none , "F.P. WavPack" , "Floating Point WavPack"},
133 {sox_encodings_lossy1, "u-law" , "u-law"},
134 {sox_encodings_lossy1, "A-law" , "A-law"},
135 {sox_encodings_lossy1, "G.721 ADPCM" , "G.721 ADPCM"},
136 {sox_encodings_lossy1, "G.723 ADPCM" , "G.723 ADPCM"},
137 {sox_encodings_lossy1, "CL ADPCM (8)" , "CL ADPCM (from 8-bit)"},
138 {sox_encodings_lossy1, "CL ADPCM (16)", "CL ADPCM (from 16-bit)"},
139 {sox_encodings_lossy1, "MS ADPCM" , "MS ADPCM"},
140 {sox_encodings_lossy1, "IMA ADPCM" , "IMA ADPCM"},
141 {sox_encodings_lossy1, "OKI ADPCM" , "OKI ADPCM"},
142 {sox_encodings_lossy1, "DPCM" , "DPCM"},
143 {sox_encodings_none , "DWVW" , "DWVW"},
144 {sox_encodings_none , "DWVWN" , "DWVWN"},
145 {sox_encodings_lossy2, "GSM" , "GSM"},
146 {sox_encodings_lossy2, "MPEG audio" , "MPEG audio (layer I, II or III)"},
147 {sox_encodings_lossy2, "Vorbis" , "Vorbis"},
148 {sox_encodings_lossy2, "AMR-WB" , "AMR-WB"},
149 {sox_encodings_lossy2, "AMR-NB" , "AMR-NB"},
150 {sox_encodings_lossy2, "CVSD" , "CVSD"},
151 {sox_encodings_lossy2, "LPC10" , "LPC10"},
152 {sox_encodings_lossy2, "Opus" , "Opus"},
155 assert_static(array_length(s_sox_encodings_info) == SOX_ENCODINGS,
156 SIZE_MISMATCH_BETWEEN_sox_encoding_t_AND_sox_encodings_info);
158 sox_encodings_info_t const *
159 sox_get_encodings_info(void)
161 return s_sox_encodings_info;
164 unsigned sox_precision(sox_encoding_t encoding, unsigned bits_per_sample)
166 switch (encoding) {
167 case SOX_ENCODING_DWVW: return bits_per_sample;
168 case SOX_ENCODING_DWVWN: return !bits_per_sample? 16: 0; /* ? */
169 case SOX_ENCODING_HCOM: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 1? bits_per_sample: 0;
170 case SOX_ENCODING_WAVPACK:
171 case SOX_ENCODING_FLAC: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 4? bits_per_sample: 0;
172 case SOX_ENCODING_SIGN2: return bits_per_sample <= 32? bits_per_sample : 0;
173 case SOX_ENCODING_UNSIGNED: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 4? bits_per_sample: 0;
175 case SOX_ENCODING_ALAW: return bits_per_sample == 8? 13: 0;
176 case SOX_ENCODING_ULAW: return bits_per_sample == 8? 14: 0;
178 case SOX_ENCODING_CL_ADPCM: return bits_per_sample? 8: 0;
179 case SOX_ENCODING_CL_ADPCM16: return bits_per_sample == 4? 13: 0;
180 case SOX_ENCODING_MS_ADPCM: return bits_per_sample == 4? 14: 0;
181 case SOX_ENCODING_IMA_ADPCM: return bits_per_sample == 4? 13: 0;
182 case SOX_ENCODING_OKI_ADPCM: return bits_per_sample == 4? 12: 0;
183 case SOX_ENCODING_G721: return bits_per_sample == 4? 12: 0;
184 case SOX_ENCODING_G723: return bits_per_sample == 3? 8:
185 bits_per_sample == 5? 14: 0;
186 case SOX_ENCODING_CVSD: return bits_per_sample == 1? 16: 0;
187 case SOX_ENCODING_DPCM: return bits_per_sample; /* ? */
189 case SOX_ENCODING_MP3: return 0; /* Accept the precision returned by the format. */
191 case SOX_ENCODING_GSM:
192 case SOX_ENCODING_VORBIS:
193 case SOX_ENCODING_OPUS:
194 case SOX_ENCODING_AMR_WB:
195 case SOX_ENCODING_AMR_NB:
196 case SOX_ENCODING_LPC10: return !bits_per_sample? 16: 0;
198 case SOX_ENCODING_WAVPACKF:
199 case SOX_ENCODING_FLOAT: return bits_per_sample == 32 ? 25: bits_per_sample == 64 ? 54: 0;
200 case SOX_ENCODING_FLOAT_TEXT: return !bits_per_sample? 54: 0;
202 case SOX_ENCODINGS:
203 case SOX_ENCODING_UNKNOWN: break;
205 return 0;
208 void sox_init_encodinginfo(sox_encodinginfo_t * e)
210 e->reverse_bytes = sox_option_default;
211 e->reverse_nibbles = sox_option_default;
212 e->reverse_bits = sox_option_default;
213 e->compression = HUGE_VAL;
216 /*--------------------------------- Comments ---------------------------------*/
218 size_t sox_num_comments(sox_comments_t comments)
220 size_t result = 0;
221 if (!comments)
222 return 0;
223 while (*comments++)
224 ++result;
225 return result;
228 void sox_append_comment(sox_comments_t * comments, char const * comment)
230 size_t n = sox_num_comments(*comments);
231 *comments = lsx_realloc(*comments, (n + 2) * sizeof(**comments));
232 assert(comment);
233 (*comments)[n++] = lsx_strdup(comment);
234 (*comments)[n] = 0;
237 void sox_append_comments(sox_comments_t * comments, char const * comment)
239 char * end;
240 if (comment) {
241 while ((end = strchr(comment, '\n'))) {
242 size_t len = end - comment;
243 char * c = lsx_malloc((len + 1) * sizeof(*c));
244 strncpy(c, comment, len);
245 c[len] = '\0';
246 sox_append_comment(comments, c);
247 comment += len + 1;
248 free(c);
250 if (*comment)
251 sox_append_comment(comments, comment);
255 sox_comments_t sox_copy_comments(sox_comments_t comments)
257 sox_comments_t result = 0;
259 if (comments) while (*comments)
260 sox_append_comment(&result, *comments++);
261 return result;
264 void sox_delete_comments(sox_comments_t * comments)
266 sox_comments_t p = *comments;
268 if (p) while (*p)
269 free(*p++);
270 free(*comments);
271 *comments = 0;
274 char * lsx_cat_comments(sox_comments_t comments)
276 sox_comments_t p = comments;
277 size_t len = 0;
278 char * result;
280 if (p) while (*p)
281 len += strlen(*p++) + 1;
283 result = lsx_calloc(len? len : 1, sizeof(*result));
285 if ((p = comments) && *p) {
286 strcpy(result, *p);
287 while (*++p)
288 strcat(strcat(result, "\n"), *p);
290 return result;
293 char const * sox_find_comment(sox_comments_t comments, char const * id)
295 size_t len = strlen(id);
297 if (comments) for (;*comments; ++comments)
298 if (!strncasecmp(*comments, id, len) && (*comments)[len] == '=')
299 return *comments + len + 1;
300 return NULL;
303 static void set_endiannesses(sox_format_t * ft)
305 if (ft->encoding.opposite_endian)
306 ft->encoding.reverse_bytes = (ft->handler.flags & SOX_FILE_ENDIAN)?
307 !(ft->handler.flags & SOX_FILE_ENDBIG) != MACHINE_IS_BIGENDIAN : sox_true;
308 else if (ft->encoding.reverse_bytes == sox_option_default)
309 ft->encoding.reverse_bytes = (ft->handler.flags & SOX_FILE_ENDIAN)?
310 !(ft->handler.flags & SOX_FILE_ENDBIG) == MACHINE_IS_BIGENDIAN : sox_false;
312 /* FIXME: Change reports to suitable warnings if trying
313 * to override something that can't be overridden. */
315 if (ft->handler.flags & SOX_FILE_ENDIAN) {
316 if (ft->encoding.reverse_bytes == (sox_option_t)
317 (!(ft->handler.flags & SOX_FILE_ENDBIG) != MACHINE_IS_BIGENDIAN))
318 lsx_report("`%s': overriding file-type byte-order", ft->filename);
319 } else if (ft->encoding.reverse_bytes == sox_option_yes)
320 lsx_report("`%s': overriding machine byte-order", ft->filename);
322 if (ft->encoding.reverse_bits == sox_option_default)
323 ft->encoding.reverse_bits = !!(ft->handler.flags & SOX_FILE_BIT_REV);
324 else if (ft->encoding.reverse_bits == !(ft->handler.flags & SOX_FILE_BIT_REV))
325 lsx_report("`%s': overriding file-type bit-order", ft->filename);
327 if (ft->encoding.reverse_nibbles == sox_option_default)
328 ft->encoding.reverse_nibbles = !!(ft->handler.flags & SOX_FILE_NIB_REV);
329 else
330 if (ft->encoding.reverse_nibbles == !(ft->handler.flags & SOX_FILE_NIB_REV))
331 lsx_report("`%s': overriding file-type nibble-order", ft->filename);
334 static sox_bool is_seekable(sox_format_t const * ft)
336 assert(ft);
337 if (!ft->fp)
338 return sox_false;
340 return !fseek(ft->fp, 0, SEEK_CUR);
343 /* check that all settings have been given */
344 static int sox_checkformat(sox_format_t * ft)
346 ft->sox_errno = SOX_SUCCESS;
348 if (ft->signal.rate <= 0) {
349 lsx_fail_errno(ft, SOX_EFMT, "sample rate zero or negative");
350 return SOX_EOF;
352 if (!ft->signal.precision) {
353 lsx_fail_errno(ft,SOX_EFMT,"data encoding or sample size was not specified");
354 return SOX_EOF;
356 return SOX_SUCCESS;
359 static sox_bool is_url(char const * text) /* detects only wget-supported URLs */
361 return !(
362 strncasecmp(text, "http:" , (size_t)5) &&
363 strncasecmp(text, "https:", (size_t)6) &&
364 strncasecmp(text, "ftp:" , (size_t)4));
367 static int xfclose(FILE * file, lsx_io_type io_type)
369 return
370 #ifdef HAVE_POPEN
371 io_type != lsx_io_file? pclose(file) :
372 #endif
373 fclose(file);
376 static void incr_pipe_size(FILE *f)
379 * Linux 2.6.35 and later has the ability to expand the pipe buffer
380 * Try to get it as big as possible to avoid stalls when SoX itself
381 * is using big buffers
383 #if defined(F_GETPIPE_SZ) && defined(F_SETPIPE_SZ)
384 static long max_pipe_size;
386 /* read the maximum size of the pipe the first time this is called */
387 if (max_pipe_size == 0) {
388 const char path[] = "/proc/sys/fs/pipe-max-size";
389 int fd = open(path, O_RDONLY);
391 max_pipe_size = -1;
392 if (fd >= 0) {
393 char buf[80];
394 ssize_t r = read(fd, buf, sizeof(buf) - 1);
396 if (r > 0) {
397 buf[r] = 0;
398 max_pipe_size = strtol(buf, NULL, 10);
400 /* guard against obviously wrong values on messed up systems */
401 if (max_pipe_size <= PIPE_BUF || max_pipe_size > INT_MAX)
402 max_pipe_size = -1;
404 close(fd);
408 if (max_pipe_size > PIPE_BUF) {
409 int fd = fileno(f);
411 if (fcntl(fd, F_SETPIPE_SZ, max_pipe_size) >= 0)
412 lsx_debug("got pipe %ld bytes\n", max_pipe_size);
413 else
414 lsx_warn("couldn't set pipe size to %ld bytes: %s\n",
415 max_pipe_size, strerror(errno));
417 #endif /* do nothing for platforms without F_{GET,SET}PIPE_SZ */
420 static FILE * xfopen(char const * identifier, char const * mode, lsx_io_type * io_type)
422 *io_type = lsx_io_file;
424 if (*identifier == '|') {
425 FILE * f = NULL;
426 #ifdef HAVE_POPEN
427 #ifndef POPEN_MODE
428 #define POPEN_MODE "r"
429 #endif
430 f = popen(identifier + 1, POPEN_MODE);
431 *io_type = lsx_io_pipe;
432 incr_pipe_size(f);
433 #else
434 lsx_fail("this build of SoX cannot open pipes");
435 #endif
436 return f;
438 else if (is_url(identifier)) {
439 FILE * f = NULL;
440 #ifdef HAVE_POPEN
441 char const * const command_format = "wget --no-check-certificate -q -O- \"%s\"";
442 char * command = lsx_malloc(strlen(command_format) + strlen(identifier));
443 sprintf(command, command_format, identifier);
444 f = popen(command, POPEN_MODE);
445 incr_pipe_size(f);
446 free(command);
447 *io_type = lsx_io_url;
448 #else
449 lsx_fail("this build of SoX cannot open URLs");
450 #endif
451 return f;
453 return fopen(identifier, mode);
456 /* Hack to rewind pipes (a small amount).
457 * Works by resetting the FILE buffer pointer */
458 static void UNUSED rewind_pipe(FILE * fp)
460 /* _FSTDIO is for Torek stdio (i.e. most BSD-derived libc's)
461 * In theory, we no longer need to check _NEWLIB_VERSION or __APPLE__ */
462 #if defined _FSTDIO || defined _NEWLIB_VERSION || defined __APPLE__
463 fp->_p -= PIPE_AUTO_DETECT_SIZE;
464 fp->_r += PIPE_AUTO_DETECT_SIZE;
465 #elif defined __GLIBC__
466 fp->_IO_read_ptr = fp->_IO_read_base;
467 #elif defined _MSC_VER && _MSC_VER >= 1900
468 #define NO_REWIND_PIPE
469 #elif defined _MSC_VER || defined _WIN32 || defined _WIN64 || \
470 defined _ISO_STDIO_ISO_H || defined __sgi
471 fp->_ptr = fp->_base;
472 #else
473 /* To fix this #error, either simply remove the #error line and live without
474 * file-type detection with pipes, or add support for your compiler in the
475 * lines above. Test with cat monkey.wav | ./sox --info - */
476 #error FIX NEEDED HERE
477 #define NO_REWIND_PIPE
478 (void)fp;
479 #endif
482 static sox_format_t * open_read(
483 char const * path,
484 void * buffer UNUSED,
485 size_t buffer_size UNUSED,
486 sox_signalinfo_t const * signal,
487 sox_encodinginfo_t const * encoding,
488 char const * filetype)
490 sox_format_t * ft = lsx_calloc(1, sizeof(*ft));
491 sox_format_handler_t const * handler;
492 char const * const io_types[] = {"file", "pipe", "file URL"};
493 char const * type = "";
494 size_t input_bufsiz = sox_globals.input_bufsiz?
495 sox_globals.input_bufsiz : sox_globals.bufsiz;
497 if (filetype) {
498 if (!(handler = sox_find_format(filetype, sox_false))) {
499 lsx_fail("no handler for given file type `%s'", filetype);
500 goto error;
502 ft->handler = *handler;
505 if (!(ft->handler.flags & SOX_FILE_NOSTDIO)) {
506 if (!strcmp(path, "-")) { /* Use stdin if the filename is "-" */
507 if (sox_globals.stdin_in_use_by) {
508 lsx_fail("`-' (stdin) already in use by `%s'", sox_globals.stdin_in_use_by);
509 goto error;
511 sox_globals.stdin_in_use_by = "audio input";
512 SET_BINARY_MODE(stdin);
513 ft->fp = stdin;
515 else {
516 ft->fp =
517 #ifdef HAVE_FMEMOPEN
518 buffer? fmemopen(buffer, buffer_size, "rb") :
519 #endif
520 xfopen(path, "rb", &ft->io_type);
521 type = io_types[ft->io_type];
522 if (ft->fp == NULL) {
523 lsx_fail("can't open input %s `%s': %s", type, path, strerror(errno));
524 goto error;
527 if (setvbuf (ft->fp, NULL, _IOFBF, sizeof(char) * input_bufsiz)) {
528 lsx_fail("Can't set read buffer");
529 goto error;
531 ft->seekable = is_seekable(ft);
534 if (!filetype) {
535 if (ft->seekable) {
536 filetype = auto_detect_format(ft, lsx_find_file_extension(path));
537 lsx_rewind(ft);
539 #ifndef NO_REWIND_PIPE
540 else if (!(ft->handler.flags & SOX_FILE_NOSTDIO) &&
541 input_bufsiz >= PIPE_AUTO_DETECT_SIZE) {
542 filetype = auto_detect_format(ft, lsx_find_file_extension(path));
543 rewind_pipe(ft->fp);
544 ft->tell_off = 0;
546 #endif
548 if (filetype) {
549 lsx_report("detected file format type `%s'", filetype);
550 if (!(handler = sox_find_format(filetype, sox_false))) {
551 lsx_fail("no handler for detected file type `%s'", filetype);
552 goto error;
555 else {
556 if (ft->io_type == lsx_io_pipe) {
557 filetype = "sox"; /* With successful pipe rewind, this isn't useful */
558 lsx_report("assuming input pipe `%s' has file-type `sox'", path);
560 else if (!(filetype = lsx_find_file_extension(path))) {
561 lsx_fail("can't determine type of %s `%s'", type, path);
562 goto error;
564 if (!(handler = sox_find_format(filetype, sox_true))) {
565 lsx_fail("no handler for file extension `%s'", filetype);
566 goto error;
569 ft->handler = *handler;
570 if (ft->handler.flags & SOX_FILE_NOSTDIO) {
571 xfclose(ft->fp, ft->io_type);
572 ft->fp = NULL;
575 if (!ft->handler.startread && !ft->handler.read) {
576 lsx_fail("file type `%s' isn't readable", filetype);
577 goto error;
580 ft->mode = 'r';
581 ft->filetype = lsx_strdup(filetype);
582 ft->filename = lsx_strdup(path);
583 if (signal)
584 ft->signal = *signal;
586 if (encoding)
587 ft->encoding = *encoding;
588 else sox_init_encodinginfo(&ft->encoding);
589 set_endiannesses(ft);
591 if ((ft->handler.flags & SOX_FILE_DEVICE) && !(ft->handler.flags & SOX_FILE_PHONY))
592 lsx_set_signal_defaults(ft);
594 ft->priv = lsx_calloc(1, ft->handler.priv_size);
595 /* Read and write starters can change their formats. */
596 if (ft->handler.startread && (*ft->handler.startread)(ft) != SOX_SUCCESS) {
597 lsx_fail("can't open input %s `%s': %s", type, ft->filename, ft->sox_errstr);
598 goto error;
601 /* Fill in some defaults: */
602 if (sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample))
603 ft->signal.precision = sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample);
604 if (!(ft->handler.flags & SOX_FILE_PHONY) && !ft->signal.channels)
605 ft->signal.channels = 1;
607 if (sox_checkformat(ft) != SOX_SUCCESS) {
608 lsx_fail("bad input format for %s `%s': %s", type, ft->filename, ft->sox_errstr);
609 goto error;
612 if (signal) {
613 if (signal->rate && signal->rate != ft->signal.rate)
614 lsx_warn("can't set sample rate %g; using %g", signal->rate, ft->signal.rate);
615 if (signal->channels && signal->channels != ft->signal.channels)
616 lsx_warn("can't set %u channels; using %u", signal->channels, ft->signal.channels);
618 return ft;
620 error:
621 if (ft->fp && ft->fp != stdin)
622 xfclose(ft->fp, ft->io_type);
623 free(ft->priv);
624 free(ft->filename);
625 free(ft->filetype);
626 free(ft);
627 return NULL;
630 sox_format_t * sox_open_read(
631 char const * path,
632 sox_signalinfo_t const * signal,
633 sox_encodinginfo_t const * encoding,
634 char const * filetype)
636 return open_read(path, NULL, (size_t)0, signal, encoding, filetype);
639 sox_format_t * sox_open_mem_read(
640 void * buffer,
641 size_t buffer_size,
642 sox_signalinfo_t const * signal,
643 sox_encodinginfo_t const * encoding,
644 char const * filetype)
646 return open_read("", buffer, buffer_size, signal,encoding,filetype);
649 sox_bool sox_format_supports_encoding(
650 char const * path,
651 char const * filetype,
652 sox_encodinginfo_t const * encoding)
654 #define enc_arg(T) (T)handler->write_formats[i++]
655 sox_bool is_file_extension = filetype == NULL;
656 sox_format_handler_t const * handler;
657 unsigned i = 0, s;
658 sox_encoding_t e;
660 assert(path || filetype);
661 assert(encoding);
662 if (!filetype)
663 filetype = lsx_find_file_extension(path);
665 if (!filetype || !(handler = sox_find_format(filetype, is_file_extension)) ||
666 !handler->write_formats)
667 return sox_false;
668 while ((e = enc_arg(sox_encoding_t))) {
669 if (e == encoding->encoding) {
670 sox_bool has_bits;
671 for (has_bits = sox_false; (s = enc_arg(unsigned)); has_bits = sox_true)
672 if (s == encoding->bits_per_sample)
673 return sox_true;
674 if (!has_bits && !encoding->bits_per_sample)
675 return sox_true;
676 break;
678 while (enc_arg(unsigned));
680 return sox_false;
681 #undef enc_arg
684 static void set_output_format(sox_format_t * ft)
686 sox_encoding_t e = SOX_ENCODING_UNKNOWN;
687 unsigned i, s;
688 unsigned const * encodings = ft->handler.write_formats;
689 #define enc_arg(T) (T)encodings[i++]
691 if (ft->handler.write_rates){
692 if (!ft->signal.rate)
693 ft->signal.rate = ft->handler.write_rates[0];
694 else {
695 sox_rate_t r;
696 i = 0;
697 while ((r = ft->handler.write_rates[i++])) {
698 if (r == ft->signal.rate)
699 break;
701 if (r != ft->signal.rate) {
702 sox_rate_t given = ft->signal.rate, max = 0;
703 ft->signal.rate = HUGE_VAL;
704 i = 0;
705 while ((r = ft->handler.write_rates[i++])) {
706 if (r > given && r < ft->signal.rate)
707 ft->signal.rate = r;
708 else max = max(r, max);
710 if (ft->signal.rate == HUGE_VAL)
711 ft->signal.rate = max;
712 lsx_warn("%s can't encode at %gHz; using %gHz", ft->handler.names[0], given, ft->signal.rate);
716 else if (!ft->signal.rate)
717 ft->signal.rate = SOX_DEFAULT_RATE;
719 if (ft->handler.flags & SOX_FILE_CHANS) {
720 if (ft->signal.channels == 1 && !(ft->handler.flags & SOX_FILE_MONO)) {
721 ft->signal.channels = (ft->handler.flags & SOX_FILE_STEREO)? 2 : 4;
722 lsx_warn("%s can't encode mono; setting channels to %u", ft->handler.names[0], ft->signal.channels);
723 } else
724 if (ft->signal.channels == 2 && !(ft->handler.flags & SOX_FILE_STEREO)) {
725 ft->signal.channels = (ft->handler.flags & SOX_FILE_QUAD)? 4 : 1;
726 lsx_warn("%s can't encode stereo; setting channels to %u", ft->handler.names[0], ft->signal.channels);
727 } else
728 if (ft->signal.channels == 4 && !(ft->handler.flags & SOX_FILE_QUAD)) {
729 ft->signal.channels = (ft->handler.flags & SOX_FILE_STEREO)? 2 : 1;
730 lsx_warn("%s can't encode quad; setting channels to %u", ft->handler.names[0], ft->signal.channels);
732 } else ft->signal.channels = max(ft->signal.channels, 1);
734 if (!encodings)
735 return;
736 /* If an encoding has been given, check if it supported by this handler */
737 if (ft->encoding.encoding) {
738 i = 0;
739 while ((e = enc_arg(sox_encoding_t))) {
740 if (e == ft->encoding.encoding)
741 break;
742 while (enc_arg(unsigned));
744 if (e != ft->encoding.encoding) {
745 lsx_warn("%s can't encode %s", ft->handler.names[0], sox_encodings_info[ft->encoding.encoding].desc);
746 ft->encoding.encoding = 0;
748 else {
749 unsigned max_p = 0;
750 unsigned max_p_s = 0;
751 unsigned given_size = 0;
752 sox_bool found = sox_false;
753 if (ft->encoding.bits_per_sample)
754 given_size = ft->encoding.bits_per_sample;
755 ft->encoding.bits_per_sample = 65;
756 while ((s = enc_arg(unsigned))) {
757 if (s == given_size)
758 found = sox_true;
759 if (sox_precision(e, s) >= ft->signal.precision) {
760 if (s < ft->encoding.bits_per_sample)
761 ft->encoding.bits_per_sample = s;
763 else if (sox_precision(e, s) > max_p) {
764 max_p = sox_precision(e, s);
765 max_p_s = s;
768 if (ft->encoding.bits_per_sample == 65)
769 ft->encoding.bits_per_sample = max_p_s;
770 if (given_size) {
771 if (found)
772 ft->encoding.bits_per_sample = given_size;
773 else lsx_warn("%s can't encode %s to %u-bit", ft->handler.names[0], sox_encodings_info[ft->encoding.encoding].desc, given_size);
778 /* If a size has been given, check if it supported by this handler */
779 if (!ft->encoding.encoding && ft->encoding.bits_per_sample) {
780 i = 0;
781 s= 0;
782 while (s != ft->encoding.bits_per_sample && (e = enc_arg(sox_encoding_t)))
783 while ((s = enc_arg(unsigned)) && s != ft->encoding.bits_per_sample);
784 if (s != ft->encoding.bits_per_sample) {
785 lsx_warn("%s can't encode to %u-bit", ft->handler.names[0], ft->encoding.bits_per_sample);
786 ft->encoding.bits_per_sample = 0;
788 else ft->encoding.encoding = e;
791 /* Find the smallest lossless encoding with precision >= signal.precision */
792 if (!ft->encoding.encoding) {
793 ft->encoding.bits_per_sample = 65;
794 i = 0;
795 while ((e = enc_arg(sox_encoding_t)))
796 while ((s = enc_arg(unsigned)))
797 if (!(sox_encodings_info[e].flags & (sox_encodings_lossy1 | sox_encodings_lossy2)) &&
798 sox_precision(e, s) >= ft->signal.precision && s < ft->encoding.bits_per_sample) {
799 ft->encoding.encoding = e;
800 ft->encoding.bits_per_sample = s;
804 /* Find the smallest lossy encoding with precision >= signal precision,
805 * or, if none such, the highest precision encoding */
806 if (!ft->encoding.encoding) {
807 unsigned max_p = 0;
808 sox_encoding_t max_p_e = 0;
809 unsigned max_p_s = 0;
810 i = 0;
811 while ((e = enc_arg(sox_encoding_t)))
812 do {
813 s = enc_arg(unsigned);
814 if (sox_precision(e, s) >= ft->signal.precision) {
815 if (s < ft->encoding.bits_per_sample) {
816 ft->encoding.encoding = e;
817 ft->encoding.bits_per_sample = s;
820 else if (sox_precision(e, s) > max_p) {
821 max_p = sox_precision(e, s);
822 max_p_e = e;
823 max_p_s = s;
825 } while (s);
826 if (!ft->encoding.encoding) {
827 ft->encoding.encoding = max_p_e;
828 ft->encoding.bits_per_sample = max_p_s;
831 ft->signal.precision = sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample);
832 #undef enc_arg
835 sox_format_handler_t const * sox_write_handler(
836 char const * path,
837 char const * filetype,
838 char const * * filetype1)
840 sox_format_handler_t const * handler;
841 if (filetype) {
842 if (!(handler = sox_find_format(filetype, sox_false))) {
843 if (filetype1)
844 lsx_fail("no handler for given file type `%s'", filetype);
845 return NULL;
848 else if (path) {
849 if (!(filetype = lsx_find_file_extension(path))) {
850 if (filetype1)
851 lsx_fail("can't determine type of `%s'", path);
852 return NULL;
854 if (!(handler = sox_find_format(filetype, sox_true))) {
855 if (filetype1)
856 lsx_fail("no handler for file extension `%s'", filetype);
857 return NULL;
860 else return NULL;
861 if (!handler->startwrite && !handler->write) {
862 if (filetype1)
863 lsx_fail("file type `%s' isn't writable", filetype);
864 return NULL;
866 if (filetype1)
867 *filetype1 = filetype;
868 return handler;
871 static sox_format_t * open_write(
872 char const * path,
873 void * buffer UNUSED,
874 size_t buffer_size UNUSED,
875 char * * buffer_ptr UNUSED,
876 size_t * buffer_size_ptr UNUSED,
877 sox_signalinfo_t const * signal,
878 sox_encodinginfo_t const * encoding,
879 char const * filetype,
880 sox_oob_t const * oob,
881 sox_bool (*overwrite_permitted)(const char *filename))
883 sox_format_t * ft = lsx_calloc(sizeof(*ft), 1);
884 sox_format_handler_t const * handler;
886 if (!path || !signal) {
887 lsx_fail("must specify file name and signal parameters to write file");
888 goto error;
891 if (!(handler = sox_write_handler(path, filetype, &filetype)))
892 goto error;
894 ft->handler = *handler;
896 if (!(ft->handler.flags & SOX_FILE_NOSTDIO)) {
897 if (!strcmp(path, "-")) { /* Use stdout if the filename is "-" */
898 if (sox_globals.stdout_in_use_by) {
899 lsx_fail("`-' (stdout) already in use by `%s'", sox_globals.stdout_in_use_by);
900 goto error;
902 sox_globals.stdout_in_use_by = "audio output";
903 SET_BINARY_MODE(stdout);
904 ft->fp = stdout;
906 else {
907 struct stat st;
908 if (!stat(path, &st) && (st.st_mode & S_IFMT) == S_IFREG &&
909 (overwrite_permitted && !overwrite_permitted(path))) {
910 lsx_fail("permission to overwrite `%s' denied", path);
911 goto error;
913 ft->fp =
914 #ifdef HAVE_FMEMOPEN
915 buffer? fmemopen(buffer, buffer_size, "w+b") :
916 buffer_ptr? open_memstream(buffer_ptr, buffer_size_ptr) :
917 #endif
918 fopen(path, "w+b");
919 if (ft->fp == NULL) {
920 lsx_fail("can't open output file `%s': %s", path, strerror(errno));
921 goto error;
925 /* stdout tends to be line-buffered. Override this */
926 /* to be Full Buffering. */
927 if (setvbuf (ft->fp, NULL, _IOFBF, sizeof(char) * sox_globals.bufsiz)) {
928 lsx_fail("Can't set write buffer");
929 goto error;
931 ft->seekable = is_seekable(ft);
934 ft->filetype = lsx_strdup(filetype);
935 ft->filename = lsx_strdup(path);
936 ft->mode = 'w';
937 ft->signal = *signal;
939 if (encoding)
940 ft->encoding = *encoding;
941 else sox_init_encodinginfo(&ft->encoding);
942 set_endiannesses(ft);
944 if (oob) {
945 ft->oob = *oob;
946 /* deep copy: */
947 ft->oob.comments = sox_copy_comments(oob->comments);
950 set_output_format(ft);
952 /* FIXME: doesn't cover the situation where
953 * codec changes audio length due to block alignment (e.g. 8svx, gsm): */
954 if (signal->rate && signal->channels)
955 ft->signal.length = ft->signal.length * ft->signal.rate / signal->rate *
956 ft->signal.channels / signal->channels + .5;
958 if ((ft->handler.flags & SOX_FILE_REWIND) && strcmp(ft->filetype, "sox") && !ft->signal.length && !ft->seekable)
959 lsx_warn("can't seek in output file `%s'; length in file header will be unspecified", ft->filename);
961 ft->priv = lsx_calloc(1, ft->handler.priv_size);
962 /* Read and write starters can change their formats. */
963 if (ft->handler.startwrite && (ft->handler.startwrite)(ft) != SOX_SUCCESS){
964 lsx_fail("can't open output file `%s': %s", ft->filename, ft->sox_errstr);
965 goto error;
968 if (sox_checkformat(ft) != SOX_SUCCESS) {
969 lsx_fail("bad format for output file `%s': %s", ft->filename, ft->sox_errstr);
970 goto error;
973 if ((ft->handler.flags & SOX_FILE_DEVICE) && signal) {
974 if (signal->rate && signal->rate != ft->signal.rate)
975 lsx_report("can't set sample rate %g; using %g", signal->rate, ft->signal.rate);
976 if (signal->channels && signal->channels != ft->signal.channels)
977 lsx_report("can't set %u channels; using %u", signal->channels, ft->signal.channels);
979 return ft;
981 error:
982 if (ft->fp && ft->fp != stdout)
983 xfclose(ft->fp, ft->io_type);
984 free(ft->priv);
985 free(ft->filename);
986 free(ft->filetype);
987 free(ft);
988 return NULL;
991 sox_format_t * sox_open_write(
992 char const * path,
993 sox_signalinfo_t const * signal,
994 sox_encodinginfo_t const * encoding,
995 char const * filetype,
996 sox_oob_t const * oob,
997 sox_bool (*overwrite_permitted)(const char *filename))
999 return open_write(path, NULL, (size_t)0, NULL, NULL, signal, encoding, filetype, oob, overwrite_permitted);
1002 sox_format_t * sox_open_mem_write(
1003 void * buffer,
1004 size_t buffer_size,
1005 sox_signalinfo_t const * signal,
1006 sox_encodinginfo_t const * encoding,
1007 char const * filetype,
1008 sox_oob_t const * oob)
1010 return open_write("", buffer, buffer_size, NULL, NULL, signal, encoding, filetype, oob, NULL);
1013 sox_format_t * sox_open_memstream_write(
1014 char * * buffer_ptr,
1015 size_t * buffer_size_ptr,
1016 sox_signalinfo_t const * signal,
1017 sox_encodinginfo_t const * encoding,
1018 char const * filetype,
1019 sox_oob_t const * oob)
1021 return open_write("", NULL, (size_t)0, buffer_ptr, buffer_size_ptr, signal, encoding, filetype, oob, NULL);
1024 size_t sox_read(sox_format_t * ft, sox_sample_t * buf, size_t len)
1026 size_t actual;
1027 if (ft->signal.length != SOX_UNSPEC)
1028 len = min(len, ft->signal.length - ft->olength);
1029 actual = ft->handler.read? (*ft->handler.read)(ft, buf, len) : 0;
1030 actual = actual > len? 0 : actual;
1031 ft->olength += actual;
1032 return actual;
1035 size_t sox_write(sox_format_t * ft, const sox_sample_t *buf, size_t len)
1037 size_t actual = ft->handler.write? (*ft->handler.write)(ft, buf, len) : 0;
1038 ft->olength += actual;
1039 return actual;
1042 int sox_close(sox_format_t * ft)
1044 int result = SOX_SUCCESS;
1046 if (ft->mode == 'r')
1047 result = ft->handler.stopread? (*ft->handler.stopread)(ft) : SOX_SUCCESS;
1048 else {
1049 if (ft->handler.flags & SOX_FILE_REWIND) {
1050 if (ft->olength != ft->signal.length && ft->seekable) {
1051 result = lsx_seeki(ft, (off_t)0, 0);
1052 if (result == SOX_SUCCESS)
1053 result = ft->handler.stopwrite? (*ft->handler.stopwrite)(ft)
1054 : ft->handler.startwrite?(*ft->handler.startwrite)(ft) : SOX_SUCCESS;
1057 else result = ft->handler.stopwrite? (*ft->handler.stopwrite)(ft) : SOX_SUCCESS;
1060 if (ft->fp == stdin) {
1061 sox_globals.stdin_in_use_by = NULL;
1062 } else if (ft->fp == stdout) {
1063 fflush(stdout);
1064 sox_globals.stdout_in_use_by = NULL;
1065 } else if (ft->fp) {
1066 xfclose(ft->fp, ft->io_type);
1069 free(ft->priv);
1070 free(ft->filename);
1071 free(ft->filetype);
1072 sox_delete_comments(&ft->oob.comments);
1074 free(ft);
1075 return result;
1078 int sox_seek(sox_format_t * ft, sox_uint64_t offset, int whence)
1080 /* FIXME: Implement SOX_SEEK_CUR and SOX_SEEK_END. */
1081 if (whence != SOX_SEEK_SET)
1082 return SOX_EOF; /* FIXME: return SOX_EINVAL */
1084 /* If file is a seekable file and this handler supports seeking,
1085 * then invoke handler's function.
1087 if (ft->seekable && ft->handler.seek)
1088 return (*ft->handler.seek)(ft, offset);
1089 return SOX_EOF; /* FIXME: return SOX_EBADF */
1092 static int strcaseends(char const * str, char const * end)
1094 size_t str_len = strlen(str), end_len = strlen(end);
1095 return str_len >= end_len && !strcasecmp(str + str_len - end_len, end);
1098 typedef enum {None, M3u, Pls} playlist_t;
1100 static playlist_t playlist_type(char const * filename)
1102 char * x, * p;
1103 playlist_t result = None;
1105 if (*filename == '|')
1106 return result;
1107 if (strcaseends(filename, ".m3u"))
1108 return M3u;
1109 if (strcaseends(filename, ".pls"))
1110 return Pls;
1111 x = lsx_strdup(filename);
1112 p = strrchr(x, '?');
1113 if (p) {
1114 *p = '\0';
1115 result = playlist_type(x);
1117 free(x);
1118 return result;
1121 sox_bool sox_is_playlist(char const * filename)
1123 return playlist_type(filename) != None;
1126 int sox_parse_playlist(sox_playlist_callback_t callback, void * p, char const * const listname)
1128 sox_bool const is_pls = playlist_type(listname) == Pls;
1129 int const comment_char = "#;"[is_pls];
1130 size_t text_length = 100;
1131 char * text = lsx_malloc(text_length + 1);
1132 char * dirname = lsx_strdup(listname);
1133 char * slash_pos = LAST_SLASH(dirname);
1134 lsx_io_type io_type;
1135 FILE * file = xfopen(listname, "r", &io_type);
1136 char * filename;
1137 int c, result = SOX_SUCCESS;
1139 if (!slash_pos)
1140 *dirname = '\0';
1141 else
1142 *slash_pos = '\0';
1144 if (file == NULL) {
1145 lsx_fail("Can't open playlist file `%s': %s", listname, strerror(errno));
1146 result = SOX_EOF;
1148 else {
1149 do {
1150 size_t i = 0;
1151 size_t begin = 0, end = 0;
1153 while (isspace(c = getc(file)));
1154 if (c == EOF)
1155 break;
1156 while (c != EOF && !strchr("\r\n", c) && c != comment_char) {
1157 if (i == text_length)
1158 text = lsx_realloc(text, (text_length <<= 1) + 1);
1159 text[i++] = c;
1160 if (!strchr(" \t\f", c))
1161 end = i;
1162 c = getc(file);
1164 if (ferror(file))
1165 break;
1166 if (c == comment_char) {
1167 do c = getc(file);
1168 while (c != EOF && !strchr("\r\n", c));
1169 if (ferror(file))
1170 break;
1172 text[end] = '\0';
1173 if (is_pls) {
1174 char dummy;
1175 if (!strncasecmp(text, "file", (size_t) 4) && sscanf(text + 4, "%*u=%c", &dummy) == 1)
1176 begin = strchr(text + 5, '=') - text + 1;
1177 else end = 0;
1179 if (begin != end) {
1180 char const * id = text + begin;
1182 if (!dirname[0] || is_url(id) || IS_ABSOLUTE(id))
1183 filename = lsx_strdup(id);
1184 else {
1185 filename = lsx_malloc(strlen(dirname) + strlen(id) + 2);
1186 sprintf(filename, "%s/%s", dirname, id);
1188 if (sox_is_playlist(filename))
1189 sox_parse_playlist(callback, p, filename);
1190 else if (callback(p, filename))
1191 c = EOF;
1192 free(filename);
1194 } while (c != EOF);
1196 if (ferror(file)) {
1197 lsx_fail("error reading playlist file `%s': %s", listname, strerror(errno));
1198 result = SOX_EOF;
1200 if (xfclose(file, io_type) && io_type == lsx_io_url) {
1201 lsx_fail("error reading playlist file URL `%s'", listname);
1202 result = SOX_EOF;
1205 free(text);
1206 free(dirname);
1207 return result;
1210 /*----------------------------- Formats library ------------------------------*/
1212 enum {
1213 #define FORMAT(f) f,
1214 #include "formats.h"
1215 #undef FORMAT
1216 NSTATIC_FORMATS
1219 static sox_bool plugins_initted = sox_false;
1221 #ifdef HAVE_LIBLTDL /* Plugin format handlers */
1222 #define MAX_DYNAMIC_FORMATS 42
1223 #define MAX_FORMATS (NSTATIC_FORMATS + MAX_DYNAMIC_FORMATS)
1224 #define MAX_FORMATS_1 (MAX_FORMATS + 1)
1225 #define MAX_NAME_LEN (size_t)1024 /* FIXME: Use vasprintf */
1226 #else
1227 #define MAX_FORMATS_1
1228 #endif
1230 #define FORMAT(f) extern sox_format_handler_t const * lsx_##f##_format_fn(void);
1231 #include "formats.h"
1232 #undef FORMAT
1234 static sox_format_tab_t s_sox_format_fns[MAX_FORMATS_1] = {
1235 #define FORMAT(f) {NULL, lsx_##f##_format_fn},
1236 #include "formats.h"
1237 #undef FORMAT
1238 {NULL, NULL}
1241 const sox_format_tab_t *
1242 sox_get_format_fns(void)
1244 return s_sox_format_fns;
1247 static unsigned nformats = NSTATIC_FORMATS;
1249 #ifdef HAVE_LIBLTDL /* Plugin format handlers */
1251 static int init_format(const char *file, lt_ptr data)
1253 lt_dlhandle lth = lt_dlopenext(file);
1254 const char *end = file + strlen(file);
1255 const char prefix[] = "sox_fmt_";
1256 char fnname[MAX_NAME_LEN];
1257 char *start = strstr(file, prefix);
1259 (void)data;
1260 if (start && (start += sizeof(prefix) - 1) < end) {
1261 int ret = snprintf(fnname, MAX_NAME_LEN,
1262 "lsx_%.*s_format_fn", (int)(end - start), start);
1263 if (ret > 0 && ret < (int)MAX_NAME_LEN) {
1264 union {sox_format_fn_t fn; lt_ptr ptr;} ltptr;
1265 ltptr.ptr = lt_dlsym(lth, fnname);
1266 lsx_debug("opening format plugin `%s': library %p, entry point %p\n",
1267 fnname, (void *)lth, ltptr.ptr);
1268 if (ltptr.fn && (ltptr.fn()->sox_lib_version_code & ~255) ==
1269 (SOX_LIB_VERSION_CODE & ~255)) { /* compatible version check */
1270 if (nformats == MAX_FORMATS) {
1271 lsx_warn("too many plugin formats");
1272 return -1;
1274 s_sox_format_fns[nformats++].fn = ltptr.fn;
1278 return 0;
1280 #endif
1282 int sox_format_init(void) /* Find & load format handlers. */
1284 if (plugins_initted)
1285 return SOX_EOF;
1287 plugins_initted = sox_true;
1288 #ifdef HAVE_LIBLTDL
1290 int error = lt_dlinit();
1291 if (error) {
1292 lsx_fail("lt_dlinit failed with %d error(s): %s", error, lt_dlerror());
1293 return SOX_EOF;
1295 lt_dlforeachfile(PKGLIBDIR, init_format, NULL);
1297 #endif
1298 return SOX_SUCCESS;
1301 void sox_format_quit(void) /* Cleanup things. */
1303 #ifdef HAVE_LIBLTDL
1304 int ret;
1305 if (plugins_initted && (ret = lt_dlexit()) != 0)
1306 lsx_fail("lt_dlexit failed with %d error(s): %s", ret, lt_dlerror());
1307 plugins_initted = sox_false;
1308 nformats = NSTATIC_FORMATS;
1309 #endif
1312 /* Find a named format in the formats library.
1314 * (c) 2005-9 Chris Bagwell and SoX contributors.
1315 * Copyright 1991 Lance Norskog And Sundry Contributors.
1317 * This source code is freely redistributable and may be used for any
1318 * purpose. This copyright notice must be maintained.
1320 * Lance Norskog, Sundry Contributors, Chris Bagwell and SoX contributors
1321 * are not responsible for the consequences of using this software.
1323 sox_format_handler_t const * sox_find_format(char const * name0, sox_bool no_dev)
1325 size_t f, n;
1327 if (name0) {
1328 char * name = lsx_strdup(name0);
1329 char * pos = strchr(name, ';');
1330 if (pos) /* Use only the 1st clause of a mime string */
1331 *pos = '\0';
1332 for (f = 0; f < nformats; ++f) {
1333 sox_format_handler_t const * handler = s_sox_format_fns[f].fn();
1335 if (!(no_dev && (handler->flags & SOX_FILE_DEVICE)))
1336 for (n = 0; handler->names[n]; ++n)
1337 if (!strcasecmp(handler->names[n], name)) {
1338 free(name);
1339 return handler; /* Found it. */
1342 free(name);
1344 if (sox_format_init() == SOX_SUCCESS) /* Try again with plugins */
1345 return sox_find_format(name0, no_dev);
1346 return NULL;