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