shithub: sox

Download patch

ref: 0fc1ff31d5dafcdc0c580d43c4d275c976f469a0
parent: 78bf52d2b3a063fc1f4d7f712492f33ba5480790
author: Chris Bagwell <chris@cnpbagwell.com>
date: Sun Oct 26 17:26:40 EDT 2014

Add reading support for RF64 WAV files.

Based on specs from http://tech.ebu.ch/docs/tech/tech3306-2009.pdf.

Patch written by Dave Lambley

--- a/ChangeLog
+++ b/ChangeLog
@@ -24,6 +24,7 @@
 
   o Add optional support for reading Ogg Opus files. (John Stumpo)
   o Fix for max size text chunks in aiff files. (cbagwell)
+  o Add reading support for RF64 WAV files. (Dave Lambley)
 
 Effects:
 
--- a/src/formats.c
+++ b/src/formats.c
@@ -62,6 +62,7 @@
   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")
--- a/src/wav.c
+++ b/src/wav.c
@@ -94,6 +94,8 @@
     gsm_signal     *gsmsample;
     int            gsmindex;
     size_t      gsmbytecount;    /* counts bytes written to data block */
+    sox_bool       isRF64;          /* True if file being read is a RF64 */
+    uint64_t       ds64_dataSize;   /* Size of data chunk from ds64 header */
 } priv_t;
 
 static char *wav_format_str(unsigned wFormatTag);
@@ -370,9 +372,13 @@
 /****************************************************************************/
 /* General Sox WAV file code                                                */
 /****************************************************************************/
-static int findChunk(sox_format_t * ft, const char *Label, uint32_t *len)
+static int findChunk(sox_format_t * ft, const char *Label, uint64_t *len)
 {
     char magic[5];
+    priv_t *wav = (priv_t *) ft->priv;
+    uint32_t len_tmp;
+
+    lsx_debug("Searching for %2x %2x %2x %2x", Label[0], Label[1], Label[2], Label[3]);
     for (;;)
     {
         if (lsx_reads(ft, magic, (size_t)4) == SOX_EOF)
@@ -382,7 +388,7 @@
             return SOX_EOF;
         }
         lsx_debug("WAV Chunk %s", magic);
-        if (lsx_readdw(ft, len) == SOX_EOF)
+        if (lsx_readdw(ft, &len_tmp) == SOX_EOF)
         {
             lsx_fail_errno(ft, SOX_EHDR, "WAVE file %s chunk is too short",
                           magic);
@@ -389,17 +395,34 @@
             return SOX_EOF;
         }
 
-        if (strncmp(Label, magic, (size_t)4) == 0)
+        if (len_tmp == 0xffffffff && wav->isRF64==sox_true)
+        {
+            /* Chunk length should come from ds64 header */
+            if (memcmp(magic, "data", (size_t)4)==0)
+            {
+                *len = wav->ds64_dataSize;
+            }
+            else
+            {
+                lsx_fail_errno(ft, SOX_EHDR, "Cannot yet read block sizes of arbitary RF64 chunks, cannot find chunk '%s'", Label);
+                return SOX_EOF;
+            }
+        }
+        else {
+            *len = len_tmp;
+        }
+
+        if (memcmp(Label, magic, (size_t)4) == 0)
             break; /* Found the given chunk */
 
-	/* Chunks are required to be word aligned. */
-	if ((*len) % 2) (*len)++;
+        /* Chunks are required to be word aligned. */
+        if ((*len) % 2) (*len)++;
 
         /* skip to next chunk */
         if (*len > 0 && lsx_seeki(ft, (off_t)(*len), SEEK_CUR) != SOX_SUCCESS)
         {
             lsx_fail_errno(ft,SOX_EHDR,
-                          "WAV chunk appears to have invalid size %d.", *len);
+                          "WAV chunk appears to have invalid size %ld.", *len);
             return SOX_EOF;
         }
     }
@@ -424,10 +447,11 @@
 {
     priv_t *       wav = (priv_t *) ft->priv;
     char        magic[5];
-    uint32_t    len;
+    uint64_t    len;
 
     /* wave file characteristics */
-    uint32_t      dwRiffLength;
+    uint64_t      qwRiffLength;
+    uint32_t      dwRiffLength_tmp;
     unsigned short wChannels;       /* number of channels */
     uint32_t      dwSamplesPerSecond; /* samples per second per channel */
     uint32_t      dwAvgBytesPerSec;/* estimate of bytes per second needed */
@@ -435,7 +459,7 @@
     uint32_t wFmtSize;
     uint16_t wExtSize = 0;    /* extended field for non-PCM */
 
-    uint32_t      dwDataLength;    /* length of sound data in bytes */
+    uint64_t      qwDataLength;    /* length of sound data in bytes */
     size_t    bytesPerBlock = 0;
     int    bytespersample;          /* bytes per sample (per channel */
     char text[256];
@@ -445,7 +469,7 @@
     wav->ignoreSize = ft->signal.length == SOX_IGNORE_LENGTH;
 
     if (lsx_reads(ft, magic, (size_t)4) == SOX_EOF || (strncmp("RIFF", magic, (size_t)4) != 0 &&
-                                             strncmp("RIFX", magic, (size_t)4) != 0))
+                                             strncmp("RIFX", magic, (size_t)4) != 0 && strncmp("RF64", magic, (size_t)4)!=0 ))
     {
         lsx_fail_errno(ft,SOX_EHDR,"WAVE: RIFF header not found");
         return SOX_EOF;
@@ -459,8 +483,18 @@
     }
     else ft->encoding.reverse_bytes = MACHINE_IS_BIGENDIAN;
 
-    lsx_readdw(ft, &dwRiffLength);
+    if (strncmp("RF64", magic, (size_t)4) == 0)
+    {
+        wav->isRF64 = sox_true;
+    }
+    else
+    {
+        wav->isRF64 = sox_false;
+    }
 
+    lsx_readdw(ft, &dwRiffLength_tmp);
+    qwRiffLength = dwRiffLength_tmp;
+
     if (lsx_reads(ft, magic, (size_t)4) == SOX_EOF || strncmp("WAVE", magic, (size_t)4))
     {
         lsx_fail_errno(ft,SOX_EHDR,"WAVE header not found");
@@ -467,6 +501,21 @@
         return SOX_EOF;
     }
 
+    if (wav->isRF64 && findChunk(ft, "ds64", &len) != SOX_EOF) {
+        lsx_debug("Found ds64 header");
+
+        if (dwRiffLength_tmp==0xffffffff)
+        {
+            lsx_readqw(ft, &qwRiffLength);
+        }
+        else
+        {
+            lsx_skipbytes(ft, (size_t)8);
+        }
+        lsx_readqw(ft, &wav->ds64_dataSize);
+        lsx_skipbytes(ft, (size_t)len-16);
+    }
+
     /* Now look for the format chunk */
     if (findChunk(ft, "fmt ", &len) == SOX_EOF)
     {
@@ -804,8 +853,11 @@
         lsx_fail_errno(ft, SOX_EOF, "Could not find data chunk.");
         return SOX_EOF;
     }
-    dwDataLength = len;
-    if (dwDataLength == MS_UNSPEC) {
+
+    /* ds64 size will have been applied in findChunk */
+    qwDataLength = len;
+    /* XXX - does MS_UNSPEC apply to RF64 files? */
+    if (qwDataLength == MS_UNSPEC) {
       wav->ignoreSize = 1;
       lsx_debug("WAV Chunk data's length is value often used in pipes or 4G files.  Ignoring length.");
     }
@@ -819,9 +871,9 @@
 
     case WAVE_FORMAT_ADPCM:
         wav->numSamples =
-            lsx_ms_adpcm_samples_in((size_t)dwDataLength, (size_t)ft->signal.channels,
+            lsx_ms_adpcm_samples_in((size_t)qwDataLength, (size_t)ft->signal.channels,
                            (size_t)wav->blockAlign, (size_t)wav->samplesPerBlock);
-        lsx_debug_more("datalen %d, numSamples %lu",dwDataLength, (unsigned long)wav->numSamples);
+        lsx_debug_more("datalen %ld, numSamples %lu",qwDataLength, (unsigned long)wav->numSamples);
         wav->blockSamplesRemaining = 0;        /* Samples left in buffer */
         ft->signal.length = wav->numSamples*ft->signal.channels;
         break;
@@ -830,9 +882,9 @@
         /* Compute easiest part of number of samples.  For every block, there
            are samplesPerBlock samples to read. */
         wav->numSamples =
-            lsx_ima_samples_in((size_t)dwDataLength, (size_t)ft->signal.channels,
+            lsx_ima_samples_in((size_t)qwDataLength, (size_t)ft->signal.channels,
                          (size_t)wav->blockAlign, (size_t)wav->samplesPerBlock);
-        lsx_debug_more("datalen %d, numSamples %lu",dwDataLength, (unsigned long)wav->numSamples);
+        lsx_debug_more("datalen %ld, numSamples %lu",qwDataLength, (unsigned long)wav->numSamples);
         wav->blockSamplesRemaining = 0;        /* Samples left in buffer */
         lsx_ima_init_table();
         ft->signal.length = wav->numSamples*ft->signal.channels;
@@ -839,13 +891,13 @@
         break;
 
     case WAVE_FORMAT_GSM610:
-        wav->numSamples = ((dwDataLength / wav->blockAlign) * wav->samplesPerBlock);
+        wav->numSamples = ((qwDataLength / wav->blockAlign) * wav->samplesPerBlock);
         wavgsminit(ft);
         ft->signal.length = wav->numSamples*ft->signal.channels;
         break;
 
     default:
-        wav->numSamples = div_bits(dwDataLength, ft->encoding.bits_per_sample) / ft->signal.channels;
+        wav->numSamples = div_bits(qwDataLength, ft->encoding.bits_per_sample) / ft->signal.channels;
         ft->signal.length = wav->numSamples * ft->signal.channels;
     }
      
@@ -858,8 +910,8 @@
     lsx_debug("Reading Wave file: %s format, %d channel%s, %d samp/sec",
            wav_format_str(wav->formatTag), ft->signal.channels,
            wChannels == 1 ? "" : "s", dwSamplesPerSecond);
-    lsx_debug("        %d byte/sec, %d block align, %d bits/samp, %u data bytes",
-           dwAvgBytesPerSec, wav->blockAlign, wBitsPerSample, dwDataLength);
+    lsx_debug("        %d byte/sec, %d block align, %d bits/samp, %lu data bytes",
+           dwAvgBytesPerSec, wav->blockAlign, wBitsPerSample, qwDataLength);
 
     /* Can also report extended fmt information */
     switch (wav->formatTag)
@@ -924,8 +976,10 @@
                 }
                 else
                 {
-                    if (lsx_readdw(ft,&len) == SOX_EOF)
+                    uint32_t len_tmp;
+                    if (lsx_readdw(ft,&len_tmp) == SOX_EOF)
                         break;
+                    len = len_tmp;
                     if (strncmp(magic,"ICRD",(size_t)4) == 0)
                     {
                         lsx_debug("Chunk ICRD");
@@ -981,7 +1035,7 @@
                     }
                     else
                     {
-                        lsx_debug("Attempting to seek beyond unsupported chunk `%c%c%c%c' of length %d bytes", magic[0], magic[1], magic[2], magic[3], len);
+                        lsx_debug("Attempting to seek beyond unsupported chunk `%c%c%c%c' of length %ld bytes", magic[0], magic[1], magic[2], magic[3], len);
                         len = (len + 1) & ~1u;
                         lsx_seeki(ft, (off_t)len, SEEK_CUR);
                     }