[opus] [PATCH 1/1] Reading input from WavPack files.

Lucas Clemente Vella lvella at gmail.com
Wed Oct 7 22:49:08 PDT 2015


Added optional support for WavPack file format using libwavpack.
---
 Makefile.am    |   7 +-
 configure.ac   |  37 ++++++++
 src/audio-in.c |  71 ++++++++-------
 src/opusenc.c  |  19 +++-
 src/opusenc.h  |   3 +
 src/wavpack.c  | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/wavpack.h  |  40 +++++++++
 7 files changed, 414 insertions(+), 39 deletions(-)
 create mode 100644 src/wavpack.c
 create mode 100644 src/wavpack.h

diff --git a/Makefile.am b/Makefile.am
index 2f1ac76..e91f9dc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -27,6 +27,7 @@ noinst_HEADERS = src/arch.h \
                  win32/unicode_support.h \
                  src/cpusupport.h \
                  src/wave_out.h \
+								 src/wavpack.h \
                  src/wav_io.h
 
 EXTRA_DIST = Makefile.unix \
@@ -51,10 +52,10 @@ dist_man_MANS = man/opusenc.1 man/opusdec.1 man/opusinfo.1
 
 resampler_CPPFLAGS = -DSPX_RESAMPLE_EXPORT= -DRANDOM_PREFIX=opustools -DOUTSIDE_SPEEX -DFLOATING_POINT
 
-opusenc_SOURCES = src/opus_header.c src/opusenc.c src/picture.c src/resample.c src/audio-in.c src/diag_range.c src/flac.c src/lpc.c win32/unicode_support.c
+opusenc_SOURCES = src/opus_header.c src/opusenc.c src/picture.c src/resample.c src/audio-in.c src/diag_range.c src/flac.c src/lpc.c src/wavpack.c win32/unicode_support.c
 opusenc_CPPFLAGS = $(AM_CPPFLAGS) $(resampler_CPPFLAGS)
-opusenc_CFLAGS = $(AM_CFLAGS) $(FLAC_CFLAGS)
-opusenc_LDADD = $(OPUS_LIBS) $(FLAC_LIBS) $(OGG_LIBS) $(LIBM)
+opusenc_CFLAGS = $(AM_CFLAGS) $(FLAC_CFLAGS) $(WAVPACK_CFLAGS)
+opusenc_LDADD = $(OPUS_LIBS) $(FLAC_LIBS) $(WAVPACK_LIBS) $(OGG_LIBS) $(LIBM)
 opusenc_MANS = man/opusenc.1
 
 opusdec_SOURCES = src/opus_header.c src/wav_io.c src/wave_out.c src/opusdec.c src/resample.c src/diag_range.c win32/unicode_support.c
diff --git a/configure.ac b/configure.ac
index edff53f..9bd173e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,42 @@ AS_IF([test "$with_flac" = "yes"],
   AC_DEFINE([HAVE_LIBFLAC],[1],[FLAC])
  ])
 
+dnl check for wavpack
+AC_ARG_WITH([wavpack],
+    [AS_HELP_STRING([--without-wavpack],[disable WavPack support])],,
+    [with_wavpack=yes])
+
+AS_IF([test "$with_wavpack" = "yes"],
+ [
+  AS_IF([test "$HAVE_PKG_CONFIG" = "yes"],
+   [PKG_CHECK_MODULES([WAVPACK],[wavpack >= 4.70.0])],
+   [
+    dnl fall back to AC_CHECK_LIB
+    AC_CHECK_LIB([wavpack],[WavpackGetLibraryVersion],
+      [
+        WAVPACK_LIBS="-lwavpack"
+      ],
+      [
+        AC_MSG_ERROR([
+          libwavpack 4.70 or later is required to build this package!
+          Please install it or configure with --disable-wavpack.
+        ])
+      ]
+    )
+    AC_CHECK_HEADER([wavpack/wavpack.h],,
+      [
+        AC_MSG_ERROR([
+          libwavpack headers are required to build this package!
+          Please install the development version of libwavpack
+          or configure with --disable-wavpack.
+        ])
+      ]
+    )
+   ])
+
+  AC_DEFINE([HAVE_LIBWAVPACK],[1],[Have libwavpack available.])
+ ])
+
 dnl check for pcap
 AC_CHECK_LIB([pcap], [pcap_open_live], [
   AC_DEFINE([HAVE_PCAP], 1, [Define if building with libpcap support])
@@ -315,6 +351,7 @@ AC_MSG_NOTICE([
 
       Assertion checking: ............ ${enable_assertions}
       FLAC input: .................... ${with_flac}
+      WavPack input: ................. ${with_wavpack}
 
 ------------------------------------------------------------------------
 
diff --git a/src/audio-in.c b/src/audio-in.c
index 8ed037b..1d38b1b 100644
--- a/src/audio-in.c
+++ b/src/audio-in.c
@@ -75,6 +75,7 @@
 #include "lpc.h"
 #include "opus_header.h"
 #include "flac.h"
+#include "wavpack.h"
 
 /* Macros to read header data */
 #define READ_U32_LE(buf) \
@@ -95,6 +96,7 @@ input_format formats[] = {
     {aiff_id, 12, aiff_open, wav_close, "aiff", N_("AIFF/AIFC file reader")},
     {flac_id,     4, flac_open, flac_close, "flac", N_("FLAC file reader")},
     {oggflac_id, 33, flac_open, flac_close, "ogg", N_("Ogg FLAC file reader")},
+    {wavpack_id, 4, wavpack_open, wavpack_close, "wv", N_("WavPack file reader")},
     {NULL, 0, NULL, NULL, NULL, NULL}
 };
 
@@ -433,6 +435,42 @@ int wav_id(unsigned char *buf, int len)
     return 1;
 }
 
+void wav_mask_warn(unsigned int mask, const char *tname)
+{
+    switch(mask){
+    case 1539: /* 4.0 using side surround instead of back */
+      fprintf(stderr, _("WARNING: %s file uses side surround instead of rear for quadraphonic;\n"
+                "remapping side speakers to rear in encoding.\n"), tname);
+      break;
+    case 1551: /* 5.1 using side instead of rear */
+      fprintf(stderr, _("WARNING: %s file uses side surround instead of rear for 5.1;\n"
+              "remapping side speakers to rear in encoding.\n"), tname);
+      break;
+    case 319:  /* 6.1 using rear instead of side */
+      fprintf(stderr, _("WARNING: %s file uses rear surround instead of side for 6.1;\n"
+              "remapping rear speakers to side in encoding.\n"), tname);
+      break;
+    case 255:  /* 7.1 'Widescreen' */
+      fprintf(stderr, _("WARNING: %s file is a 7.1 'Widescreen' channel mapping;\n"
+              "remapping speakers to Vorbis 7.1 format.\n"), tname);
+      break;
+    case 0:    /* default/undeclared */
+    case 1:    /* mono */
+    case 3:    /* stereo */
+    case 51:   /* quad */
+    case 55:   /* 5.0 */
+    case 63:   /* 5.1 */
+    case 1807: /* 6.1 */
+    case 1599: /* 7.1 */
+      break;
+    default:
+      fprintf(stderr, _("WARNING: Unknown WAV surround channel mask: %d\n"
+              "Blindly mapping speakers using default SMPTE/ITU ordering.\n"),
+              mask);
+      break;
+    }
+}
+
 int wav_open(FILE *in, oe_enc_opt *opt, unsigned char *oldbuf, int buflen)
 {
     unsigned char buf[40];
@@ -501,38 +539,7 @@ int wav_open(FILE *in, oe_enc_opt *opt, unsigned char *oldbuf, int buflen)
 
       format.mask = READ_U32_LE(buf+20);
       /* warn the user if the format mask is not a supported/expected type */
-      switch(format.mask){
-      case 1539: /* 4.0 using side surround instead of back */
-        fprintf(stderr, _("WARNING: WAV file uses side surround instead of rear for quadraphonic;\n"
-                "remapping side speakers to rear in encoding.\n"));
-        break;
-      case 1551: /* 5.1 using side instead of rear */
-        fprintf(stderr, _("WARNING: WAV file uses side surround instead of rear for 5.1;\n"
-                "remapping side speakers to rear in encoding.\n"));
-        break;
-      case 319:  /* 6.1 using rear instead of side */
-        fprintf(stderr, _("WARNING: WAV file uses rear surround instead of side for 6.1;\n"
-                "remapping rear speakers to side in encoding.\n"));
-        break;
-      case 255:  /* 7.1 'Widescreen' */
-        fprintf(stderr, _("WARNING: WAV file is a 7.1 'Widescreen' channel mapping;\n"
-                "remapping speakers to Vorbis 7.1 format.\n"));
-        break;
-      case 0:    /* default/undeclared */
-      case 1:    /* mono */
-      case 3:    /* stereo */
-      case 51:   /* quad */
-      case 55:   /* 5.0 */
-      case 63:   /* 5.1 */
-      case 1807: /* 6.1 */
-      case 1599: /* 7.1 */
-        break;
-      default:
-        fprintf(stderr, _("WARNING: Unknown WAV surround channel mask: %d\n"
-                "Blindly mapping speakers using default SMPTE/ITU ordering.\n"),
-                format.mask);
-        break;
-      }
+      wav_mask_warn(format.mask, "WAV");
       format.format = READ_U16_LE(buf+24);
     }
     else
diff --git a/src/opusenc.c b/src/opusenc.c
index 0f83194..ef002dd 100644
--- a/src/opusenc.c
+++ b/src/opusenc.c
@@ -111,11 +111,19 @@ void usage(void)
   printf("Usage: opusenc [options] input_file output_file.opus\n");
   printf("\n");
   printf("Encodes input_file using Opus.\n");
+  printf("It can read the WAV, AIFF,%s%s or raw files.\n",
 #if defined(HAVE_LIBFLAC)
-  printf("It can read the WAV, AIFF, FLAC, Ogg/FLAC, or raw files.\n");
+      " FLAC, Ogg/FLAC,"
 #else
-  printf("It can read the WAV, AIFF, or raw files.\n");
+      ""
 #endif
+      ,
+#if defined(HAVE_LIBWAVPACK)
+      " WavPack,"
+#else
+      ""
+#endif
+  );
   printf("\nGeneral options:\n");
   printf(" -h, --help         This help\n");
   printf(" -V, --version      Version information\n");
@@ -380,6 +388,7 @@ int main(int argc, char **argv)
   inopt.ignorelength=0;
   inopt.copy_comments=1;
   inopt.copy_pictures=1;
+  inopt.is_lossy=0;
 
   start_time = time(NULL);
   srand(((getpid()&65535)<<15)^start_time);
@@ -635,6 +644,7 @@ int main(int argc, char **argv)
       perror(inFile);
       exit(1);
     }
+    inopt.infilename = inFile;
   }
 
   if(inopt.rawmode){
@@ -810,8 +820,9 @@ int main(int argc, char **argv)
     else if(opus_app==OPUS_APPLICATION_RESTRICTED_LOWDELAY)fprintf(stderr," (low-delay)\n");
     else fprintf(stderr," (unknown)\n");
     fprintf(stderr,"-----------------------------------------------------\n");
-    fprintf(stderr,"   Input: %0.6gkHz %d channel%s\n",
-            header.input_sample_rate/1000.,chan,chan<2?"":"s");
+    fprintf(stderr,"   Input: %0.6gkHz %d channel%s%s\n",
+            header.input_sample_rate/1000.,chan,chan<2?"":"s",
+            inopt.is_lossy ? " (lossy)" : "");
     fprintf(stderr,"  Output: %d channel%s (",header.channels,header.channels<2?"":"s");
     if(header.nb_coupled>0)fprintf(stderr,"%d coupled",header.nb_coupled*2);
     if(header.nb_streams-header.nb_coupled>0)fprintf(stderr,
diff --git a/src/opusenc.h b/src/opusenc.h
index 91c1548..ca76349 100644
--- a/src/opusenc.h
+++ b/src/opusenc.h
@@ -39,6 +39,7 @@ typedef struct
     int comments_length;
     int copy_comments;
     int copy_pictures;
+    char is_lossy;
 } oe_enc_opt;
 
 void setup_scaler(oe_enc_opt *opt, float scale);
@@ -106,4 +107,6 @@ void clear_resample(oe_enc_opt *opt);
 long wav_read(void *, float *buffer, int samples);
 long wav_ieee_read(void *, float *buffer, int samples);
 
+void wav_mask_warn(unsigned int mask, const char *tname);
+
 #endif /* __OPUSENC_H */
diff --git a/src/wavpack.c b/src/wavpack.c
new file mode 100644
index 0000000..4b2604e
--- /dev/null
+++ b/src/wavpack.c
@@ -0,0 +1,276 @@
+/*Copyright 2012-2015, Xiph.Org Foundation and contributors.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  - Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+  - Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "opus_header.h"
+#include "wavpack.h"
+
+#if defined(HAVE_LIBWAVPACK)
+
+/* Stream reader used by libwavpack: */
+
+struct input_stream {
+  FILE *fd;
+  unsigned char *buf;
+  int32_t buf_remaining;
+};
+
+static int32_t read_bytes (void *is, void *data, int32_t bcount)
+{
+  struct input_stream *s = is;
+  int32_t min = 0;
+  if(s->buf_remaining) {
+    min = bcount < s->buf_remaining ? bcount : s->buf_remaining;
+    memcpy(data, s->buf, min);
+
+    bcount -= min;
+    data = min + (char*)data;
+
+    s->buf_remaining -= min;
+    s->buf += min;
+  }
+  return min + (int32_t) fread (data, 1, bcount, s->fd);
+}
+
+static uint32_t get_pos (void *is)
+{
+  struct input_stream *s = is;
+  return ftell (s->fd);
+}
+
+static int set_pos_abs (void *is, uint32_t pos)
+{
+  struct input_stream *s = is;
+  s->buf_remaining = 0;
+  return fseek (s->fd, pos, SEEK_SET);
+}
+
+static int set_pos_rel (void *is, int32_t delta, int mode)
+{
+  struct input_stream *s = is;
+  s->buf_remaining = 0;
+  return fseek (s->fd, delta, mode);
+}
+
+static int push_back_byte (void *is, int c)
+{
+  struct input_stream *s = is;
+  if(s->buf_remaining) {
+    ++s->buf_remaining;
+    *(--s->buf) = c;
+    return c;
+  }
+  return ungetc (c, s->fd);
+}
+
+static uint32_t get_length (void *is)
+{
+  struct input_stream *s = is;
+  struct stat statbuf;
+
+  if (!s->fd || fstat (fileno (s->fd), &statbuf) || !(statbuf.st_mode & S_IFREG))
+    return 0;
+
+  return statbuf.st_size;
+}
+
+static int can_seek (void *is)
+{
+  struct input_stream *s = is;
+  struct stat statbuf;
+
+  return s->fd && !fstat (fileno (s->fd), &statbuf) && (statbuf.st_mode & S_IFREG);
+}
+
+static WavpackStreamReader reader = {
+    .read_bytes = read_bytes,
+    .get_pos = get_pos,
+    .set_pos_abs = set_pos_abs,
+    .set_pos_rel = set_pos_rel,
+    .push_back_byte = push_back_byte,
+    .get_length = get_length,
+    .can_seek = can_seek,
+    .write_bytes = NULL
+};
+
+/* Decoding data. */
+struct decoding_data
+{
+  WavpackContext *ctx;
+
+  struct input_stream wv_is;
+  struct input_stream wvc_is;
+
+  const int *channel_permute;
+  unsigned short channels;
+
+  char is_fixed;
+  float fixed_range;
+};
+
+static long wavpack_read(void *src, float *buffer, int samples)
+{
+  struct decoding_data *data = src;
+  int32_t *i_buf = (int32_t *)buffer;
+
+  long ret = WavpackUnpackSamples(data->ctx, i_buf, samples);
+
+  /* Reorder channels */
+  if(data->channel_permute) {
+    int32_t tmp[8]; /* Maximum number of channels with definite mapping. */
+    for(int i = 0; i < ret; ++i) {
+      int32_t *frame = &i_buf[i * data->channels];
+      memcpy(tmp, frame, data->channels * 4);
+      for(unsigned short j = 0; j < data->channels; ++j)
+        frame[j] = tmp[data->channel_permute[j]];
+    }
+  }
+
+  /* Convert to float */
+  if(data->is_fixed) {
+    for(unsigned i = 0; i < ret * data->channels; ++i)
+      buffer[i] = i_buf[i] / data->fixed_range;
+  }
+
+  return ret;
+}
+
+int wavpack_id(unsigned char *buf,int len){
+  /* Sanity check. */
+  if(len < 4)
+    return 0;
+
+  return memcmp(buf, "wvpk", 4) == 0;
+}
+
+int wavpack_open(FILE *in,oe_enc_opt *opt,unsigned char *oldbuf,int buflen){
+  struct decoding_data *data = calloc(1, sizeof *data);
+  int flags = OPEN_NORMALIZE;
+
+  /* If we have a filename, we try to find the corresponding wvc file. */
+  if(opt->infilename) {
+    char *wvc_fname = malloc(strlen(opt->infilename) + 2);
+
+    strcpy(wvc_fname, opt->infilename);
+    strcat(wvc_fname, "c");
+    FILE *fd = fopen(wvc_fname, "rb");
+    free(wvc_fname);
+
+    if(fd) {
+      data->wvc_is.fd = fd;
+      flags |= OPEN_WVC;
+    }
+  }
+
+  data->wv_is.fd = in;
+  data->wv_is.buf = oldbuf;
+  data->wv_is.buf_remaining = buflen;
+
+  /* Open the stream decoder. */
+  {
+    char err_msg[80]; /* This size is given by libwavpack docs. */
+
+    data->ctx = WavpackOpenFileInputEx(&reader, &data->wv_is,
+        data->wvc_is.fd ? &data->wvc_is : NULL, err_msg, flags, 0);
+    if(!data->ctx) {
+      fprintf(stderr, _("Error: Parsing of WavPack file failed:\n%s\n"), err_msg);
+      if(data->wvc_is.fd)
+        fclose(data->wvc_is.fd);
+      return 0;
+    }
+  }
+
+  int mode = WavpackGetMode(data->ctx);
+  opt->is_lossy = !(mode & MODE_LOSSLESS);
+
+  /* Determine sample format */
+  data->is_fixed = !(mode & MODE_FLOAT);
+  if(data->is_fixed) {
+    const float ranges[] = {128.0f, 32768.0f, 8388608.0f, 2147483648.0f};
+    data->fixed_range = ranges[WavpackGetBytesPerSample(data->ctx) - 1];
+  }
+
+  /* Set the options. */
+  opt->read_samples = wavpack_read;
+  opt->readdata = data;
+
+  data->channels = opt->channels = WavpackGetNumChannels(data->ctx);
+  opt->total_samples_per_channel = WavpackGetNumSamples(data->ctx);
+  opt->rate = WavpackGetSampleRate(data->ctx);
+  opt->samplesize = WavpackGetBitsPerSample(data->ctx);
+
+  /* Wavpack uses the same mask and channel ordering of WAV. */
+  unsigned wav_mask = WavpackGetChannelMask(data->ctx);
+  wav_mask_warn(wav_mask, "WavPack");
+
+  if (opt->channels <= 8)
+    /* Where we know the mappings, use them. */
+    data->channel_permute = wav_permute_matrix[data->channels-1];
+  else
+    data->channel_permute = NULL;
+
+  return 1;
+}
+
+void wavpack_close(void *client_data){
+  struct decoding_data *data = client_data;
+
+  WavpackCloseFile(data->ctx);
+
+  if(data->wvc_is.fd)
+    fclose(data->wvc_is.fd);
+
+  free(data);
+}
+
+#else
+
+/*WavPack support is disabled.*/
+
+int wavpack_id(unsigned char *buf,int len){
+  (void)buf;
+  (void)len;
+  return 0;
+}
+
+int wavpack_open(FILE *in,oe_enc_opt *opt,unsigned char *oldbuf,int buflen){
+  (void)in;
+  (void)opt;
+  (void)oldbuf;
+  (void)buflen;
+  return 0;
+}
+
+void wavpack_close(void *client_data){
+  (void)client_data;
+}
+
+#endif
diff --git a/src/wavpack.h b/src/wavpack.h
new file mode 100644
index 0000000..2a5c39c
--- /dev/null
+++ b/src/wavpack.h
@@ -0,0 +1,40 @@
+/*Copyright 2012-2015, Xiph.Org Foundation and contributors.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  - Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+  - Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
+#ifndef __WAVPACK_H
+# define __WAVPACK_H
+
+#include <stdio.h>
+
+# if defined(HAVE_LIBWAVPACK)
+#  include <wavpack/wavpack.h>
+# endif
+
+#include "opusenc.h"
+
+int wavpack_id(unsigned char *buf,int len);
+int wavpack_open(FILE *in,oe_enc_opt *opt,unsigned char *oldbuf,int buflen);
+void wavpack_close(void *client_data);
+
+#endif
-- 
2.1.4



More information about the opus mailing list