shithub: sox

Download patch

ref: 7eab17120ab538b8ae0f02b719fd279e1bfc7861
parent: 60b5e4f356dfc3b16880c0aeebf684c3cc41a5ca
author: robs <robs>
date: Sat May 5 05:44:30 EDT 2007

Add AMR-NB file format

--- a/AUTHORS
+++ b/AUTHORS
@@ -89,7 +89,7 @@
 		format support, libao playback, Secret Rabbit Code
 		resampling; many fixes and much cleanup.
 	Rob Sykes		robs@users.sourceforge.net
-                Formats: M3U, PLS, FLAC, AMR-WB, 24bit support for popular
+                Formats: M3U, PLS, FLAC, AMR, 24bit support for popular
                 formats.
 		Effects: pad, bass, treble, new flanger, soft-knee companding,
                 speed via resampling, filters makeover inc. gnuplot & octave
--- a/ChangeLog
+++ b/ChangeLog
@@ -11,7 +11,7 @@
 
   o Added support for ADPCM-encoded PRC files, based on Danny Smith's
     rec2wav and sndcmp.
-  o Added AMR-WB format.  (robs)
+  o Added AMR-NB [FR# 728875] & AMR-WB formats (with external libs).  (robs)
   o Added M3U & PLS playlist formats [FR# 1667341] (Note: SHOUTcast PLS
     is only partially supported).  (robs)
   o Added libao support.  (Reuben Thomas)
@@ -47,6 +47,7 @@
    (Bug# 1666599) (cbagwell)
   o Fix I/O performance regression in 13.0.0.  (Reuben Thomas)
   o Fix displayed times when playing a file and using trim.  (robs)
+  o Fix CDDA sector duration display for non-CDDA sample rates.  (robs)
 
   Internal improvements:
 
--- a/configure.ac
+++ b/configure.ac
@@ -256,7 +256,7 @@
 if test "$with_amr_wb" != "no"; then
     using_amr_wb=yes
     AC_CHECK_HEADER(amrwb/dec.h,
-        [AC_CHECK_LIB(amrwb, D_MAIN_decode,,using_amr_wb=no)],
+        [AC_CHECK_LIB(amrwb, D_IF_init,,using_amr_wb=no)],
         using_amr_wb=no)
     if test "$with_amr_wb" = "yes" -a "$using_amr_wb" = "no"; then
         AC_MSG_FAILURE([cannot find amr-wb])
@@ -264,6 +264,22 @@
 fi
 AM_CONDITIONAL(HAVE_AMR_WB, test x$using_amr_wb = xyes)
 
+dnl Check for amr-nb libraries
+AC_ARG_WITH(amr-nb,
+    AC_HELP_STRING([--without-amr-nb],
+        [Don't try to use amr-nb]),
+        [with_amr_nb=$withval])
+if test "$with_amr_nb" != "no"; then
+    using_amr_nb=yes
+    AC_CHECK_HEADER(amrnb/sp_dec.h,
+        [AC_CHECK_LIB(amrnb, Decoder_Interface_init,,using_amr_nb=no)],
+        using_amr_nb=no)
+    if test "$with_amr_nb" = "yes" -a "$using_amr_nb" = "no"; then
+        AC_MSG_FAILURE([cannot find amr-nb])
+    fi
+fi
+AM_CONDITIONAL(HAVE_AMR_NB, test x$using_amr_nb = xyes)
+
 dnl Generate output files.
 AX_CREATE_STDINT_H(src/soxstdint.h)
 AC_CONFIG_FILES(Makefile src/Makefile src/libgsm/Makefile lpc10/Makefile)
@@ -292,6 +308,7 @@
 echo "LAME MP3 writer................... $using_lame"
 echo "Secret Rabbit Code resampling..... $using_samplerate"
 echo "AMR-WB format..................... $using_amr_wb"
+echo "AMR-NB format..................... $using_amr_nb"
 echo
 echo "Configure finished.  Do 'make && make install' to compile and install SoX."
 echo
--- a/soxformat.7
+++ b/soxformat.7
@@ -33,10 +33,10 @@
 .SH NAME
 SoX \- Sound eXchange, the Swiss Army knife of audio manipulation
 .SH DESCRIPTION
-File types can be set by the filename extension or the
-.B \-t
-option (see above). File types that can be determined by a filename
-extension are listed with their names preceded by a dot. File types
+File types that can be determined by a filename
+extension are listed with their names preceded by a dot.
+.SP
+File types
 that require an external library, such as ffmpeg or libsndfile, are
 marked e.g. `\fB(ffmpeg)\fR'. File types that can be handled by an
 external library via its pseudo file type (currently libsndfile or
@@ -102,9 +102,28 @@
 	sox infile -t alsa default
 .EE
 .TP
+\&\fB.amr\-nb\fR
+Adaptive Multi Rate\*mNarrow Band speech codec; a lossy format used in 3rd
+generation mobile telephony and defined in 3GPP TS 26.071 et al.
+.SP
+AMR-WB audio has a fixed sampling rate of 8 kHz and supports encoding
+to the following bit-rates (as selected by the
+.B \-C
+option): 0 = 4\*d75 kbit/s, 1 = 5\*d15 kbit/s, 2 = 5\*d9 kbit/s, 3 =
+6\*d7 kbit/s, 4 = 7\*d4 kbit/s 5 = 7\*d95 kbit/s, 6 = 10\*d2
+kbit/s, 7 = 12\*d2 kbit/s.
+.SP
+This format in SoX is optional and requires access to external libraries.
+To see if there is support for this format, enter
+.EX
+	sox -h
+.EE
+and look for it under the list:
+.IR "SUPPORTED FILE FORMATS" .
+.TP
 \&\fB.amr\-wb\fR
-Adaptive Multi Rate\*mWideband speech codec; a lossy format used in 3rd
-generation mobile telephony and defined in 3GPP TS 26.173.
+Adaptive Multi Rate\*mWide Band speech codec; a lossy format used in 3rd
+generation mobile telephony and defined in 3GPP TS 26.171 et al.
 .SP
 AMR-WB audio has a fixed sampling rate of 16 kHz and supports encoding
 to the following bit-rates (as selected by the
@@ -111,7 +130,15 @@
 .B \-C
 option): 0 = 6\*d6 kbit/s, 1 = 8\*d85 kbit/s, 2 = 12\*d65 kbit/s, 3 =
 14\*d25 kbit/s, 4 = 15\*d85 kbit/s 5 = 18\*d25 kbit/s, 6 = 19\*d85
-kbit/s, 7 = 23\*d05 kbit/s, 8 = 23\*d85 kbit/s
+kbit/s, 7 = 23\*d05 kbit/s, 8 = 23\*d85 kbit/s.
+.SP
+This format in SoX is optional and requires access to external libraries.
+To see if there is support for this format on your system, enter
+.EX
+	sox -h
+.EE
+and look for it under the list:
+.IR "SUPPORTED FILE FORMATS" .
 .TP
 .B ao
 libao device driver.
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -128,6 +128,11 @@
 libsox_fmt_amr_wb_la_LIBADD = libsox.la
 pkglib_LTLIBRARIES += libsox_fmt_amr_wb.la
 endif
+if HAVE_AMR_NB
+libsox_fmt_amr_nb_la_SOURCES = amr-nb.c
+libsox_fmt_amr_nb_la_LIBADD = libsox.la
+pkglib_LTLIBRARIES += libsox_fmt_amr_nb.la
+endif
 if HAVE_MP3
 libsox_fmt_mp3_la_SOURCES = mp3.c
 libsox_fmt_mp3_la_LIBADD = libsox.la
--- /dev/null
+++ b/src/amr-nb.c
@@ -1,0 +1,55 @@
+/*
+ * File format: AMR-NB   (c) 2007 robs@users.sourceforge.net
+ *
+ * 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 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,
+ * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+ */
+
+/* In order to use this format with SoX, first build & install:
+ *   http://ftp.penguin.cz/pub/users/utx/amr/amrnb-w.x.y.z.tar.bz2
+ * or install equivalent package(s) e.g. marillat.
+ */
+
+#include "amrnb/typedef.h"
+#include "amrnb/interf_dec.h"
+#include "amrnb/sp_dec.h"
+#define Mode  _Mode
+#define MR102 _MR102
+#define MR122 _MR122
+#define MR475 _MR475
+#define MR515 _MR515
+#define MR59  _MR59
+#define MR67  _MR67
+#define MR74  _MR74
+#define MR795 _MR795
+#define MRDTX _MRDTX
+#include "amrnb/interf_enc.h"
+
+static char const magic[] = "#!AMR\n";
+#define AMR_CODED_MAX       32                  /* max coded size */
+#define AMR_ENCODING        SOX_ENCODING_AMR_NB
+#define AMR_FORMAT_FN       sox_amr_nb_format_fn
+#define AMR_FRAME           160                 /* 20ms @ 8kHz */
+#define AMR_MODE_MAX        7
+#define AMR_NAMES           "amr-nb", "anb"
+#define AMR_PRIV_TOO_BIG    amr_nb_PRIVSIZE_too_big
+#define AMR_RATE            8000
+#define D_IF_decode         Decoder_Interface_Decode
+#define D_IF_exit           Decoder_Interface_exit
+#define D_IF_init           Decoder_Interface_init
+#define E_IF_encode         Encoder_Interface_Encode
+#define E_IF_exit           Encoder_Interface_exit
+#define E_IF_init()         Encoder_Interface_init(1)
+static unsigned block_size[] = {13,14,16,18,20,21,27,32,6,1,1,1,1,1,1,1};
+#include "amr.h"
--- a/src/amr-wb.c
+++ b/src/amr-wb.c
@@ -16,163 +16,23 @@
  * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
  */
 
-/* In order to use this format with SoX, first obtain, build & install:
- *   http://ftp.penguin.cz/pub/users/utx/amr/amrwb-7.0.0.0.tar.bz2
+/* In order to use this format with SoX, first build & install:
+ *   http://ftp.penguin.cz/pub/users/utx/amr/amrwb-w.x.y.z.tar.bz2
+ * or install equivalent package(s) e.g. marillat.
  */
 
-#include "sox_i.h"
 #include "amrwb/typedef.h"
 #include "amrwb/enc_if.h"
 #include "amrwb/dec_if.h"
 #include "amrwb/if_rom.h"
-#include <string.h>
-#include <math.h>
 
 static char const magic[] = "#!AMR-WB\n";
-
-typedef struct amr_wb
-{
-  void * state;
-  Word16 coding_mode;
-  Word16 pcm[L_FRAME16k];
-  sox_size_t pcm_index;
-} * amr_wb_t;
-
-assert_static(sizeof(struct amr_wb) <= SOX_MAX_FILE_PRIVSIZE,
-              /* else */ amr_wb_PRIVSIZE_too_big);
-
-static sox_size_t decode_1_frame(ft_t ft)
-{
-  amr_wb_t this = (amr_wb_t) ft->priv;
-  Word16 mode;
-  UWord8 serial[NB_SERIAL_MAX];
-
-  if (fread(serial, sizeof(UWord8), 1, ft->fp) != 1)
-    return L_FRAME16k;
-  mode = (Word16)((serial[0] >> 3) & 0x0F);
-  if (fread(&serial[1], sizeof(UWord8), block_size[mode] - 1, ft->fp) != block_size[mode] - 1)
-    return L_FRAME16k;
-  D_IF_decode(this->state, serial, this->pcm, _good_frame);
-  return 0;
-}
-
-static void encode_1_frame(ft_t ft)
-{
-  amr_wb_t this = (amr_wb_t) ft->priv;
-  UWord8 serial[NB_SERIAL_MAX];
-  Word16 mode = this->coding_mode;
-  Word32 serial_size = E_IF_encode(this->state, mode, this->pcm, serial, 1);
-  fwrite(serial, 1, serial_size, ft->fp);
-}
-
-static void set_format(ft_t ft)
-{
-  ft->signal.rate = 16000;
-  ft->signal.size = SOX_SIZE_16BIT;
-  ft->signal.encoding = SOX_ENCODING_AMR_WB;
-  ft->signal.channels = 1;
-}
-
-static int startread(ft_t ft)
-{
-  amr_wb_t this = (amr_wb_t) ft->priv;
-  char buffer[sizeof(magic)];
-
-  this->pcm_index = L_FRAME16k;
-
-  this->state = D_IF_init();
-
-  fread(buffer, sizeof(char), sizeof(buffer) - 1, ft->fp);
-  buffer[sizeof(buffer) - 1] = 0;
-  if (strcmp(buffer, magic)) {
-    sox_fail("Invalid magic number");
-    return SOX_EOF;
-  }
-  set_format(ft);
-  return SOX_SUCCESS;
-}
-
-static sox_size_t read(ft_t ft, sox_ssample_t * buf, sox_size_t len)
-{
-  amr_wb_t this = (amr_wb_t) ft->priv;
-  sox_size_t done;
-
-  for (done = 0; done < len; done++) {
-    if (this->pcm_index >= L_FRAME16k)
-      this->pcm_index = decode_1_frame(ft);
-    if (this->pcm_index >= L_FRAME16k)
-      break;
-    *buf++ = SOX_SIGNED_16BIT_TO_SAMPLE(this->pcm[this->pcm_index++], ft->clips);
-  }
-  return done;
-}
-
-static int stopread(ft_t ft)
-{
-  amr_wb_t this = (amr_wb_t) ft->priv;
-  D_IF_exit(this->state);
-  return SOX_SUCCESS;
-}
-
-static int startwrite(ft_t ft)
-{
-  amr_wb_t this = (amr_wb_t) ft->priv;
-
-  if (ft->signal.compression != HUGE_VAL) {
-    this->coding_mode = ft->signal.compression;
-    if (this->coding_mode != ft->signal.compression || this->coding_mode > 8) {
-      sox_fail_errno(ft, SOX_EINVAL, "compression level must be a whole number from 0 to 8");
-      return SOX_EOF;
-    }
-  }
-  else this->coding_mode = 0;
-
-  set_format(ft);
-  this->state = E_IF_init();
-  sox_writes(ft, magic);
-  this->pcm_index = 0;
-  return SOX_SUCCESS;
-}
-
-static sox_size_t write(ft_t ft, const sox_ssample_t * buf, sox_size_t len)
-{
-  amr_wb_t this = (amr_wb_t) ft->priv;
-  sox_size_t done;
-
-  for (done = 0; done < len; ++done) {
-    this->pcm[this->pcm_index++] = (Word16) (SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips));
-    if (this->pcm_index == L_FRAME16k) {
-      this->pcm_index = 0;
-      encode_1_frame(ft);
-    }
-  }
-  return done;
-}
-
-static int stopwrite(ft_t ft)
-{
-  amr_wb_t this = (amr_wb_t) ft->priv;
-
-  if (this->pcm_index) {
-    do {
-      this->pcm[this->pcm_index++] = 0;
-    } while (this->pcm_index < L_FRAME16k);
-    encode_1_frame(ft);
-  }
-  E_IF_exit(this->state);
-  return SOX_SUCCESS;
-}
-
-const sox_format_t *sox_amr_wb_format_fn(void);
-
-const sox_format_t *sox_amr_wb_format_fn(void)
-{
-  static char const * names[] = {"amr-wb", "awb", NULL};
-  static sox_format_t driver = {
-    names, 0,
-    startread, read, stopread,
-    startwrite, write, stopwrite,
-    sox_format_nothing_seek
-  };
-  return &driver;
-}
+#define AMR_CODED_MAX       NB_SERIAL_MAX
+#define AMR_ENCODING        SOX_ENCODING_AMR_WB
+#define AMR_FORMAT_FN       sox_amr_wb_format_fn
+#define AMR_FRAME           L_FRAME16k
+#define AMR_MODE_MAX        8
+#define AMR_NAMES           "amr-wb", "awb"
+#define AMR_PRIV_TOO_BIG    amr_wb_PRIVSIZE_too_big
+#define AMR_RATE            16000
+#include "amr.h"
--- /dev/null
+++ b/src/amr.h
@@ -1,0 +1,165 @@
+/*
+ * File format: AMR   (c) 2007 robs@users.sourceforge.net
+ *
+ * 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 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,
+ * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+ */
+
+#include "sox_i.h"
+#include <string.h>
+#include <math.h>
+
+typedef struct amr
+{
+  void * state;
+  unsigned mode;
+  short pcm[AMR_FRAME];
+  sox_size_t pcm_index;
+} * amr_t;
+
+assert_static(sizeof(struct amr) <= SOX_MAX_FILE_PRIVSIZE, AMR_PRIV_TOO_BIG);
+
+static sox_size_t decode_1_frame(ft_t ft)
+{
+  amr_t amr = (amr_t) ft->priv;
+  int block_size_1;
+  unsigned char coded[AMR_CODED_MAX];
+
+  if (fread(coded, sizeof(coded[0]), 1, ft->fp) != 1)
+    return AMR_FRAME;
+  block_size_1 = block_size[(coded[0] >> 3) & 0x0F] - 1;
+  if (fread(&coded[1], sizeof(coded[1]), block_size_1, ft->fp) != block_size_1)
+    return AMR_FRAME;
+  D_IF_decode(amr->state, coded, amr->pcm, 0);
+  return 0;
+}
+
+static void encode_1_frame(ft_t ft)
+{
+  amr_t amr = (amr_t) ft->priv;
+  unsigned char coded[AMR_CODED_MAX];
+  fwrite(coded, 1, (unsigned)E_IF_encode(amr->state, amr->mode, amr->pcm, coded, 1), ft->fp);
+}
+
+static void set_format(ft_t ft)
+{
+  ft->signal.rate = AMR_RATE;
+  ft->signal.size = SOX_SIZE_16BIT;
+  ft->signal.encoding = AMR_ENCODING;
+  ft->signal.channels = 1;
+}
+
+static int startread(ft_t ft)
+{
+  amr_t amr = (amr_t) ft->priv;
+  char buffer[sizeof(magic)];
+
+  amr->pcm_index = AMR_FRAME;
+
+  amr->state = D_IF_init();
+
+  fread(buffer, sizeof(char), sizeof(buffer) - 1, ft->fp);
+  buffer[sizeof(buffer) - 1] = 0;
+  if (strcmp(buffer, magic)) {
+    sox_fail("Invalid magic number");
+    return SOX_EOF;
+  }
+  set_format(ft);
+  return SOX_SUCCESS;
+}
+
+static sox_size_t read(ft_t ft, sox_ssample_t * buf, sox_size_t len)
+{
+  amr_t amr = (amr_t) ft->priv;
+  sox_size_t done;
+
+  for (done = 0; done < len; done++) {
+    if (amr->pcm_index >= AMR_FRAME)
+      amr->pcm_index = decode_1_frame(ft);
+    if (amr->pcm_index >= AMR_FRAME)
+      break;
+    *buf++ = SOX_SIGNED_16BIT_TO_SAMPLE(amr->pcm[amr->pcm_index++], ft->clips);
+  }
+  return done;
+}
+
+static int stopread(ft_t ft)
+{
+  amr_t amr = (amr_t) ft->priv;
+  D_IF_exit(amr->state);
+  return SOX_SUCCESS;
+}
+
+static int startwrite(ft_t ft)
+{
+  amr_t amr = (amr_t) ft->priv;
+
+  if (ft->signal.compression != HUGE_VAL) {
+    amr->mode = ft->signal.compression;
+    if (amr->mode != ft->signal.compression || amr->mode > AMR_MODE_MAX) {
+      sox_fail_errno(ft, SOX_EINVAL, "compression level must be a whole number from 0 to %i", AMR_MODE_MAX);
+      return SOX_EOF;
+    }
+  }
+  else amr->mode = 0;
+
+  set_format(ft);
+  amr->state = E_IF_init();
+  sox_writes(ft, magic);
+  amr->pcm_index = 0;
+  return SOX_SUCCESS;
+}
+
+static sox_size_t write(ft_t ft, const sox_ssample_t * buf, sox_size_t len)
+{
+  amr_t amr = (amr_t) ft->priv;
+  sox_size_t done;
+
+  for (done = 0; done < len; ++done) {
+    amr->pcm[amr->pcm_index++] = SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips);
+    if (amr->pcm_index == AMR_FRAME) {
+      amr->pcm_index = 0;
+      encode_1_frame(ft);
+    }
+  }
+  return done;
+}
+
+static int stopwrite(ft_t ft)
+{
+  amr_t amr = (amr_t) ft->priv;
+
+  if (amr->pcm_index) {
+    do {
+      amr->pcm[amr->pcm_index++] = 0;
+    } while (amr->pcm_index < AMR_FRAME);
+    encode_1_frame(ft);
+  }
+  E_IF_exit(amr->state);
+  return SOX_SUCCESS;
+}
+
+const sox_format_t *AMR_FORMAT_FN(void);
+
+const sox_format_t *AMR_FORMAT_FN(void)
+{
+  static char const * names[] = {AMR_NAMES, NULL};
+  static sox_format_t driver = {
+    names, 0,
+    startread, read, stopread,
+    startwrite, write, stopwrite,
+    sox_format_nothing_seek
+  };
+  return &driver;
+}
--- a/src/auto.c
+++ b/src/auto.c
@@ -114,6 +114,13 @@
                      (memcmp(header, "XAJ\0", 4) == 0) ||
                      (memcmp(header, "XA\0\0", 4) == 0))
                 type = "xa";
+            else if (strncmp(header, "#!AM", 4) == 0) {
+              rc = sox_readbuf(ft, header, 5);
+              if (rc >= 2 && strncmp(header, "R\n", 2) == 0)
+                type = "amr-nb";
+              else if (rc >= 5 && strncmp(header, "R-WB\n", 5) == 0)
+                type = "amr-wb";
+            }
         } /* read 4-byte header */
 
         /* If we didn't find type yet then start looking for file
--- a/src/misc.c
+++ b/src/misc.c
@@ -73,6 +73,7 @@
         "Vorbis",
         "FLAC",
         "AMR-WB",
+        "AMR-NB",
 };
 
 assert_static(array_length(sox_encodings_str) == SOX_ENCODINGS,
--- a/src/sox.h
+++ b/src/sox.h
@@ -181,6 +181,7 @@
   SOX_ENCODING_VORBIS    , /* Vorbis compression */
   SOX_ENCODING_FLAC      , /* FLAC compression */
   SOX_ENCODING_AMR_WB    , /* AMR-WB compression */
+  SOX_ENCODING_AMR_NB    , /* AMR-NB compression */
 
   SOX_ENCODINGS            /* End of list marker */
 } sox_encoding_t;