ref: 9fd6eeeaeb69ab568e7dbb26ed84e1bec0d4bd1a
parent: e597e802903cc5f7c72e2efef1cbb166ea79b194
author: John Stumpo <stump@jstump.com>
date: Tue Mar 5 19:04:48 EST 2013
Add read-only Opus support using libopusfile.
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -159,6 +159,7 @@
endif (NOT HAVE_SYS_SOUNDCARD_H)
optional(HAVE_WAVEAUDIO mmsystem.h winmm waveInGetDevCapsA waveaudio)
optional4(HAVE_OGG_VORBIS vorbis/codec.h ogg ogg_stream_flush vorbis vorbis_analysis_headerout vorbisfile ov_clear vorbisenc vorbis_encode_init_vbr vorbis)
+optional3(HAVE_OPUS opusfile.h ogg ogg_stream_flush opus opus_encoder_create opusfile op_open_callbacks opus)
optional(HAVE_WAVPACK wavpack/wavpack.h wavpack WavpackGetSampleRate wavpack)
if (HAVE_LAME_LAME_H OR HAVE_MAD_H)
--- a/configure.ac
+++ b/configure.ac
@@ -427,8 +427,13 @@
AC_CHECK_LIB(vorbisenc, vorbis_encode_init_vbr, OGG_VORBIS_LIBS="-lvorbisenc $OGG_VORBIS_LIBS", using_oggvorbis=no, $OGG_VORBIS_LIBS)],
using_oggvorbis=no)])
+# Check for Opus
+AC_OPTIONAL_FORMAT(opus, OPUS,
+ [PKG_CHECK_MODULES(OPUS, [opusfile], [], using_opus=no)],
+ using_opus=no)
+
# Check for FLAC libraries
# Note passing in OGG_VORBIS_LIBS. That is because FLAC has optional
# support for OGG and if OGG libraries are found on this
@@ -673,6 +678,7 @@
echo " dlopen twolame............$enable_dl_twolame"
fi
echo "oggvorbis..................$using_oggvorbis"
+echo "opus.......................$using_opus"
echo "sndfile....................$using_sndfile"
if test "x$using_sndfile" = "xyes"; then
echo " dlopen sndfile............$enable_dl_sndfile"
--- a/src/formats.c
+++ b/src/formats.c
@@ -57,6 +57,7 @@
CHECK(txw , 0, 0, "" , 0, 6, "LM8953")
CHECK(sndt , 0, 0, "" , 0, 6, "SOUND\x1a")
CHECK(vorbis, 0, 4, "OggS" , 29, 6, "vorbis")
+ CHECK(opus , 0, 4, "OggS" , 28, 8, "OpusHead")
CHECK(speex , 0, 4, "OggS" , 28, 6, "Speex")
CHECK(hcom ,65, 4, "FSSD" , 128,4, "HCOM")
CHECK(wav , 0, 4, "RIFF" , 8, 4, "WAVE")
@@ -142,6 +143,7 @@
{sox_encodings_lossy2, "AMR-NB" , "AMR-NB"},
{sox_encodings_lossy2, "CVSD" , "CVSD"},
{sox_encodings_lossy2, "LPC10" , "LPC10"},
+ {sox_encodings_lossy2, "Opus" , "Opus"},
};
assert_static(array_length(s_sox_encodings_info) == SOX_ENCODINGS,
@@ -182,6 +184,7 @@
case SOX_ENCODING_GSM:
case SOX_ENCODING_VORBIS:
+ case SOX_ENCODING_OPUS:
case SOX_ENCODING_AMR_WB:
case SOX_ENCODING_AMR_NB:
case SOX_ENCODING_LPC10: return !bits_per_sample? 16: 0;
--- a/src/formats.h
+++ b/src/formats.h
@@ -92,6 +92,9 @@
#if defined HAVE_MP3 && (defined STATIC_MP3 || !defined HAVE_LIBLTDL)
FORMAT(mp3)
#endif
+#if defined HAVE_OPUS && (defined STATIC_OPUS || !defined HAVE_LIBLTDL)
+ FORMAT(opus)
+#endif
#if defined HAVE_OSS && (defined STATIC_OSS || !defined HAVE_LIBLTDL)
FORMAT(oss)
#endif
--- a/src/optional-fmts.am
+++ b/src/optional-fmts.am
@@ -135,6 +135,23 @@
endif
endif
+if HAVE_OPUS
+if STATIC_OPUS
+ libsox_la_SOURCES += opus.c
+ libsox_la_CFLAGS += @OPUS_CFLAGS@ -Wno-long-long
+if STATIC_LIBSOX_ONLY
+ sox_LDADD += @OPUS_LIBS@
+else
+ libsox_la_LIBADD += @OPUS_LIBS@
+endif
+else
+ libsox_fmt_opus_la_SOURCES = opus.c
+ libsox_fmt_opus_la_CFLAGS = @OPUS_CFLAGS@ -Wno-long-long
+ libsox_fmt_opus_la_LIBADD = libsox.la @OPUS_LIBS@
+ pkglib_LTLIBRARIES += libsox_fmt_opus.la
+endif
+endif
+
if HAVE_OSS
if STATIC_OSS
libsox_la_SOURCES += oss.c
--- /dev/null
+++ b/src/opus.c
@@ -1,0 +1,235 @@
+/* libSoX Opus-in-Ogg sound format handler
+ * Copyright (C) 2013 John Stumpo <stump@jstump.com>
+ *
+ * Largely based on vorbis.c:
+ * libSoX Ogg Vorbis sound format handler
+ * Copyright 2001, Stan Seibert <indigo@aztec.asu.edu>
+ *
+ * Portions from oggenc, (c) Michael Smith <msmith@labyrinth.net.au>,
+ * ogg123, (c) Kenneth Arnold <kcarnold@yahoo.com>, and
+ * libvorbisfile (c) Xiphophorus Company
+ *
+ * May 9, 2001 - Stan Seibert (indigo@aztec.asu.edu)
+ * Ogg Vorbis handler initially written.
+ *
+ * July 5, 1991 - Skeleton file
+ * Copyright 1991 Lance Norskog And Sundry Contributors
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * Lance Norskog And Sundry Contributors are not responsible for
+ * the consequences of using this software.
+ */
+
+#include "sox_i.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <opusfile.h>
+
+#define DEF_BUF_LEN 4096
+
+#define BUF_ERROR -1
+#define BUF_EOF 0
+#define BUF_DATA 1
+
+typedef struct {
+ /* Decoding data */
+ OggOpusFile *of;
+ char *buf;
+ size_t buf_len;
+ size_t start;
+ size_t end; /* Unsent data samples in buf[start] through buf[end-1] */
+ int current_section;
+ int eof;
+} priv_t;
+
+/******** Callback functions used in op_open_callbacks ************/
+
+static int callback_read(void* ft_data, unsigned char* ptr, int nbytes)
+{
+ sox_format_t* ft = (sox_format_t*)ft_data;
+ return lsx_readbuf(ft, ptr, (size_t)nbytes);
+}
+
+static int callback_seek(void* ft_data, opus_int64 off, int whence)
+{
+ sox_format_t* ft = (sox_format_t*)ft_data;
+ int ret = ft->seekable ? lsx_seeki(ft, (off_t)off, whence) : -1;
+
+ if (ret == EBADF)
+ ret = -1;
+ return ret;
+}
+
+static int callback_close(void* ft_data UNUSED)
+{
+ /* Do nothing so sox can close the file for us */
+ return 0;
+}
+
+static opus_int64 callback_tell(void* ft_data)
+{
+ sox_format_t* ft = (sox_format_t*)ft_data;
+ return lsx_tell(ft);
+}
+
+/********************* End callbacks *****************************/
+
+
+/*
+ * Do anything required before you start reading samples.
+ * Read file header.
+ * Find out sampling rate,
+ * size and encoding of samples,
+ * mono/stereo/quad.
+ */
+static int startread(sox_format_t * ft)
+{
+ priv_t * vb = (priv_t *) ft->priv;
+ const OpusTags *ot;
+ int i;
+
+ OpusFileCallbacks callbacks = {
+ callback_read,
+ callback_seek,
+ callback_tell,
+ callback_close
+ };
+
+ /* Init the decoder */
+ vb->of = op_open_callbacks(ft, &callbacks, NULL, (size_t) 0, NULL);
+ if (vb->of == NULL) {
+ lsx_fail_errno(ft, SOX_EHDR, "Input not an Ogg Opus audio stream");
+ return (SOX_EOF);
+ }
+
+ /* Get info about the Opus stream */
+ ot = op_tags(vb->of, -1);
+
+ /* Record audio info */
+ ft->signal.rate = 48000; /* libopusfile always uses 48 kHz */
+ ft->encoding.encoding = SOX_ENCODING_OPUS;
+ ft->signal.channels = op_channel_count(vb->of, -1);
+
+ /* op_pcm_total doesn't work on non-seekable files so
+ * skip that step in that case. Also, it reports
+ * "frame"-ish results so we must * channels.
+ */
+ if (ft->seekable)
+ ft->signal.length = op_pcm_total(vb->of, -1) * ft->signal.channels;
+
+ /* Record comments */
+ for (i = 0; i < ot->comments; i++)
+ sox_append_comment(&ft->oob.comments, ot->user_comments[i]);
+
+ /* Setup buffer */
+ vb->buf_len = DEF_BUF_LEN;
+ vb->buf_len -= vb->buf_len % (ft->signal.channels*2); /* 2 bytes per sample */
+ vb->buf = lsx_calloc(vb->buf_len, sizeof(char));
+ vb->start = vb->end = 0;
+
+ /* Fill in other info */
+ vb->eof = 0;
+ vb->current_section = -1;
+
+ return (SOX_SUCCESS);
+}
+
+
+/* Refill the buffer with samples. Returns BUF_EOF if the end of the
+ * Opus data was reached while the buffer was being filled,
+ * BUF_ERROR is something bad happens, and BUF_DATA otherwise */
+static int refill_buffer(sox_format_t * ft)
+{
+ priv_t * vb = (priv_t *) ft->priv;
+ int num_read;
+
+ if (vb->start == vb->end) /* Samples all played */
+ vb->start = vb->end = 0;
+
+ while (vb->end < vb->buf_len) {
+ num_read = op_read(vb->of, (opus_int16*) (vb->buf + vb->end),
+ (int) ((vb->buf_len - vb->end) / sizeof(opus_int16)),
+ &vb->current_section);
+ if (num_read == 0)
+ return (BUF_EOF);
+ else if (num_read == OP_HOLE)
+ lsx_warn("Warning: hole in stream; probably harmless");
+ else if (num_read < 0)
+ return (BUF_ERROR);
+ else
+ vb->end += num_read * sizeof(opus_int16) * ft->signal.channels;
+ }
+ return (BUF_DATA);
+}
+
+
+/*
+ * Read up to len samples from file.
+ * Convert to signed longs.
+ * Place in buf[].
+ * Return number of samples read.
+ */
+
+static size_t read_samples(sox_format_t * ft, sox_sample_t * buf, size_t len)
+{
+ priv_t * vb = (priv_t *) ft->priv;
+ size_t i;
+ int ret;
+ sox_sample_t l;
+
+
+ for (i = 0; i < len; i++) {
+ if (vb->start == vb->end) {
+ if (vb->eof)
+ break;
+ ret = refill_buffer(ft);
+ if (ret == BUF_EOF || ret == BUF_ERROR) {
+ vb->eof = 1;
+ if (vb->end == 0)
+ break;
+ }
+ }
+
+ l = (vb->buf[vb->start + 1] << 24)
+ | (0xffffff & (vb->buf[vb->start] << 16));
+ *(buf + i) = l;
+ vb->start += 2;
+ }
+ return i;
+}
+
+/*
+ * Do anything required when you stop reading samples.
+ * Don't close input file!
+ */
+static int stopread(sox_format_t * ft)
+{
+ priv_t * vb = (priv_t *) ft->priv;
+
+ free(vb->buf);
+ op_free(vb->of);
+
+ return (SOX_SUCCESS);
+}
+
+static int seek(sox_format_t * ft, uint64_t offset)
+{
+ priv_t * vb = (priv_t *) ft->priv;
+
+ return op_pcm_seek(vb->of, (opus_int64)(offset / ft->signal.channels))? SOX_EOF:SOX_SUCCESS;
+}
+
+LSX_FORMAT_HANDLER(opus)
+{
+ static const char *const names[] = {"opus", NULL};
+ static sox_format_handler_t handler = {SOX_LIB_VERSION_CODE,
+ "Xiph.org's Opus lossy compression", names, 0,
+ startread, read_samples, stopread,
+ NULL, NULL, NULL,
+ seek, NULL, NULL, sizeof(priv_t)
+ };
+ return &handler;
+}
--- a/src/sox.h
+++ b/src/sox.h
@@ -593,6 +593,7 @@
SOX_ENCODING_AMR_NB , /**< AMR-NB compression */
SOX_ENCODING_CVSD , /**< Continuously Variable Slope Delta modulation */
SOX_ENCODING_LPC10 , /**< Linear Predictive Coding */
+ SOX_ENCODING_OPUS , /**< Opus compression */
SOX_ENCODINGS /**< End of list marker */
} sox_encoding_t;