shithub: sox

Download patch

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;