shithub: sox

Download patch

ref: 2d3823c23c8d5f5dcb05abfc071ecc781e011b7b
parent: 6698585c2cd75b34b5ffbce7bb87adaf6d8ac061
author: Ulrich Klauer <ulrich@chirlu.de>
date: Wed Oct 5 22:21:02 EDT 2011

Add support for floating point encodings in AIFF-C files

Both reading and writing AIFF-C files with 32-bit or 64-bit floating
point samples is enabled (compression types "fl32"/"FL32" and
"fl64"/"FL64").

This resolves feature request #3418119.

--- a/ChangeLog
+++ b/ChangeLog
@@ -17,6 +17,7 @@
 File formats:
 
   o Mention in man pages that WAV files support floating point encodings.
+  o Add support for floating point encodings in AIFF-C files. (Ulrich Klauer)
 
 Audio device drivers:
 
--- a/src/aifc-fmt.c
+++ b/src/aifc-fmt.c
@@ -22,9 +22,11 @@
 {
   static char const * const names[] = {"aifc", "aiffc", NULL};
   static unsigned const write_encodings[] = {
-    SOX_ENCODING_SIGN2, 32, 24, 16, 8, 0, 0};
+    SOX_ENCODING_SIGN2, 32, 24, 16, 8, 0,
+    SOX_ENCODING_FLOAT, 32, 64, 0,
+    0};
   static sox_format_handler_t const sox_aifc_format = {SOX_LIB_VERSION_CODE,
-    "AIFF-C (not compressed, linear), defined in DAVIC 1.4 Part 9 Annex B",
+    "AIFF-C (not compressed), defined in DAVIC 1.4 Part 9 Annex B",
     names, SOX_FILE_BIG_END,
     lsx_aiffstartread, lsx_rawread, lsx_aiffstopread,
     lsx_aifcstartwrite, lsx_rawwrite, lsx_aifcstopwrite,
--- a/src/aiff.c
+++ b/src/aiff.c
@@ -41,6 +41,7 @@
   uint32_t totalsize;
   uint32_t chunksize;
   unsigned short channels = 0;
+  sox_encoding_t enc = SOX_ENCODING_SIGN2;
   uint32_t frames;
   unsigned short bits = 0;
   double rate = 0.0;
@@ -111,10 +112,28 @@
           /* Need to endian swap all the data */
           is_sowt = 1;
         }
+        else if (strncmp(buf, "fl32", (size_t)4) == 0 ||
+            strncmp(buf, "FL32", (size_t)4) == 0) {
+          enc = SOX_ENCODING_FLOAT;
+          if (bits != 32) {
+            lsx_fail_errno(ft, SOX_EHDR,
+              "Sample size of %u is not consistent with `fl32' compression type", bits);
+            return SOX_EOF;
+          }
+        }
+        else if (strncmp(buf, "fl64", (size_t)4) == 0 ||
+            strncmp(buf, "FL64", (size_t)4) == 0) {
+          enc = SOX_ENCODING_FLOAT;
+          if (bits != 64) {
+            lsx_fail_errno(ft, SOX_EHDR,
+              "Sample size of %u is not consistent with `fl64' compression type", bits);
+            return SOX_EOF;
+          }
+        }
         else if (strncmp(buf, "NONE", (size_t)4) != 0 &&
             strncmp(buf, "twos", (size_t)4) != 0) {
           buf[4] = 0;
-          lsx_fail_errno(ft,SOX_EHDR,"AIFC files that contain compressed data are not supported: %s",buf);
+          lsx_fail_errno(ft, SOX_EHDR, "Unsupported AIFC compression type `%s'", buf);
           return(SOX_EOF);
         }
       }
@@ -331,13 +350,11 @@
   }
 
   if (foundcomm) {
-    if (ft->encoding.encoding != SOX_ENCODING_UNKNOWN && ft->encoding.encoding != SOX_ENCODING_SIGN2)
-      lsx_report("AIFF only supports signed data.  Forcing to signed.");
-    ft->encoding.encoding = SOX_ENCODING_SIGN2;
     if      (bits <=  8) bits = 8;
     else if (bits <= 16) bits = 16;
     else if (bits <= 24) bits = 24;
     else if (bits <= 32) bits = 32;
+    else if (bits == 64 && enc == SOX_ENCODING_FLOAT) /* no-op */;
     else {
       lsx_fail_errno(ft,SOX_EFMT,"unsupported sample size in AIFF header: %d", bits);
       return(SOX_EOF);
@@ -408,7 +425,7 @@
   reportInstrument(ft);
 
   return lsx_check_read_params(
-      ft, channels, rate, SOX_ENCODING_SIGN2, bits, (uint64_t)ssndsize, sox_false);
+      ft, channels, rate, enc, bits, (uint64_t)ssndsize, sox_false);
 }
 
 /* print out the MIDI key allocations, loop points, directions etc */
@@ -807,11 +824,11 @@
 
 static int aifcwriteheader(sox_format_t * ft, uint64_t nframes)
 {
-        unsigned hsize =
-                12 /*FVER*/ + 8 /*COMM hdr*/ + 18+4+1+15 /*COMM chunk*/ +
-                8 /*SSND hdr*/ + 12 /*SSND chunk*/;
+        unsigned hsize;
         unsigned bits = 0;
         uint64_t size;
+        char *ctype = NULL, *cname = NULL;
+        unsigned cname_len = 0, comm_len = 0, comm_padding = 0;
 
         if (ft->encoding.encoding == SOX_ENCODING_SIGN2 &&
             ft->encoding.bits_per_sample == 8)
@@ -825,6 +842,12 @@
         else if (ft->encoding.encoding == SOX_ENCODING_SIGN2 &&
                  ft->encoding.bits_per_sample == 32)
                 bits = 32;
+        else if (ft->encoding.encoding == SOX_ENCODING_FLOAT &&
+                 ft->encoding.bits_per_sample == 32)
+                bits = 32;
+        else if (ft->encoding.encoding == SOX_ENCODING_FLOAT &&
+                 ft->encoding.bits_per_sample == 64)
+                bits = 64;
         else
         {
                 lsx_fail_errno(ft,SOX_EFMT,"unsupported output encoding/size for AIFC header");
@@ -831,6 +854,31 @@
                 return(SOX_EOF);
         }
 
+        /* calculate length of COMM chunk (without header) */
+        switch (ft->encoding.encoding) {
+          case SOX_ENCODING_SIGN2:
+            ctype = "NONE";
+            cname = "not compressed";
+            break;
+          case SOX_ENCODING_FLOAT:
+            if (bits == 32) {
+              ctype = "fl32";
+              cname = "32-bit floating point";
+            } else {
+              ctype = "fl64";
+              cname = "64-bit floating point";
+            }
+            break;
+          default: /* can't happen */
+            break;
+        }
+        cname_len = strlen(cname);
+        comm_len = 18+4+1+cname_len;
+        comm_padding = comm_len%2;
+
+        hsize = 12 /*FVER*/ + 8 /*COMM hdr*/ + comm_len+comm_padding /*COMM chunk*/ +
+                8 /*SSND hdr*/ + 12 /*SSND chunk*/;
+
         lsx_writes(ft, "FORM"); /* IFF header */
         /* file size */
         size = hsize + nframes * (ft->encoding.bits_per_sample >> 3) * ft->signal.channels;
@@ -849,16 +897,17 @@
 
         /* COMM chunk -- describes encoding (and #frames) */
         lsx_writes(ft, "COMM");
-        lsx_writedw(ft, 18+4+1+15); /* COMM chunk size */
+        lsx_writedw(ft, comm_len+comm_padding); /* COMM chunk size */
         lsx_writew(ft, ft->signal.channels); /* nchannels */
         lsx_writedw(ft, (unsigned) nframes); /* number of frames */
         lsx_writew(ft, bits); /* sample width, in bits */
         write_ieee_extended(ft, (double)ft->signal.rate);
 
-        lsx_writes(ft, "NONE"); /*compression_type*/
-        lsx_writeb(ft, 14);
-        lsx_writes(ft, "not compressed");
-        lsx_writeb(ft, 0);
+        lsx_writes(ft, ctype); /*compression_type*/
+        lsx_writeb(ft, cname_len);
+        lsx_writes(ft, cname);
+        if (comm_padding)
+          lsx_writeb(ft, 0);
 
         /* SSND chunk -- describes data */
         lsx_writes(ft, "SSND");