[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