cannam@125: /* cannam@125: ** Copyright (C) 2010-2014 Erik de Castro Lopo cannam@125: ** cannam@125: ** All rights reserved. cannam@125: ** cannam@125: ** Redistribution and use in source and binary forms, with or without cannam@125: ** modification, are permitted provided that the following conditions are cannam@125: ** met: cannam@125: ** cannam@125: ** * Redistributions of source code must retain the above copyright cannam@125: ** notice, this list of conditions and the following disclaimer. cannam@125: ** * Redistributions in binary form must reproduce the above copyright cannam@125: ** notice, this list of conditions and the following disclaimer in cannam@125: ** the documentation and/or other materials provided with the cannam@125: ** distribution. cannam@125: ** * Neither the author nor the names of any contributors may be used cannam@125: ** to endorse or promote products derived from this software without cannam@125: ** specific prior written permission. cannam@125: ** cannam@125: ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS cannam@125: ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED cannam@125: ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR cannam@125: ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR cannam@125: ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, cannam@125: ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, cannam@125: ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; cannam@125: ** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, cannam@125: ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR cannam@125: ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF cannam@125: ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cannam@125: */ cannam@125: cannam@125: #include cannam@125: #include cannam@125: #include cannam@125: #include cannam@125: #include cannam@125: #include cannam@125: #include cannam@125: #include cannam@125: #include cannam@125: #include cannam@125: #include cannam@125: cannam@125: #include cannam@125: cannam@125: #include "common.h" cannam@125: cannam@125: #define BUFFER_LEN (1 << 16) cannam@125: cannam@125: #define NOT(x) (! (x)) cannam@125: cannam@125: cannam@125: static void usage_exit (const char *progname) ; cannam@125: static void salvage_file (const char * broken_wav, const char * fixed_w64) ; cannam@125: cannam@125: int cannam@125: main (int argc, char *argv []) cannam@125: { cannam@125: if (argc != 3) cannam@125: usage_exit (program_name (argv [0])) ; cannam@125: cannam@125: salvage_file (argv [1], argv [2]) ; cannam@125: cannam@125: return 0 ; cannam@125: } /* main */ cannam@125: cannam@125: /*============================================================================== cannam@125: */ cannam@125: cannam@125: static void lseek_or_die (int fd, off_t offset, int whence) ; cannam@125: static sf_count_t get_file_length (int fd, const char * name) ; cannam@125: static sf_count_t find_data_offset (int fd, int format) ; cannam@125: static void copy_data (int fd, SNDFILE * sndfile, int readsize) ; cannam@125: cannam@125: cannam@125: static void cannam@125: usage_exit (const char *progname) cannam@125: { printf ("Usage :\n\n %s \n\n", progname) ; cannam@125: puts ("Salvages the audio data from WAV files which are more than 4G in length.\n") ; cannam@125: printf ("Using %s.\n\n", sf_version_string ()) ; cannam@125: exit (1) ; cannam@125: } /* usage_exit */ cannam@125: cannam@125: static void cannam@125: salvage_file (const char * broken_wav, const char * fixed_w64) cannam@125: { SNDFILE * sndfile ; cannam@125: SF_INFO sfinfo ; cannam@125: sf_count_t broken_len, data_offset ; cannam@125: int fd, read_size ; cannam@125: cannam@125: if (strcmp (broken_wav, fixed_w64) == 0) cannam@125: { printf ("Error : Input and output files must be different.\n\n") ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: cannam@125: if ((fd = open (broken_wav, O_RDONLY)) < 0) cannam@125: { printf ("Error : Not able to open file '%s' : %s\n", broken_wav, strerror (errno)) ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: cannam@125: broken_len = get_file_length (fd, broken_wav) ; cannam@125: if (broken_len <= 0xffffffff) cannam@125: printf ("File is not greater than 4Gig but salvaging anyway.\n") ; cannam@125: cannam@125: /* Grab the format info from the broken file. */ cannam@125: memset (&sfinfo, 0, sizeof (sfinfo)) ; cannam@125: if ((sndfile = sf_open (broken_wav, SFM_READ, &sfinfo)) == NULL) cannam@125: { printf ("sf_open ('%s') failed : %s\n", broken_wav, sf_strerror (NULL)) ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: sf_close (sndfile) ; cannam@125: cannam@125: data_offset = find_data_offset (fd, sfinfo.format & SF_FORMAT_TYPEMASK) ; cannam@125: cannam@125: printf ("Offset to audio data : %" PRId64 "\n", data_offset) ; cannam@125: cannam@125: switch (sfinfo.format & SF_FORMAT_TYPEMASK) cannam@125: { case SF_FORMAT_WAV : cannam@125: case SF_FORMAT_WAVEX : cannam@125: sfinfo.format = SF_FORMAT_W64 | (sfinfo.format & SF_FORMAT_SUBMASK) ; cannam@125: break ; cannam@125: cannam@125: default : cannam@125: printf ("Don't currently support this file type.\n") ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: cannam@125: switch (sfinfo.format & SF_FORMAT_SUBMASK) cannam@125: { case SF_FORMAT_PCM_U8 : cannam@125: case SF_FORMAT_PCM_S8 : cannam@125: read_size = 1 ; cannam@125: break ; cannam@125: cannam@125: case SF_FORMAT_PCM_16 : cannam@125: read_size = 2 ; cannam@125: break ; cannam@125: cannam@125: case SF_FORMAT_PCM_24 : cannam@125: read_size = 3 ; cannam@125: break ; cannam@125: cannam@125: case SF_FORMAT_PCM_32 : cannam@125: case SF_FORMAT_FLOAT : cannam@125: read_size = 4 ; cannam@125: break ; cannam@125: cannam@125: case SF_FORMAT_DOUBLE : cannam@125: read_size = 8 ; cannam@125: break ; cannam@125: cannam@125: default : cannam@125: printf ("Sorry, don't currently support this file encoding type.\n") ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: cannam@125: read_size *= sfinfo.channels ; cannam@125: cannam@125: if ((sndfile = sf_open (fixed_w64, SFM_WRITE, &sfinfo)) == NULL) cannam@125: { printf ("sf_open ('%s') failed : %s\n", broken_wav, sf_strerror (NULL)) ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: cannam@125: lseek_or_die (fd, data_offset, SEEK_SET) ; cannam@125: cannam@125: copy_data (fd, sndfile, read_size) ; cannam@125: cannam@125: sf_close (sndfile) ; cannam@125: cannam@125: puts ("Done!") ; cannam@125: } /* salvage_file */ cannam@125: cannam@125: /*------------------------------------------------------------------------------ cannam@125: */ cannam@125: cannam@125: static void cannam@125: lseek_or_die (int fd, off_t offset, int whence) cannam@125: { cannam@125: if (lseek (fd, offset, whence) < 0) cannam@125: { printf ("lseek failed : %s\n", strerror (errno)) ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: cannam@125: return ; cannam@125: } /* lseek_or_die */ cannam@125: cannam@125: cannam@125: static sf_count_t cannam@125: get_file_length (int fd, const char * name) cannam@125: { struct stat sbuf ; cannam@125: cannam@125: if (sizeof (sbuf.st_size) != 8) cannam@125: { puts ("Error : sizeof (sbuf.st_size) != 8. Was program compiled with\n" cannam@125: " 64 bit file offsets?\n") ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: cannam@125: if (fstat (fd, &sbuf) != 0) cannam@125: { printf ("Error : fstat ('%s') failed : %s\n", name, strerror (errno)) ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: cannam@125: return sbuf.st_size ; cannam@125: } /* get_file_length */ cannam@125: cannam@125: static sf_count_t cannam@125: find_data_offset (int fd, int format) cannam@125: { char buffer [8192], *cptr ; cannam@125: const char * target = "XXXX" ; cannam@125: sf_count_t offset = -1, extra ; cannam@125: int rlen, slen ; cannam@125: cannam@125: switch (format) cannam@125: { case SF_FORMAT_WAV : cannam@125: case SF_FORMAT_WAVEX : cannam@125: target = "data" ; cannam@125: extra = 8 ; cannam@125: break ; cannam@125: cannam@125: case SF_FORMAT_AIFF : cannam@125: target = "SSND" ; cannam@125: extra = 16 ; cannam@125: break ; cannam@125: cannam@125: default : cannam@125: puts ("Error : Sorry, don't handle this input file format.\n") ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: cannam@125: slen = strlen (target) ; cannam@125: cannam@125: lseek_or_die (fd, 0, SEEK_SET) ; cannam@125: cannam@125: printf ("Searching for '%s' maker.\n", target) ; cannam@125: cannam@125: if ((rlen = read (fd, buffer, sizeof (buffer))) < 0) cannam@125: { printf ("Error : failed read : %s\n", strerror (errno)) ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: cannam@125: cptr = memchr (buffer, target [0], rlen - slen) ; cannam@125: if (cptr && memcmp (cptr, target, slen) == 0) cannam@125: offset = cptr - buffer ; cannam@125: else cannam@125: { printf ("Error : Could not find data offset.\n") ; cannam@125: exit (1) ; cannam@125: } ; cannam@125: cannam@125: return offset + extra ; cannam@125: } /* find_data_offset */ cannam@125: cannam@125: static void cannam@125: copy_data (int fd, SNDFILE * sndfile, int readsize) cannam@125: { static char * buffer ; cannam@125: sf_count_t readlen, count ; cannam@125: int bufferlen, done = 0 ; cannam@125: cannam@125: bufferlen = readsize * 1024 ; cannam@125: buffer = malloc (bufferlen) ; cannam@125: cannam@125: while (NOT (done) && (readlen = read (fd, buffer, bufferlen)) >= 0) cannam@125: { if (readlen < bufferlen) cannam@125: { readlen -= readlen % readsize ; cannam@125: done = 1 ; cannam@125: } ; cannam@125: cannam@125: if ((count = sf_write_raw (sndfile, buffer, readlen)) != readlen) cannam@125: { printf ("Error : sf_write_raw returned %" PRId64 " : %s\n", count, sf_strerror (sndfile)) ; cannam@125: return ; cannam@125: } ; cannam@125: } ; cannam@125: cannam@125: free (buffer) ; cannam@125: cannam@125: return ; cannam@125: } /* copy_data */ cannam@125: