/* Implements the public API for using libSoX file formats. * All public functions & data are prefixed with sox_ . * * (c) 2005-8 Chris Bagwell and SoX contributors * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE#include "sox_i.h" #include <assert.h>#include <ctype.h>#include <errno.h>#include <fcntl.h>#include <stdlib.h>#include <string.h>#include <sys/stat.h>#include <sys/types.h> #ifdef HAVE_IO_H #include <io.h>#endif #if HAVE_MAGIC #include <magic.h>#endif #ifdef HAVE_UNISTD_H# include <unistd.h>#endif #define PIPE_AUTO_DETECT_SIZE 256 /* Only as much as we can rewind a pipe */#define AUTO_DETECT_SIZE 4096 /* For seekable file, so no restriction */ static char const * auto_detect_format(sox_format_t * ft, char const * ext){ char data[AUTO_DETECT_SIZE]; size_t len = lsx_readbuf(ft, data, ft->seekable? sizeof(data) : PIPE_AUTO_DETECT_SIZE); #define CHECK(type, p2, l2, d2, p1, l1, d1) if (len >= p1 + l1 && \ !memcmp(data + p1, d1, (size_t)l1) && !memcmp(data + p2, d2, (size_t)l2)) return #type; CHECK(voc , 0, 0, "" , 0, 20, "Creative Voice File\x1a") CHECK(smp , 0, 0, "" , 0, 17, "SOUND SAMPLE DATA") CHECK(wve , 0, 0, "" , 0, 15, "ALawSoundFile**") CHECK(gsrt , 0, 0, "" , 16, 9, "ring.bin") CHECK(amr-wb, 0, 0, "" , 0, 9, "#!AMR-WB\n") CHECK(prc , 0, 0, "" , 0, 8, "\x37\x00\x00\x10\x6d\x00\x00\x10") CHECK(sph , 0, 0, "" , 0, 7, "NIST_1A") CHECK(amr-nb, 0, 0, "" , 0, 6, "#!AMR\n") CHECK(txw , 0, 0, "" , 0, 6, "LM8953") CHECK(sndt , 0, 0, "" , 0, 6, "SOUND\x1a") CHECK(vorbis, 0, 4, "OggS" , 29, 6, "vorbis") CHECK(opus , 0, 4, "OggS" , 28, 8, "OpusHead") CHECK(speex , 0, 4, "OggS" , 28, 6, "Speex") CHECK(hcom ,65, 4, "FSSD" , 128,4, "HCOM") CHECK(wav , 0, 4, "RIFF" , 8, 4, "WAVE") CHECK(wav , 0, 4, "RIFX" , 8, 4, "WAVE") CHECK(wav , 0, 4, "RF64" , 8, 4, "WAVE") CHECK(aiff , 0, 4, "FORM" , 8, 4, "AIFF") CHECK(aifc , 0, 4, "FORM" , 8, 4, "AIFC") CHECK(8svx , 0, 4, "FORM" , 8, 4, "8SVX") CHECK(maud , 0, 4, "FORM" , 8, 4, "MAUD") CHECK(xa , 0, 0, "" , 0, 4, "XA\0\0") CHECK(xa , 0, 0, "" , 0, 4, "XAI\0") CHECK(xa , 0, 0, "" , 0, 4, "XAJ\0") CHECK(au , 0, 0, "" , 0, 4, ".snd") CHECK(au , 0, 0, "" , 0, 4, "dns.") CHECK(au , 0, 0, "" , 0, 4, "\0ds.") CHECK(au , 0, 0, "" , 0, 4, ".sd\0") CHECK(flac , 0, 0, "" , 0, 4, "fLaC") CHECK(avr , 0, 0, "" , 0, 4, "2BIT") CHECK(caf , 0, 0, "" , 0, 4, "caff") CHECK(wv , 0, 0, "" , 0, 4, "wvpk") CHECK(paf , 0, 0, "" , 0, 4, " paf") CHECK(sf , 0, 0, "" , 0, 4, "\144\243\001\0") CHECK(sf , 0, 0, "" , 0, 4, "\0\001\243\144") CHECK(sf , 0, 0, "" , 0, 4, "\144\243\002\0") CHECK(sf , 0, 0, "" , 0, 4, "\0\002\243\144") CHECK(sf , 0, 0, "" , 0, 4, "\144\243\003\0") CHECK(sf , 0, 0, "" , 0, 4, "\0\003\243\144") CHECK(sf , 0, 0, "" , 0, 4, "\144\243\004\0") CHECK(sox , 0, 0, "" , 0, 4, ".SoX") CHECK(sox , 0, 0, "" , 0, 4, "XoS.") if (ext && !strcasecmp(ext, "snd")) CHECK(sndr , 7, 1, "" , 0, 2, "\0") #undef CHECK #if HAVE_MAGIC if (sox_globals.use_magic) { static magic_t magic; char const * filetype = NULL; if (!magic) { magic = magic_open(MAGIC_MIME | MAGIC_SYMLINK); if (magic) magic_load(magic, NULL); } if (magic) filetype = magic_buffer(magic, data, len); if (filetype && strncmp(filetype, "application/octet-stream", (size_t)24) && !lsx_strends(filetype, "/unknown") && strncmp(filetype, "text/plain", (size_t)10) ) return filetype; else if (filetype) lsx_debug("libmagic detected %s", filetype); }#endif return NULL;} static sox_encodings_info_t const s_sox_encodings_info[] = { {sox_encodings_none , "n/a" , "Unknown or not applicable"}, {sox_encodings_none , "Signed PCM" , "Signed Integer PCM"}, {sox_encodings_none , "Unsigned PCM" , "Unsigned Integer PCM"}, {sox_encodings_none , "F.P. PCM" , "Floating Point PCM"}, {sox_encodings_none , "F.P. PCM" , "Floating Point (text) PCM"}, {sox_encodings_none , "FLAC" , "FLAC"}, {sox_encodings_none , "HCOM" , "HCOM"}, {sox_encodings_none , "WavPack" , "WavPack"}, {sox_encodings_none , "F.P. WavPack" , "Floating Point WavPack"}, {sox_encodings_lossy1, "u-law" , "u-law"}, {sox_encodings_lossy1, "A-law" , "A-law"}, {sox_encodings_lossy1, "G.721 ADPCM" , "G.721 ADPCM"}, {sox_encodings_lossy1, "G.723 ADPCM" , "G.723 ADPCM"}, {sox_encodings_lossy1, "CL ADPCM (8)" , "CL ADPCM (from 8-bit)"}, {sox_encodings_lossy1, "CL ADPCM (16)", "CL ADPCM (from 16-bit)"}, {sox_encodings_lossy1, "MS ADPCM" , "MS ADPCM"}, {sox_encodings_lossy1, "IMA ADPCM" , "IMA ADPCM"}, {sox_encodings_lossy1, "OKI ADPCM" , "OKI ADPCM"}, {sox_encodings_lossy1, "DPCM" , "DPCM"}, {sox_encodings_none , "DWVW" , "DWVW"}, {sox_encodings_none , "DWVWN" , "DWVWN"}, {sox_encodings_lossy2, "GSM" , "GSM"}, {sox_encodings_lossy2, "MPEG audio" , "MPEG audio (layer I, II or III)"}, {sox_encodings_lossy2, "Vorbis" , "Vorbis"}, {sox_encodings_lossy2, "AMR-WB" , "AMR-WB"}, {sox_encodings_lossy2, "AMR-NB" , "AMR-NB"}, {sox_encodings_lossy2, "CVSD" , "CVSD"}, {sox_encodings_lossy2, "LPC10" , "LPC10"}, {sox_encodings_lossy2, "Opus" , "Opus"},}; assert_static(array_length(s_sox_encodings_info) == SOX_ENCODINGS, SIZE_MISMATCH_BETWEEN_sox_encoding_t_AND_sox_encodings_info); sox_encodings_info_t const *sox_get_encodings_info(void){ return s_sox_encodings_info;} unsigned sox_precision(sox_encoding_t encoding, unsigned bits_per_sample){ switch (encoding) { case SOX_ENCODING_DWVW: return bits_per_sample; case SOX_ENCODING_DWVWN: return !bits_per_sample? 16: 0; /* ? */ case SOX_ENCODING_HCOM: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 1? bits_per_sample: 0; case SOX_ENCODING_WAVPACK: case SOX_ENCODING_FLAC: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 4? bits_per_sample: 0; case SOX_ENCODING_SIGN2: return bits_per_sample <= 32? bits_per_sample : 0; case SOX_ENCODING_UNSIGNED: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 4? bits_per_sample: 0; case SOX_ENCODING_ALAW: return bits_per_sample == 8? 13: 0; case SOX_ENCODING_ULAW: return bits_per_sample == 8? 14: 0; case SOX_ENCODING_CL_ADPCM: return bits_per_sample? 8: 0; case SOX_ENCODING_CL_ADPCM16: return bits_per_sample == 4? 13: 0; case SOX_ENCODING_MS_ADPCM: return bits_per_sample == 4? 14: 0; case SOX_ENCODING_IMA_ADPCM: return bits_per_sample == 4? 13: 0; case SOX_ENCODING_OKI_ADPCM: return bits_per_sample == 4? 12: 0; case SOX_ENCODING_G721: return bits_per_sample == 4? 12: 0; case SOX_ENCODING_G723: return bits_per_sample == 3? 8: bits_per_sample == 5? 14: 0; case SOX_ENCODING_CVSD: return bits_per_sample == 1? 16: 0; case SOX_ENCODING_DPCM: return bits_per_sample; /* ? */ case SOX_ENCODING_MP3: return 0; /* Accept the precision returned by the format. */ case SOX_ENCODING_GSM: case SOX_ENCODING_VORBIS: case SOX_ENCODING_OPUS: case SOX_ENCODING_AMR_WB: case SOX_ENCODING_AMR_NB: case SOX_ENCODING_LPC10: return !bits_per_sample? 16: 0; case SOX_ENCODING_WAVPACKF: case SOX_ENCODING_FLOAT: return bits_per_sample == 32 ? 25: bits_per_sample == 64 ? 54: 0; case SOX_ENCODING_FLOAT_TEXT: return !bits_per_sample? 54: 0; case SOX_ENCODINGS: case SOX_ENCODING_UNKNOWN: break; } return 0;} void sox_init_encodinginfo(sox_encodinginfo_t * e){ e->reverse_bytes = sox_option_default; e->reverse_nibbles = sox_option_default; e->reverse_bits = sox_option_default; e->compression = HUGE_VAL;} /*--------------------------------- Comments ---------------------------------*/ size_t sox_num_comments(sox_comments_t comments){ size_t result = 0; if (!comments) return 0; while (*comments++) ++result; return result;} void sox_append_comment(sox_comments_t * comments, char const * comment){ size_t n = sox_num_comments(*comments); *comments = lsx_realloc(*comments, (n + 2) * sizeof(**comments)); assert(comment); (*comments)[n++] = lsx_strdup(comment); (*comments)[n] = 0;} void sox_append_comments(sox_comments_t * comments, char const * comment){ char * end; if (comment) { while ((end = strchr(comment, '\n'))) { size_t len = end - comment; char * c = lsx_malloc((len + 1) * sizeof(*c)); strncpy(c, comment, len); c[len] = '\0'; sox_append_comment(comments, c); comment += len + 1; free(c); } if (*comment) sox_append_comment(comments, comment); }} sox_comments_t sox_copy_comments(sox_comments_t comments){ sox_comments_t result = 0; if (comments) while (*comments) sox_append_comment(&result, *comments++); return result;} void sox_delete_comments(sox_comments_t * comments){ sox_comments_t p = *comments; if (p) while (*p) free(*p++); free(*comments); *comments = 0;} char * lsx_cat_comments(sox_comments_t comments){ sox_comments_t p = comments; size_t len = 0; char * result; if (p) while (*p) len += strlen(*p++) + 1; result = lsx_calloc(len? len : 1, sizeof(*result)); if ((p = comments) && *p) { strcpy(result, *p); while (*++p) strcat(strcat(result, "\n"), *p); } return result;} char const * sox_find_comment(sox_comments_t comments, char const * id){ size_t len = strlen(id); if (comments) for (;*comments; ++comments) if (!strncasecmp(*comments, id, len) && (*comments)[len] == '=') return *comments + len + 1; return NULL;} static void set_endiannesses(sox_format_t * ft){ if (ft->handler.flags & SOX_FILE_ENDIAN) { sox_bool file_is_bigendian = !(ft->handler.flags & SOX_FILE_ENDBIG); if (ft->encoding.opposite_endian) { ft->encoding.reverse_bytes = file_is_bigendian != MACHINE_IS_BIGENDIAN; lsx_report("`%s': overriding file-type byte-order", ft->filename); } else if (ft->encoding.reverse_bytes == sox_option_default) { ft->encoding.reverse_bytes = file_is_bigendian == MACHINE_IS_BIGENDIAN; } } else { if (ft->encoding.opposite_endian) { ft->encoding.reverse_bytes = sox_option_yes; lsx_report("`%s': overriding machine byte-order", ft->filename); } else if (ft->encoding.reverse_bytes == sox_option_default) { ft->encoding.reverse_bytes = sox_option_no; } } /* FIXME: Change reports to suitable warnings if trying * to override something that can't be overridden. */ if (ft->encoding.reverse_bits == sox_option_default) ft->encoding.reverse_bits = !!(ft->handler.flags & SOX_FILE_BIT_REV); else if (ft->encoding.reverse_bits == !(ft->handler.flags & SOX_FILE_BIT_REV)) lsx_report("`%s': overriding file-type bit-order", ft->filename); if (ft->encoding.reverse_nibbles == sox_option_default) ft->encoding.reverse_nibbles = !!(ft->handler.flags & SOX_FILE_NIB_REV); else if (ft->encoding.reverse_nibbles == !(ft->handler.flags & SOX_FILE_NIB_REV)) lsx_report("`%s': overriding file-type nibble-order", ft->filename);} static sox_bool is_seekable(sox_format_t const * ft){ assert(ft); if (!ft->fp) return sox_false; return !fseek(ft->fp, 0, SEEK_CUR);} /* check that all settings have been given */static int sox_checkformat(sox_format_t * ft){ ft->sox_errno = SOX_SUCCESS; if (ft->signal.rate <= 0) { lsx_fail_errno(ft, SOX_EFMT, "sample rate zero or negative"); return SOX_EOF; } if (!ft->signal.precision) { lsx_fail_errno(ft,SOX_EFMT,"data encoding or sample size was not specified"); return SOX_EOF; } return SOX_SUCCESS;} static sox_bool is_url(char const * text) /* detects only wget-supported URLs */{ return !( strncasecmp(text, "http:" , (size_t)5) && strncasecmp(text, "https:", (size_t)6) && strncasecmp(text, "ftp:" , (size_t)4));} static int xfclose(FILE * file, lsx_io_type io_type){#ifndef HAVE_POPEN USED(io_type);#endif return#ifdef HAVE_POPEN io_type != lsx_io_file? pclose(file) :#endif fclose(file);} static void incr_pipe_size(FILE *f){/* * Linux 2.6.35 and later has the ability to expand the pipe buffer * Try to get it as big as possible to avoid stalls when SoX itself * is using big buffers */#if defined(F_GETPIPE_SZ) && defined(F_SETPIPE_SZ) static long max_pipe_size; /* read the maximum size of the pipe the first time this is called */ if (max_pipe_size == 0) { const char path[] = "/proc/sys/fs/pipe-max-size"; int fd = open(path, O_RDONLY); max_pipe_size = -1; if (fd >= 0) { char buf[80]; ssize_t r = read(fd, buf, sizeof(buf) - 1); if (r > 0) { buf[r] = 0; max_pipe_size = strtol(buf, NULL, 10); /* guard against obviously wrong values on messed up systems */ if (max_pipe_size <= PIPE_BUF || max_pipe_size > INT_MAX) max_pipe_size = -1; } close(fd); } } if (max_pipe_size > PIPE_BUF) { int fd = fileno(f); if (fcntl(fd, F_SETPIPE_SZ, max_pipe_size) >= 0) lsx_debug("got pipe %ld bytes\n", max_pipe_size); else lsx_warn("couldn't set pipe size to %ld bytes: %s\n", max_pipe_size, strerror(errno)); }#else USED(f);#endif /* do nothing for platforms without F_{GET,SET}PIPE_SZ */} static FILE * xfopen(char const * identifier, char const * mode, lsx_io_type * io_type){ *io_type = lsx_io_file; if (*identifier == '|') { FILE * f = NULL;#ifdef HAVE_POPEN#ifndef POPEN_MODE#define POPEN_MODE "r"#endif f = popen(identifier + 1, POPEN_MODE); *io_type = lsx_io_pipe; incr_pipe_size(f);#else lsx_fail("this build of SoX cannot open pipes");#endif return f; } else if (is_url(identifier)) { FILE * f = NULL;#ifdef HAVE_POPEN char const * const command_format = "wget --no-check-certificate -q -O- \"%s\""; char * command = lsx_malloc(strlen(command_format) + strlen(identifier)); sprintf(command, command_format, identifier); f = popen(command, POPEN_MODE); incr_pipe_size(f); free(command); *io_type = lsx_io_url;#else lsx_fail("this build of SoX cannot open URLs");#endif return f; } return fopen(identifier, mode);} /* Hack to rewind pipes (a small amount). * Works by resetting the FILE buffer pointer */static void UNUSED rewind_pipe(FILE * fp){/* _FSTDIO is for Torek stdio (i.e. most BSD-derived libc's) * In theory, we no longer need to check _NEWLIB_VERSION or __APPLE__ */#if defined _FSTDIO || defined _NEWLIB_VERSION || defined __APPLE__ fp->_p -= PIPE_AUTO_DETECT_SIZE; fp->_r += PIPE_AUTO_DETECT_SIZE;#elif defined __GLIBC__ fp->_IO_read_ptr = fp->_IO_read_base;#elif defined _MSC_VER && _MSC_VER >= 1900 #define NO_REWIND_PIPE#elif defined _MSC_VER || defined _WIN32 || defined _WIN64 || \ defined _ISO_STDIO_ISO_H || defined __sgi fp->_ptr = fp->_base;#elif defined NO_REWIND_PIPE USED(fp);#else /* To fix this #error, either simply remove the #error line and live without * file-type detection with pipes, or add support for your compiler in the * lines above. Test with cat monkey.wav | ./sox --info - */ #error FIX NEEDED HERE #define NO_REWIND_PIPE USED(fp);#endif} static sox_format_t * open_read( char const * path, void * buffer UNUSED, size_t buffer_size UNUSED, sox_signalinfo_t const * signal, sox_encodinginfo_t const * encoding, char const * filetype){Lines 1–500 of 1361. Load more
/* Implements the public API for using libSoX file formats. * All public functions & data are prefixed with sox_ . * * (c) 2005-8 Chris Bagwell and SoX contributors * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include "sox_i.h" #include <assert.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #ifdef HAVE_IO_H #include <io.h> #endif #if HAVE_MAGIC #include <magic.h> #endif #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #define PIPE_AUTO_DETECT_SIZE 256 /* Only as much as we can rewind a pipe */ #define AUTO_DETECT_SIZE 4096 /* For seekable file, so no restriction */ static char const * auto_detect_format(sox_format_t * ft, char const * ext) { char data[AUTO_DETECT_SIZE]; size_t len = lsx_readbuf(ft, data, ft->seekable? sizeof(data) : PIPE_AUTO_DETECT_SIZE); #define CHECK(type, p2, l2, d2, p1, l1, d1) if (len >= p1 + l1 && \ !memcmp(data + p1, d1, (size_t)l1) && !memcmp(data + p2, d2, (size_t)l2)) return #type; CHECK(voc , 0, 0, "" , 0, 20, "Creative Voice File\x1a") CHECK(smp , 0, 0, "" , 0, 17, "SOUND SAMPLE DATA") CHECK(wve , 0, 0, "" , 0, 15, "ALawSoundFile**") CHECK(gsrt , 0, 0, "" , 16, 9, "ring.bin") CHECK(amr-wb, 0, 0, "" , 0, 9, "#!AMR-WB\n") CHECK(prc , 0, 0, "" , 0, 8, "\x37\x00\x00\x10\x6d\x00\x00\x10") CHECK(sph , 0, 0, "" , 0, 7, "NIST_1A") CHECK(amr-nb, 0, 0, "" , 0, 6, "#!AMR\n") CHECK(txw , 0, 0, "" , 0, 6, "LM8953") CHECK(sndt , 0, 0, "" , 0, 6, "SOUND\x1a") CHECK(vorbis, 0, 4, "OggS" , 29, 6, "vorbis") CHECK(opus , 0, 4, "OggS" , 28, 8, "OpusHead") CHECK(speex , 0, 4, "OggS" , 28, 6, "Speex") CHECK(hcom ,65, 4, "FSSD" , 128,4, "HCOM") CHECK(wav , 0, 4, "RIFF" , 8, 4, "WAVE") CHECK(wav , 0, 4, "RIFX" , 8, 4, "WAVE") CHECK(wav , 0, 4, "RF64" , 8, 4, "WAVE") CHECK(aiff , 0, 4, "FORM" , 8, 4, "AIFF") CHECK(aifc , 0, 4, "FORM" , 8, 4, "AIFC") CHECK(8svx , 0, 4, "FORM" , 8, 4, "8SVX") CHECK(maud , 0, 4, "FORM" , 8, 4, "MAUD") CHECK(xa , 0, 0, "" , 0, 4, "XA\0\0") CHECK(xa , 0, 0, "" , 0, 4, "XAI\0") CHECK(xa , 0, 0, "" , 0, 4, "XAJ\0") CHECK(au , 0, 0, "" , 0, 4, ".snd") CHECK(au , 0, 0, "" , 0, 4, "dns.") CHECK(au , 0, 0, "" , 0, 4, "\0ds.") CHECK(au , 0, 0, "" , 0, 4, ".sd\0") CHECK(flac , 0, 0, "" , 0, 4, "fLaC") CHECK(avr , 0, 0, "" , 0, 4, "2BIT") CHECK(caf , 0, 0, "" , 0, 4, "caff") CHECK(wv , 0, 0, "" , 0, 4, "wvpk") CHECK(paf , 0, 0, "" , 0, 4, " paf") CHECK(sf , 0, 0, "" , 0, 4, "\144\243\001\0") CHECK(sf , 0, 0, "" , 0, 4, "\0\001\243\144") CHECK(sf , 0, 0, "" , 0, 4, "\144\243\002\0") CHECK(sf , 0, 0, "" , 0, 4, "\0\002\243\144") CHECK(sf , 0, 0, "" , 0, 4, "\144\243\003\0") CHECK(sf , 0, 0, "" , 0, 4, "\0\003\243\144") CHECK(sf , 0, 0, "" , 0, 4, "\144\243\004\0") CHECK(sox , 0, 0, "" , 0, 4, ".SoX") CHECK(sox , 0, 0, "" , 0, 4, "XoS.") if (ext && !strcasecmp(ext, "snd")) CHECK(sndr , 7, 1, "" , 0, 2, "\0") #undef CHECK #if HAVE_MAGIC if (sox_globals.use_magic) { static magic_t magic; char const * filetype = NULL; if (!magic) { magic = magic_open(MAGIC_MIME | MAGIC_SYMLINK); if (magic) magic_load(magic, NULL); } if (magic) filetype = magic_buffer(magic, data, len); if (filetype && strncmp(filetype, "application/octet-stream", (size_t)24) && !lsx_strends(filetype, "/unknown") && strncmp(filetype, "text/plain", (size_t)10) ) return filetype; else if (filetype) lsx_debug("libmagic detected %s", filetype); } #endif return NULL; } static sox_encodings_info_t const s_sox_encodings_info[] = { {sox_encodings_none , "n/a" , "Unknown or not applicable"}, {sox_encodings_none , "Signed PCM" , "Signed Integer PCM"}, {sox_encodings_none , "Unsigned PCM" , "Unsigned Integer PCM"}, {sox_encodings_none , "F.P. PCM" , "Floating Point PCM"}, {sox_encodings_none , "F.P. PCM" , "Floating Point (text) PCM"}, {sox_encodings_none , "FLAC" , "FLAC"}, {sox_encodings_none , "HCOM" , "HCOM"}, {sox_encodings_none , "WavPack" , "WavPack"}, {sox_encodings_none , "F.P. WavPack" , "Floating Point WavPack"}, {sox_encodings_lossy1, "u-law" , "u-law"}, {sox_encodings_lossy1, "A-law" , "A-law"}, {sox_encodings_lossy1, "G.721 ADPCM" , "G.721 ADPCM"}, {sox_encodings_lossy1, "G.723 ADPCM" , "G.723 ADPCM"}, {sox_encodings_lossy1, "CL ADPCM (8)" , "CL ADPCM (from 8-bit)"}, {sox_encodings_lossy1, "CL ADPCM (16)", "CL ADPCM (from 16-bit)"}, {sox_encodings_lossy1, "MS ADPCM" , "MS ADPCM"}, {sox_encodings_lossy1, "IMA ADPCM" , "IMA ADPCM"}, {sox_encodings_lossy1, "OKI ADPCM" , "OKI ADPCM"}, {sox_encodings_lossy1, "DPCM" , "DPCM"}, {sox_encodings_none , "DWVW" , "DWVW"}, {sox_encodings_none , "DWVWN" , "DWVWN"}, {sox_encodings_lossy2, "GSM" , "GSM"}, {sox_encodings_lossy2, "MPEG audio" , "MPEG audio (layer I, II or III)"}, {sox_encodings_lossy2, "Vorbis" , "Vorbis"}, {sox_encodings_lossy2, "AMR-WB" , "AMR-WB"}, {sox_encodings_lossy2, "AMR-NB" , "AMR-NB"}, {sox_encodings_lossy2, "CVSD" , "CVSD"}, {sox_encodings_lossy2, "LPC10" , "LPC10"}, {sox_encodings_lossy2, "Opus" , "Opus"}, }; assert_static(array_length(s_sox_encodings_info) == SOX_ENCODINGS, SIZE_MISMATCH_BETWEEN_sox_encoding_t_AND_sox_encodings_info); sox_encodings_info_t const * sox_get_encodings_info(void) { return s_sox_encodings_info; } unsigned sox_precision(sox_encoding_t encoding, unsigned bits_per_sample) { switch (encoding) { case SOX_ENCODING_DWVW: return bits_per_sample; case SOX_ENCODING_DWVWN: return !bits_per_sample? 16: 0; /* ? */ case SOX_ENCODING_HCOM: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 1? bits_per_sample: 0; case SOX_ENCODING_WAVPACK: case SOX_ENCODING_FLAC: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 4? bits_per_sample: 0; case SOX_ENCODING_SIGN2: return bits_per_sample <= 32? bits_per_sample : 0; case SOX_ENCODING_UNSIGNED: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 4? bits_per_sample: 0; case SOX_ENCODING_ALAW: return bits_per_sample == 8? 13: 0; case SOX_ENCODING_ULAW: return bits_per_sample == 8? 14: 0; case SOX_ENCODING_CL_ADPCM: return bits_per_sample? 8: 0; case SOX_ENCODING_CL_ADPCM16: return bits_per_sample == 4? 13: 0; case SOX_ENCODING_MS_ADPCM: return bits_per_sample == 4? 14: 0; case SOX_ENCODING_IMA_ADPCM: return bits_per_sample == 4? 13: 0; case SOX_ENCODING_OKI_ADPCM: return bits_per_sample == 4? 12: 0; case SOX_ENCODING_G721: return bits_per_sample == 4? 12: 0; case SOX_ENCODING_G723: return bits_per_sample == 3? 8: bits_per_sample == 5? 14: 0; case SOX_ENCODING_CVSD: return bits_per_sample == 1? 16: 0; case SOX_ENCODING_DPCM: return bits_per_sample; /* ? */ case SOX_ENCODING_MP3: return 0; /* Accept the precision returned by the format. */ case SOX_ENCODING_GSM: case SOX_ENCODING_VORBIS: case SOX_ENCODING_OPUS: case SOX_ENCODING_AMR_WB: case SOX_ENCODING_AMR_NB: case SOX_ENCODING_LPC10: return !bits_per_sample? 16: 0; case SOX_ENCODING_WAVPACKF: case SOX_ENCODING_FLOAT: return bits_per_sample == 32 ? 25: bits_per_sample == 64 ? 54: 0; case SOX_ENCODING_FLOAT_TEXT: return !bits_per_sample? 54: 0; case SOX_ENCODINGS: case SOX_ENCODING_UNKNOWN: break; } return 0; } void sox_init_encodinginfo(sox_encodinginfo_t * e) { e->reverse_bytes = sox_option_default; e->reverse_nibbles = sox_option_default; e->reverse_bits = sox_option_default; e->compression = HUGE_VAL; } /*--------------------------------- Comments ---------------------------------*/ size_t sox_num_comments(sox_comments_t comments) { size_t result = 0; if (!comments) return 0; while (*comments++) ++result; return result; } void sox_append_comment(sox_comments_t * comments, char const * comment) { size_t n = sox_num_comments(*comments); *comments = lsx_realloc(*comments, (n + 2) * sizeof(**comments)); assert(comment); (*comments)[n++] = lsx_strdup(comment); (*comments)[n] = 0; } void sox_append_comments(sox_comments_t * comments, char const * comment) { char * end; if (comment) { while ((end = strchr(comment, '\n'))) { size_t len = end - comment; char * c = lsx_malloc((len + 1) * sizeof(*c)); strncpy(c, comment, len); c[len] = '\0'; sox_append_comment(comments, c); comment += len + 1; free(c); } if (*comment) sox_append_comment(comments, comment); } } sox_comments_t sox_copy_comments(sox_comments_t comments) { sox_comments_t result = 0; if (comments) while (*comments) sox_append_comment(&result, *comments++); return result; } void sox_delete_comments(sox_comments_t * comments) { sox_comments_t p = *comments; if (p) while (*p) free(*p++); free(*comments); *comments = 0; } char * lsx_cat_comments(sox_comments_t comments) { sox_comments_t p = comments; size_t len = 0; char * result; if (p) while (*p) len += strlen(*p++) + 1; result = lsx_calloc(len? len : 1, sizeof(*result)); if ((p = comments) && *p) { strcpy(result, *p); while (*++p) strcat(strcat(result, "\n"), *p); } return result; } char const * sox_find_comment(sox_comments_t comments, char const * id) { size_t len = strlen(id); if (comments) for (;*comments; ++comments) if (!strncasecmp(*comments, id, len) && (*comments)[len] == '=') return *comments + len + 1; return NULL; } static void set_endiannesses(sox_format_t * ft) { if (ft->handler.flags & SOX_FILE_ENDIAN) { sox_bool file_is_bigendian = !(ft->handler.flags & SOX_FILE_ENDBIG); if (ft->encoding.opposite_endian) { ft->encoding.reverse_bytes = file_is_bigendian != MACHINE_IS_BIGENDIAN; lsx_report("`%s': overriding file-type byte-order", ft->filename); } else if (ft->encoding.reverse_bytes == sox_option_default) { ft->encoding.reverse_bytes = file_is_bigendian == MACHINE_IS_BIGENDIAN; } } else { if (ft->encoding.opposite_endian) { ft->encoding.reverse_bytes = sox_option_yes; lsx_report("`%s': overriding machine byte-order", ft->filename); } else if (ft->encoding.reverse_bytes == sox_option_default) { ft->encoding.reverse_bytes = sox_option_no; } } /* FIXME: Change reports to suitable warnings if trying * to override something that can't be overridden. */ if (ft->encoding.reverse_bits == sox_option_default) ft->encoding.reverse_bits = !!(ft->handler.flags & SOX_FILE_BIT_REV); else if (ft->encoding.reverse_bits == !(ft->handler.flags & SOX_FILE_BIT_REV)) lsx_report("`%s': overriding file-type bit-order", ft->filename); if (ft->encoding.reverse_nibbles == sox_option_default) ft->encoding.reverse_nibbles = !!(ft->handler.flags & SOX_FILE_NIB_REV); else if (ft->encoding.reverse_nibbles == !(ft->handler.flags & SOX_FILE_NIB_REV)) lsx_report("`%s': overriding file-type nibble-order", ft->filename); } static sox_bool is_seekable(sox_format_t const * ft) { assert(ft); if (!ft->fp) return sox_false; return !fseek(ft->fp, 0, SEEK_CUR); } /* check that all settings have been given */ static int sox_checkformat(sox_format_t * ft) { ft->sox_errno = SOX_SUCCESS; if (ft->signal.rate <= 0) { lsx_fail_errno(ft, SOX_EFMT, "sample rate zero or negative"); return SOX_EOF; } if (!ft->signal.precision) { lsx_fail_errno(ft,SOX_EFMT,"data encoding or sample size was not specified"); return SOX_EOF; } return SOX_SUCCESS; } static sox_bool is_url(char const * text) /* detects only wget-supported URLs */ { return !( strncasecmp(text, "http:" , (size_t)5) && strncasecmp(text, "https:", (size_t)6) && strncasecmp(text, "ftp:" , (size_t)4)); } static int xfclose(FILE * file, lsx_io_type io_type) { #ifndef HAVE_POPEN USED(io_type); #endif return #ifdef HAVE_POPEN io_type != lsx_io_file? pclose(file) : #endif fclose(file); } static void incr_pipe_size(FILE *f) { /* * Linux 2.6.35 and later has the ability to expand the pipe buffer * Try to get it as big as possible to avoid stalls when SoX itself * is using big buffers */ #if defined(F_GETPIPE_SZ) && defined(F_SETPIPE_SZ) static long max_pipe_size; /* read the maximum size of the pipe the first time this is called */ if (max_pipe_size == 0) { const char path[] = "/proc/sys/fs/pipe-max-size"; int fd = open(path, O_RDONLY); max_pipe_size = -1; if (fd >= 0) { char buf[80]; ssize_t r = read(fd, buf, sizeof(buf) - 1); if (r > 0) { buf[r] = 0; max_pipe_size = strtol(buf, NULL, 10); /* guard against obviously wrong values on messed up systems */ if (max_pipe_size <= PIPE_BUF || max_pipe_size > INT_MAX) max_pipe_size = -1; } close(fd); } } if (max_pipe_size > PIPE_BUF) { int fd = fileno(f); if (fcntl(fd, F_SETPIPE_SZ, max_pipe_size) >= 0) lsx_debug("got pipe %ld bytes\n", max_pipe_size); else lsx_warn("couldn't set pipe size to %ld bytes: %s\n", max_pipe_size, strerror(errno)); } #else USED(f); #endif /* do nothing for platforms without F_{GET,SET}PIPE_SZ */ } static FILE * xfopen(char const * identifier, char const * mode, lsx_io_type * io_type) { *io_type = lsx_io_file; if (*identifier == '|') { FILE * f = NULL; #ifdef HAVE_POPEN #ifndef POPEN_MODE #define POPEN_MODE "r" #endif f = popen(identifier + 1, POPEN_MODE); *io_type = lsx_io_pipe; incr_pipe_size(f); #else lsx_fail("this build of SoX cannot open pipes"); #endif return f; } else if (is_url(identifier)) { FILE * f = NULL; #ifdef HAVE_POPEN char const * const command_format = "wget --no-check-certificate -q -O- \"%s\""; char * command = lsx_malloc(strlen(command_format) + strlen(identifier)); sprintf(command, command_format, identifier); f = popen(command, POPEN_MODE); incr_pipe_size(f); free(command); *io_type = lsx_io_url; #else lsx_fail("this build of SoX cannot open URLs"); #endif return f; } return fopen(identifier, mode); } /* Hack to rewind pipes (a small amount). * Works by resetting the FILE buffer pointer */ static void UNUSED rewind_pipe(FILE * fp) { /* _FSTDIO is for Torek stdio (i.e. most BSD-derived libc's) * In theory, we no longer need to check _NEWLIB_VERSION or __APPLE__ */ #if defined _FSTDIO || defined _NEWLIB_VERSION || defined __APPLE__ fp->_p -= PIPE_AUTO_DETECT_SIZE; fp->_r += PIPE_AUTO_DETECT_SIZE; #elif defined __GLIBC__ fp->_IO_read_ptr = fp->_IO_read_base; #elif defined _MSC_VER && _MSC_VER >= 1900 #define NO_REWIND_PIPE #elif defined _MSC_VER || defined _WIN32 || defined _WIN64 || \ defined _ISO_STDIO_ISO_H || defined __sgi fp->_ptr = fp->_base; #elif defined NO_REWIND_PIPE USED(fp); #else /* To fix this #error, either simply remove the #error line and live without * file-type detection with pipes, or add support for your compiler in the * lines above. Test with cat monkey.wav | ./sox --info - */ #error FIX NEEDED HERE #define NO_REWIND_PIPE USED(fp); #endif } static sox_format_t * open_read( char const * path, void * buffer UNUSED, size_t buffer_size UNUSED, sox_signalinfo_t const * signal, sox_encodinginfo_t const * encoding, char const * filetype) {