shithub: sox

Download patch

ref: e95bf6acc5d699b6d86fa89d817f9aa9af7f8c42
parent: abde517163c601b461c47533fef2e35a2dbac535
author: robs <robs>
date: Fri Mar 21 18:22:22 EDT 2008

initial support for wavpack

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -86,6 +86,7 @@
 endif (NOT HAVE_SYS_SOUNDCARD_H)
 optional3(HAVE_OGG_VORBIS vorbis/codec.h vorbis vorbis_analysis_headerout vorbisfile ov_clear vorbisenc vorbis_encode_init_vbr vorbis)
 optional3(HAVE_FFMPEG ffmpeg/avformat.h avformat av_open_input_file avutil av_rescale_q avcodec avcodec_decode_audio2 ffmpeg)
+optional(HAVE_WAVPACK wavpack/wavpack.h wavpack WavpackGetSampleRate wavpack)
 
 subdirs(src lpc10)
 
--- a/configure.ac
+++ b/configure.ac
@@ -394,6 +394,26 @@
 AM_CONDITIONAL(HAVE_AMRNB, test x$using_amr_nb = xyes)
 AC_SUBST(AMR_NB_LIBS)
 
+dnl Check for wavpack libraries
+AC_ARG_WITH(wavpack,
+    AC_HELP_STRING([--without-wavpack],
+        [Don't try to use wavpack]))
+using_wavpack=no
+if test "$with_wavpack" != "no"; then
+    using_wavpack=yes
+    AC_CHECK_HEADER(wavpack/wavpack.h,
+        [AC_CHECK_LIB(wavpack, WavpackGetSampleRate, WAVPACK_LIBS="$WAVPACK_LIBS -lwavpack" ,using_wavpack=no)],
+        using_wavpack=no)
+    if test "$with_wavpack" = "yes" -a "$using_wavpack" = "no"; then
+        AC_MSG_FAILURE([cannot find wavpack])
+    fi
+fi
+if test "$using_wavpack" = yes; then
+   AC_DEFINE(HAVE_WAVPACK, 1, [Define to 1 if you have WavPack.])
+fi
+AM_CONDITIONAL(HAVE_WAVPACK, test x$using_wavpack = xyes)
+AC_SUBST(WAVPACK_LIBS)
+
 dnl Test for libsamplerate.
 AC_ARG_WITH(samplerate,
     AC_HELP_STRING([--without-samplerate],
@@ -461,6 +481,7 @@
 echo "LAME MP3 writer................... $using_lame"
 echo "AMR-WB format..................... $using_amr_wb"
 echo "AMR-NB format..................... $using_amr_nb"
+echo "WavPack format.................... $using_wavpack"
 echo "LADSPA effects.................... $using_ladspa"
 echo "Secret Rabbit Code resampling..... $using_samplerate"
 echo "pkg-config location............... $pkgconfig_option"
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -153,6 +153,11 @@
 libsox_fmt_amr_nb_la_LIBADD = libsox.la @AMR_NB_LIBS@
 pkglib_LTLIBRARIES += libsox_fmt_amr_nb.la
 endif
+if HAVE_WAVPACK
+libsox_fmt_wavpack_la_SOURCES = wavpack.c
+libsox_fmt_wavpack_la_LIBADD = libsox.la @WAVPACK_LIBS@
+pkglib_LTLIBRARIES += libsox_fmt_wavpack.la
+endif
 if HAVE_MP3
 libsox_fmt_mp3_la_SOURCES = mp3.c mp3-duration.h
 libsox_fmt_mp3_la_LIBADD = libsox.la @MP3_LIBS@
@@ -254,6 +259,11 @@
     libsox_la_SOURCES += amr-nb.c amr.h amr1.h amr2.h
     libsox_la_LIBADD += @AMR_NB_LIBS@
     sox_LDADD += @AMR_NB_LIBS@
+endif
+if HAVE_WAVPACK
+    libsox_la_SOURCES += wavpack.c
+    libsox_la_LIBADD += @WAVPACK_LIBS@
+    sox_LDADD += @WAVPACK_LIBS@
 endif
 if HAVE_MP3
     libsox_la_SOURCES += mp3.c mp3-duration.h
--- a/src/formats.c
+++ b/src/formats.c
@@ -43,6 +43,8 @@
   "Floating Point (text) PCM",
   "FLAC",
   "HCOM",
+  "WavPack",
+  "Floating Point WavPack",
   "", /* Lossless above, lossy below */
   "u-law",
   "A-law",
@@ -73,6 +75,8 @@
   "F.P. PCM",
   "FLAC",
   "HCOM",
+  "WavPack",
+  "F.P. WavPack",
   "", /* Lossless above, lossy below */
   "u-law",
   "A-law",
@@ -129,6 +133,7 @@
   MAGIC(flac  , 0, 0, ""     , 0,  4, "fLaC")
   MAGIC(avr   , 0, 0, ""     , 0,  4, "2BIT")
   MAGIC(caf   , 0, 0, ""     , 0,  4, "caff")
+  MAGIC(wv    , 0, 0, ""     , 0,  4, "wvpk")
   MAGIC(paf   , 0, 0, ""     , 0,  4, " paf")
   MAGIC(sf    , 0, 0, ""     , 0,  4, "\144\243\001\0")
   MAGIC(sf    , 0, 0, ""     , 0,  4, "\0\001\243\144")
@@ -148,7 +153,8 @@
 {
   switch (encoding) {
     case SOX_ENCODING_HCOM:       return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 1? bits_per_sample: 0;
-    case SOX_ENCODING_FLAC:       return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 3? bits_per_sample: 0;
+    case SOX_ENCODING_WAVPACK:
+    case SOX_ENCODING_FLAC:       return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 4? bits_per_sample: 0;
     case SOX_ENCODING_SIGN2:      return bits_per_sample <= 32? bits_per_sample : 0;
     case SOX_ENCODING_UNSIGNED:   return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 4? bits_per_sample: 0;
 
@@ -172,6 +178,7 @@
     case SOX_ENCODING_AMR_NB:
     case SOX_ENCODING_LPC10:      return !bits_per_sample? 16: 0;
 
+    case SOX_ENCODING_WAVPACKF:
     case SOX_ENCODING_FLOAT:      return bits_per_sample == 32 ? 24: bits_per_sample == 64 ? 53: 0;
     case SOX_ENCODING_FLOAT_TEXT: return !bits_per_sample? 53: 0;
 
@@ -435,8 +442,10 @@
       }
     }
     ft->handler = *handler;
-    if (ft->handler.flags & SOX_FILE_NOSTDIO)
+    if (ft->handler.flags & SOX_FILE_NOSTDIO) {
       fclose(ft->fp);
+      ft->fp = NULL;
+    }
   }
   if (!ft->handler.startread && !ft->handler.read) {
     sox_fail("file type `%s' isn't readable", filetype);
--- a/src/formats.h
+++ b/src/formats.h
@@ -75,3 +75,6 @@
 #if defined HAVE_OGG_VORBIS
   FORMAT(vorbis)
 #endif
+#if defined HAVE_WAVPACK
+  FORMAT(wavpack)
+#endif
--- a/src/formats_i.c
+++ b/src/formats_i.c
@@ -179,6 +179,11 @@
   clearerr(ft->fp);
 }
 
+int lsx_unreadb(sox_format_t * ft, unsigned b)
+{
+  return ungetc((int)b, ft->fp);
+}
+
 /* Implements traditional fseek() behavior.  Meant to abstract out
  * file operations so that they could one day also work on memory
  * buffers.
--- a/src/sox.h
+++ b/src/sox.h
@@ -209,6 +209,8 @@
   SOX_ENCODING_FLOAT_TEXT, /* floating point (text format) */
   SOX_ENCODING_FLAC      , /* FLAC compression */
   SOX_ENCODING_HCOM      , /*  */
+  SOX_ENCODING_WAVPACK   , /*  */
+  SOX_ENCODING_WAVPACKF  , /*  */
 
   SOX_ENCODING_LOSSLESS  , /* Lossless above, lossy below */
 
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -79,47 +79,48 @@
 void lsx_set_signal_defaults(sox_signalinfo_t * signal);
 #define lsx_writechars(ft, chars, len) (lsx_writebuf(ft, chars, len) == len? SOX_SUCCESS : SOX_EOF)
 
-sox_size_t lsx_read_b_buf(sox_format_t * ft, uint8_t *buf, sox_size_t len);
-sox_size_t lsx_read_w_buf(sox_format_t * ft, uint16_t *buf, sox_size_t len);
 sox_size_t lsx_read_3_buf(sox_format_t * ft, uint24_t *buf, sox_size_t len);
+sox_size_t lsx_read_b_buf(sox_format_t * ft, uint8_t *buf, sox_size_t len);
+sox_size_t lsx_read_df_buf(sox_format_t * ft, double *buf, sox_size_t len);
 sox_size_t lsx_read_dw_buf(sox_format_t * ft, uint32_t *buf, sox_size_t len);
 sox_size_t lsx_read_f_buf(sox_format_t * ft, float *buf, sox_size_t len);
-sox_size_t lsx_read_df_buf(sox_format_t * ft, double *buf, sox_size_t len);
+sox_size_t lsx_read_w_buf(sox_format_t * ft, uint16_t *buf, sox_size_t len);
 
-sox_size_t lsx_write_b_buf(sox_format_t * ft, uint8_t *buf, sox_size_t len);
-sox_size_t lsx_write_w_buf(sox_format_t * ft, uint16_t *buf, sox_size_t len);
 sox_size_t lsx_write_3_buf(sox_format_t * ft, uint24_t *buf, sox_size_t len);
+sox_size_t lsx_write_b_buf(sox_format_t * ft, uint8_t *buf, sox_size_t len);
+sox_size_t lsx_write_df_buf(sox_format_t * ft, double *buf, sox_size_t len);
 sox_size_t lsx_write_dw_buf(sox_format_t * ft, uint32_t *buf, sox_size_t len);
 sox_size_t lsx_write_f_buf(sox_format_t * ft, float *buf, sox_size_t len);
-sox_size_t lsx_write_df_buf(sox_format_t * ft, double *buf, sox_size_t len);
+sox_size_t lsx_write_w_buf(sox_format_t * ft, uint16_t *buf, sox_size_t len);
 
 int lsx_read3(sox_format_t * ft, uint24_t * u3);
 int lsx_readb(sox_format_t * ft, uint8_t * ub);
+int lsx_readchars(sox_format_t * ft, char * chars, sox_size_t len);
 int lsx_readdf(sox_format_t * ft, double * d);
 int lsx_readdw(sox_format_t * ft, uint32_t * udw);
 int lsx_readf(sox_format_t * ft, float * f);
 int lsx_readw(sox_format_t * ft, uint16_t * uw);
-int lsx_readchars(sox_format_t * ft, char * chars, sox_size_t len);
 
 int lsx_write3(sox_format_t * ft, unsigned u3);
 int lsx_writeb(sox_format_t * ft, unsigned ub);
+int lsx_writedf(sox_format_t * ft, double d);
 int lsx_writedw(sox_format_t * ft, unsigned udw);
 int lsx_writef(sox_format_t * ft, double f);
 int lsx_writesb(sox_format_t * ft, signed);
 int lsx_writesw(sox_format_t * ft, signed);
 int lsx_writew(sox_format_t * ft, unsigned uw);
-int lsx_writedf(sox_format_t * ft, double d);
 
-int lsx_seeki(sox_format_t * ft, sox_ssize_t to_sample, int whence);
-int lsx_offset_seek(sox_format_t * ft, off_t byte_offset, sox_size_t to_sample);
-sox_size_t lsx_filelength(sox_format_t * ft);
-int lsx_flush(sox_format_t * ft);
-sox_ssize_t lsx_tell(sox_format_t * ft);
 int lsx_eof(sox_format_t * ft);
 int lsx_error(sox_format_t * ft);
-void lsx_rewind(sox_format_t * ft);
+int lsx_flush(sox_format_t * ft);
+int lsx_seeki(sox_format_t * ft, sox_ssize_t offset, int whence);
+int lsx_unreadb(sox_format_t * ft, unsigned ub);
+sox_size_t lsx_filelength(sox_format_t * ft);
+sox_ssize_t lsx_tell(sox_format_t * ft);
 void lsx_clearerr(sox_format_t * ft);
+void lsx_rewind(sox_format_t * ft);
 
+int lsx_offset_seek(sox_format_t * ft, off_t byte_offset, sox_size_t to_sample);
 
 void lsx_fail_errno(sox_format_t *, int, const char *, ...)
 #ifdef __GNUC__
@@ -132,8 +133,6 @@
 {
   sox_globals_t * global_info;
 } sox_formats_globals;
-
-
 
 
 
--- a/src/soxconfig.h.cmake
+++ b/src/soxconfig.h.cmake
@@ -34,4 +34,5 @@
 #cmakedefine HAVE_SYS_TIME_H          1
 #cmakedefine HAVE_UNISTD_H            1
 #cmakedefine HAVE_VSNPRINTF           1
+#cmakedefine HAVE_WAVPACK             1
 #cmakedefine WORDS_BIGENDIAN          1
--- a/src/tests.sh
+++ b/src/tests.sh
@@ -124,16 +124,16 @@
 
 do_multichannel_formats () {
   format1=u1
-  convertToAndFrom s1 u1 s2 u2 s3 u3 s4 u4 raw Raw dat au wav aiff aifc flac caf sph
+  convertToAndFrom s1 u1 s2 u2 s3 u3 s4 u4 raw Raw dat au wav aiff aifc flac caf sph wv
 
   format1=s2
-  convertToAndFrom s2 u2 s3 u3 s4 u4 raw Raw dat au wav aiff aifc flac caf sph
+  convertToAndFrom s2 u2 s3 u3 s4 u4 raw Raw dat au wav aiff aifc flac caf sph wv
 
   format1=u3
-  convertToAndFrom s3 u3 s4 u4 raw Raw wav aiff aifc flac sph
+  convertToAndFrom s3 u3 s4 u4 raw Raw wav aiff aifc flac sph wv
 
   format1=s4
-  convertToAndFrom s4 u4 Raw wav aiff aifc caf sph
+  convertToAndFrom s4 u4 Raw wav aiff aifc caf sph wv
 
   format1=al
   convertToAndFrom al s2 u2 s4 raw Raw dat aiff aifc flac caf
@@ -196,6 +196,7 @@
 ${builddir}/sox_sample_test || exit 1
 
 # Don't try to test unsupported stuff
+${bindir}/sox --help|grep "^AUDIO FILE.*\<wv\>">/dev/null || skip="wv $skip"
 ${bindir}/sox --help|grep "^AUDIO FILE.*\<flac\>">/dev/null || skip="flac $skip"
 ${bindir}/sox --help|grep "^AUDIO FILE.*\<caf\>" >/dev/null || skip="caf $skip"
 
--- /dev/null
+++ b/src/wavpack.c
@@ -1,0 +1,181 @@
+/*
+ * File format: WavPack   (c) 2008 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 "wavpack/wavpack.h"
+
+typedef struct {
+  WavpackContext * codec;
+  sox_size_t first_block_size;
+} priv_t;
+
+assert_static(sizeof(priv_t) <= SOX_MAX_FILE_PRIVSIZE, WAVPACK_PRIV_TOO_BIG);
+
+static int32_t ft_read_b_buf(void * ft, void * buf, int32_t len) {
+  return (int32_t)lsx_read_b_buf((sox_format_t *)ft, buf, (sox_size_t)len);} 
+static uint32_t ft_tell(void * ft) {
+  return lsx_tell((sox_format_t *)ft);} 
+static int ft_seek_abs(void * ft, uint32_t offset) {
+  return lsx_seeki((sox_format_t *)ft, (sox_ssize_t)offset, SEEK_SET);} 
+static int ft_seek_rel(void * ft, int32_t offset, int mode) {
+  return lsx_seeki((sox_format_t *)ft, offset, mode);} 
+static int ft_unreadb(void * ft, int b) {
+  return lsx_unreadb((sox_format_t *)ft, (unsigned)b);} 
+static uint32_t ft_filelength(void * ft) {
+  return lsx_filelength((sox_format_t *)ft);} 
+static int ft_is_seekable(void *ft) {
+  return ((sox_format_t *)ft)->seekable;} 
+static int32_t ft_write_b_buf(void * ft, void * buf, int32_t len) {
+  priv_t * p = (priv_t *)((sox_format_t *)ft)->priv;
+  if (!p->first_block_size)
+    p->first_block_size = len;
+  return (int32_t)lsx_write_b_buf((sox_format_t *)ft, buf, (sox_size_t)len);}
+
+static WavpackStreamReader io_fns = {
+  ft_read_b_buf, ft_tell, ft_seek_abs, ft_seek_rel,
+  ft_unreadb, ft_filelength, ft_is_seekable, ft_write_b_buf
+};
+
+static int start_read(sox_format_t * ft)
+{
+  priv_t * p = (priv_t *)ft->priv;
+  char msg[80];
+
+  p->codec = WavpackOpenFileInputEx(&io_fns, ft, NULL, msg, OPEN_NORMALIZE, 0);
+  ft->encoding.bits_per_sample = WavpackGetBytesPerSample(p->codec) << 3;
+  ft->signal.channels   = WavpackGetNumChannels(p->codec);
+  if (WavpackGetSampleRate(p->codec) && ft->signal.rate && ft->signal.rate != WavpackGetSampleRate(p->codec))
+    sox_warn("`%s': overriding sample rate", ft->filename);
+  else ft->signal.rate = WavpackGetSampleRate(p->codec);
+
+  ft->length = WavpackGetNumSamples(p->codec) * ft->signal.channels;
+  ft->encoding.encoding = (WavpackGetMode(p->codec) & MODE_FLOAT)? 
+    SOX_ENCODING_WAVPACKF : SOX_ENCODING_WAVPACK;
+  return SOX_SUCCESS;
+}
+
+static sox_size_t read_samples(sox_format_t * ft, sox_sample_t * buf, sox_size_t len)
+{
+  priv_t * p = (priv_t *)ft->priv;
+  size_t i, actual = WavpackUnpackSamples(p->codec, buf, len / ft->signal.channels) * ft->signal.channels;
+  for (i = 0; i < actual; ++i) switch (ft->encoding.bits_per_sample) {
+    case  8: buf[i] = SOX_SIGNED_8BIT_TO_SAMPLE(buf[i],); break;
+    case 16: buf[i] = SOX_SIGNED_16BIT_TO_SAMPLE(buf[i],); break;
+    case 24: buf[i] = SOX_SIGNED_24BIT_TO_SAMPLE(buf[i],); break;
+    case 32: buf[i] = ft->encoding.encoding == SOX_ENCODING_WAVPACKF?
+      SOX_FLOAT_32BIT_TO_SAMPLE(*(float *)&buf[i], ft->clips) :
+      SOX_SIGNED_32BIT_TO_SAMPLE(buf[i],);
+      break;
+  }
+  return actual;
+}
+
+static int stop_read(sox_format_t * ft)
+{
+  priv_t * p = (priv_t *)ft->priv;
+  WavpackCloseFile(p->codec);
+  return SOX_SUCCESS;
+}
+
+static int start_write(sox_format_t * ft)
+{
+  priv_t * p = (priv_t *)ft->priv;
+  WavpackConfig config;
+
+  p->codec = WavpackOpenFileOutput(ft_write_b_buf, ft, NULL);
+  memset(&config, 0, sizeof(config));
+  config.bytes_per_sample  = ft->encoding.bits_per_sample >> 3;
+  config.bits_per_sample   = ft->encoding.bits_per_sample;
+  config.channel_mask      = ft->signal.channels == 1? 4 :
+      ft->signal.channels == 2? 3 : (1 << ft->signal.channels) - 1;
+  config.num_channels      = ft->signal.channels;
+  config.sample_rate       = (int32_t)(ft->signal.rate + .5);
+  config.flags = CONFIG_VERY_HIGH_FLAG;
+  if (!WavpackSetConfiguration(p->codec, &config, ft->length? ft->length / ft->signal.channels : (uint32_t)-1)) {
+    lsx_fail_errno(ft, SOX_EHDR, WavpackGetErrorMessage(p->codec));
+    return SOX_EOF;
+  }
+  WavpackPackInit(p->codec);
+  return SOX_SUCCESS;
+}
+
+static sox_size_t write_samples(sox_format_t * ft, const sox_sample_t * buf, sox_size_t len)
+{
+  priv_t * p = (priv_t *)ft->priv;
+  size_t i;
+  int32_t * obuf = lsx_malloc(len * sizeof(*obuf));
+  int result;
+
+  for (i = 0; i < len; ++i) switch (ft->encoding.bits_per_sample) {
+    case  8: obuf[i] = SOX_SAMPLE_TO_SIGNED_8BIT(buf[i], ft->clips); break;
+    case 16: obuf[i] = SOX_SAMPLE_TO_SIGNED_16BIT(buf[i], ft->clips); break;
+    case 24: obuf[i] = SOX_SAMPLE_TO_SIGNED_24BIT(buf[i], ft->clips) << 8;
+             obuf[i] >>= 8; break;
+    case 32: obuf[i] = ft->encoding.encoding == SOX_ENCODING_WAVPACKF?
+      SOX_SAMPLE_TO_SIGNED_24BIT(*(float *)&buf[i], ft->clips) :
+      SOX_SAMPLE_TO_SIGNED_32BIT(buf[i], ft->clips);
+      break;
+  }
+  result = WavpackPackSamples(p->codec, obuf, len / ft->signal.channels);
+  free(obuf);
+  return result? len : 0;
+}
+
+static int stop_write(sox_format_t * ft)
+{
+  priv_t * p = (priv_t *)ft->priv;
+  WavpackFlushSamples(p->codec);
+  if (!WavpackFlushSamples(p->codec)) {
+    lsx_fail_errno(ft, SOX_EINVAL, "%s", WavpackGetErrorMessage(p->codec));
+    return SOX_EOF;
+  }
+  if (ft->seekable && WavpackGetNumSamples(p->codec) != WavpackGetSampleIndex(p->codec) && p->first_block_size >= 4) {
+    char * buf = lsx_malloc(p->first_block_size);
+    lsx_rewind(ft);
+    lsx_readchars(ft, buf, p->first_block_size);
+    if (!memcmp(buf, "wvpk", 4))
+      WavpackUpdateNumSamples(p->codec, buf);
+  }
+  p->codec = WavpackCloseFile(p->codec);
+  return SOX_SUCCESS;
+}
+
+static int seek(sox_format_t * ft, sox_size_t offset)
+{
+  priv_t * p = (priv_t *)ft->priv;
+
+  return WavpackSeekSample(p->codec, (offset / ft->signal.channels))? SOX_SUCCESS : SOX_EOF;
+}
+
+SOX_FORMAT_HANDLER(wavpack)
+{
+  static char const * const names[] = {"wv", NULL};
+  static unsigned const write_encodings[] = {
+    SOX_ENCODING_WAVPACK, 8, 16, 24, 32, 0,
+    SOX_ENCODING_WAVPACKF, 32, 0,
+    0};
+  static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
+    "Lossless, lossy, and hybrid audio compression",
+    names, 0,
+    start_read, read_samples, stop_read,
+    start_write, write_samples, stop_write,
+    seek, write_encodings, NULL
+  };
+  return &handler;
+}