shithub: opus-tools

Download patch

ref: a638dfa37bd857e14b088e0ccade701dc6aafc79
parent: e9bbfacf0a1bbade70005fa730cc50e838baaa32
author: Jean-Marc Valin <jmvalin@jmvalin.ca>
date: Wed May 10 18:30:32 EDT 2017

opusenc: Convert to use libopusenc

This adds a dependency, but gets rid of a lot of code. The main benefit
is that libopusenc supports delayed decision.

Signed-off-by: Mark Harris <mark.hsj@gmail.com>

--- a/Makefile.am
+++ b/Makefile.am
@@ -15,8 +15,7 @@
                  src/diag_range.h \
                  src/flac.h \
                  src/info_opus.h \
-                 src/lpc.h \
-                 src/opusenc.h \
+                 src/encoder.h \
                  src/opus_header.h \
                  src/opusinfo.h \
                  src/os_support.h \
@@ -53,10 +52,10 @@
 
 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_CPPFLAGS = $(AM_CPPFLAGS) $(resampler_CPPFLAGS)
-opusenc_CFLAGS = $(AM_CFLAGS) $(FLAC_CFLAGS)
-opusenc_LDADD = $(OPUS_LIBS) $(FLAC_LIBS) $(OGG_LIBS) $(LIBM)
+opusenc_SOURCES = src/opus_header.c src/opusenc.c src/picture.c src/audio-in.c src/diag_range.c src/flac.c win32/unicode_support.c
+opusenc_CPPFLAGS = $(AM_CPPFLAGS)
+opusenc_CFLAGS = $(AM_CFLAGS) $(LIBOPUSENC_CFLAGS) $(FLAC_CFLAGS)
+opusenc_LDADD = $(LIBOPUSENC_LIBS) $(OPUS_LIBS) $(FLAC_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
--- a/configure.ac
+++ b/configure.ac
@@ -159,6 +159,17 @@
   OPUSURL_LIBS="$OPUSFILE_LIBS -lopusurl"
  ])
 
+dnl check for libopusenc
+AS_IF([test "$HAVE_PKG_CONFIG" = "yes"],
+ [PKG_CHECK_MODULES([LIBOPUSENC],[libopusenc >= 0.1.1])],
+ [
+  dnl fall back to the old school test
+  XIPH_PATH_LIBOPUSENC(, AC_MSG_ERROR([
+    libopusenc is required to build this package!
+    Please see https://opus-codec.org/ for how to obtain a copy.
+  ]))
+ ])
+
 dnl check for OSS
 HAVE_OSS=no
 AC_CHECK_HEADERS([sys/soundcard.h soundcard.h machine/soundcard.h],[
--- /dev/null
+++ b/m4/libopusenc.m4
@@ -1,0 +1,117 @@
+# Configure paths for libopusenc
+# Jean-Marc Valin <jmvalin@jmvalin.ca> 11-12-2017
+# Jack Moffitt <jack@icecast.org> 10-21-2000
+# Shamelessly stolen from Owen Taylor and Manish Singh
+
+dnl XIPH_PATH_LIBOPUSENC([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl Test for libopusenc, and define LIBOPUSENC_CFLAGS and LIBOPUSENC_LIBS
+dnl
+AC_DEFUN([XIPH_PATH_LIBOPUSENC],
+[dnl
+dnl Get the cflags and libraries
+dnl
+AC_ARG_WITH(libopusenc,AC_HELP_STRING([--with-libopusenc=PFX],[Prefix where libopusenc is installed (optional)]), libopusenc_prefix="$withval", libopusenc_prefix="")
+AC_ARG_WITH(libopusenc-libraries,AC_HELP_STRING([--with-libopusenc-libraries=DIR],[Directory where libopusenc library is installed (optional)]), libopusenc_libraries="$withval", libopusenc_libraries="")
+AC_ARG_WITH(libopusenc-includes,AC_HELP_STRING([--with-libopusenc-includes=DIR],[Directory where libopusenc header files are installed (optional)]), libopusenc_includes="$withval", libopusenc_includes="")
+AC_ARG_ENABLE(libopusenctest,AC_HELP_STRING([--disable-libopusenctest],[Do not try to compile and run a test libopusenc program]),, enable_libopusenctest=yes)
+
+  if test "x$libopusenc_libraries" != "x" ; then
+    LIBOPUSENC_LIBS="-L$libopusenc_libraries"
+  elif test "x$libopusenc_prefix" = "xno" || test "x$libopusenc_prefix" = "xyes" ; then
+    LIBOPUSENC_LIBS=""
+  elif test "x$libopusenc_prefix" != "x" ; then
+    LIBOPUSENC_LIBS="-L$libopusenc_prefix/lib"
+  elif test "x$prefix" != "xNONE" ; then
+    LIBOPUSENC_LIBS="-L$prefix/lib"
+  fi
+
+  if test "x$libopusenc_prefix" != "xno" ; then
+    LIBOPUSENC_LIBS="$LIBOPUSENC_LIBS -lopusenc"
+  fi
+
+  if test "x$libopusenc_includes" != "x" ; then
+    LIBOPUSENC_CFLAGS="-I$libopusenc_includes"
+  elif test "x$libopusenc_prefix" = "xno" || test "x$libopusenc_prefix" = "xyes" ; then
+    LIBOPUSENC_CFLAGS=""
+  elif test "x$libopusenc_prefix" != "x" ; then
+    LIBOPUSENC_CFLAGS="-I$libopusenc_prefix/include/opus"
+  elif test "x$prefix" != "xNONE"; then
+    LIBOPUSENC_CFLAGS="-I$prefix/include/opus"
+  fi
+
+  AC_MSG_CHECKING(for libopusenc)
+  if test "x$libopusenc_prefix" = "xno" ; then
+    no_libopusenc="disabled"
+    enable_libopusenctest="no"
+  else
+    no_libopusenc=""
+  fi
+
+
+  if test "x$enable_libopusenctest" = "xyes" ; then
+    ac_save_CFLAGS="$CFLAGS"
+    ac_save_LIBS="$LIBS"
+    CFLAGS="$CFLAGS $LIBOPUSENC_CFLAGS $OPUS_CFLAGS"
+    LIBS="$LIBS $LIBOPUSENC_LIBS $OPUS_LIBS"
+dnl
+dnl Now check if the installed libopusenc is sufficiently new.
+dnl
+      rm -f conf.libopusenctest
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <opusenc.h>
+
+int main ()
+{
+  system("touch conf.libopusenctest");
+  return 0;
+}
+
+],, no_libopusenc=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+  fi
+
+  if test "x$no_libopusenc" = "xdisabled" ; then
+     AC_MSG_RESULT(no)
+     ifelse([$2], , :, [$2])
+  elif test "x$no_libopusenc" = "x" ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$1], , :, [$1])
+  else
+     AC_MSG_RESULT(no)
+     if test -f conf.libopusenctest ; then
+       :
+     else
+       echo "*** Could not run libopusenc test program, checking why..."
+       CFLAGS="$CFLAGS $LIBOPUSENC_CFLAGS"
+       LIBS="$LIBS $LIBOPUSENC_LIBS"
+       AC_TRY_LINK([
+#include <stdio.h>
+#include <opusenc.h>
+],     [ return 0; ],
+       [ echo "*** The test program compiled, but did not run. This usually means"
+       echo "*** that the run-time linker is not finding libopusenc or finding the wrong"
+       echo "*** version of libopusenc. If it is not finding libopusenc, you'll need to set your"
+       echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+       echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+       echo "*** is required on your system"
+       echo "***"
+       echo "*** If you have an old version installed, it is best to remove it, although"
+       echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+       [ echo "*** The test program failed to compile or link. See the file config.log for the"
+       echo "*** exact error that occured. This usually means libopusenc was incorrectly installed"
+       echo "*** or that you have moved libopusenc since it was installed." ])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+     LIBOPUSENC_CFLAGS=""
+     LIBOPUSENC_LIBS=""
+     ifelse([$2], , :, [$2])
+  fi
+  AC_SUBST(LIBOPUSENC_CFLAGS)
+  AC_SUBST(LIBOPUSENC_LIBS)
+  rm -f conf.libopusenctest
+])
--- a/man/opusenc.1
+++ b/man/opusenc.1
@@ -267,33 +267,17 @@
 There may only be one picture each of type 1 and 2 in a file.
 
 .I media-type
-is optional. If left blank, it will be detected from the file. For
-best compatibility with players, use pictures with a
-.I media-type
-of image/jpeg or image/png. The
-.I media-type
-can also be "-->" to mean that
-.I filename
-is actually a URL to an image, though this use is discouraged.
-The file at the URL will not be fetched.
-The URL itself is stored in the metadata.
+is optional and is now ignored.
 
 .I description
 is optional. The default is an empty string.
 
-The next part specifies the resolution and color information. If the
-.I media-type
-is image/jpeg, image/png, or image/gif, this can usually be left empty
-and the information will be read from the file.
-Otherwise, you must specify the width in
-pixels, height in pixels, and color depth in bits-per-pixel. If the image has
-indexed colors you should also specify the number of colors used. If possible,
-these are checked against the file for accuracy.
+The next part specifies the resolution and color information, but
+is now ignored.
 
 .I filename
-is the path to the picture file to be imported, or the URL if the
-.I media-type
-is "-->".
+is the path to the picture file to be imported.
+
 .IP "--padding n"
 Reserve
 .I n
--- a/src/audio-in.c
+++ b/src/audio-in.c
@@ -71,9 +71,7 @@
 #endif
 
 #include <ogg/ogg.h>
-#include "opusenc.h"
-#include "speex_resampler.h"
-#include "lpc.h"
+#include "encoder.h"
 #include "opus_header.h"
 #include "flac.h"
 
@@ -886,157 +884,6 @@
     opt->readdata = d;
     d->channels = opt->channels;
     d->scale_factor = scale;
-}
-
-typedef struct {
-    audio_read_func real_reader;
-    void *real_readdata;
-    ogg_int64_t *original_samples;
-    int channels;
-    int lpc_ptr;
-    int *extra_samples;
-    float *lpc_out;
-} padder;
-
-/* Read audio data, appending padding to make up any gap
- * between the available and requested number of samples
- * with LPC-predicted data to minimize the pertubation of
- * the valid data that falls in the same frame.
- */
-static long read_padder(void *data, float *buffer, int samples) {
-    padder *d = data;
-    long in_samples = d->real_reader(d->real_readdata, buffer, samples);
-    int i, extra=0;
-    const int lpc_order=32;
-
-    if(d->original_samples)*d->original_samples+=in_samples;
-
-    if(in_samples<samples){
-      if(d->lpc_ptr<0){
-        d->lpc_out=calloc(d->channels * *d->extra_samples, sizeof(*d->lpc_out));
-        if(in_samples>lpc_order*2){
-          float *lpc=alloca(lpc_order*sizeof(*lpc));
-          for(i=0;i<d->channels;i++){
-            vorbis_lpc_from_data(buffer+i,lpc,in_samples,lpc_order,d->channels);
-            vorbis_lpc_predict(lpc,buffer+i+(in_samples-lpc_order)*d->channels,
-                               lpc_order,d->lpc_out+i,*d->extra_samples,d->channels);
-          }
-        }
-        d->lpc_ptr=0;
-      }
-      extra=samples-in_samples;
-      if(extra>*d->extra_samples)extra=*d->extra_samples;
-      *d->extra_samples-=extra;
-    }
-    memcpy(buffer+in_samples*d->channels,d->lpc_out+d->lpc_ptr*d->channels,extra*d->channels*sizeof(*buffer));
-    d->lpc_ptr+=extra;
-    return in_samples+extra;
-}
-
-void setup_padder(oe_enc_opt *opt,ogg_int64_t *original_samples) {
-    padder *d = calloc(1, sizeof(padder));
-
-    d->real_reader = opt->read_samples;
-    d->real_readdata = opt->readdata;
-
-    opt->read_samples = read_padder;
-    opt->readdata = d;
-    d->channels = opt->channels;
-    d->extra_samples = &opt->extraout;
-    d->original_samples=original_samples;
-    d->lpc_ptr = -1;
-    d->lpc_out = NULL;
-}
-
-void clear_padder(oe_enc_opt *opt) {
-    padder *d = opt->readdata;
-
-    opt->read_samples = d->real_reader;
-    opt->readdata = d->real_readdata;
-
-    if(d->lpc_out)free(d->lpc_out);
-    free(d);
-}
-
-typedef struct {
-    SpeexResamplerState *resampler;
-    audio_read_func real_reader;
-    void *real_readdata;
-    float *bufs;
-    int channels;
-    int bufpos;
-    int bufsize;
-    int done;
-} resampler;
-
-static long read_resampled(void *d, float *buffer, int samples)
-{
-    resampler *rs = d;
-    int out_samples=0;
-    float *pcmbuf;
-    int *inbuf;
-    pcmbuf=rs->bufs;
-    inbuf=&rs->bufpos;
-    while(out_samples<samples){
-      int i;
-      int reading, ret;
-      unsigned in_len, out_len;
-      out_len=samples-out_samples;
-      reading=rs->bufsize-*inbuf;
-      if(reading>1024)reading=1024;
-      ret=rs->real_reader(rs->real_readdata, pcmbuf+*inbuf*rs->channels, reading);
-      *inbuf+=ret;
-      in_len=*inbuf;
-      speex_resampler_process_interleaved_float(rs->resampler, pcmbuf, &in_len, buffer+out_samples*rs->channels, &out_len);
-      out_samples+=out_len;
-      if(ret==0&&in_len==0){
-        for(i=out_samples*rs->channels;i<samples*rs->channels;i++)buffer[i]=0;
-        return out_samples;
-      }
-      for(i=0;i<rs->channels*(*inbuf-(long int)in_len);i++)pcmbuf[i]=pcmbuf[i+rs->channels*in_len];
-      *inbuf-=in_len;
-    }
-    return out_samples;
-}
-
-int setup_resample(oe_enc_opt *opt, int complexity, long outfreq) {
-    resampler *rs = calloc(1, sizeof(resampler));
-    int err;
-
-    rs->bufsize = 5760*2; /* Have at least two output frames worth, just in case of ugly ratios */
-    rs->bufpos = 0;
-
-    rs->real_reader = opt->read_samples;
-    rs->real_readdata = opt->readdata;
-    rs->channels = opt->channels;
-    rs->done = 0;
-    rs->resampler = speex_resampler_init(rs->channels, opt->rate, outfreq, complexity, &err);
-    if(err!=0)fprintf(stderr, _("resampler error: %s\n"), speex_resampler_strerror(err));
-
-    speex_resampler_skip_zeros(rs->resampler);
-
-    rs->bufs = malloc(sizeof(float) * rs->bufsize * opt->channels);
-
-    opt->read_samples = read_resampled;
-    opt->readdata = rs;
-    if(opt->total_samples_per_channel)
-        opt->total_samples_per_channel = (opus_int64)
-            ((double)opt->total_samples_per_channel * ((double)outfreq/(double)opt->rate));
-    opt->rate = outfreq;
-
-    return 0;
-}
-
-void clear_resample(oe_enc_opt *opt) {
-    resampler *rs = opt->readdata;
-
-    opt->read_samples = rs->real_reader;
-    opt->readdata = rs->real_readdata;
-    speex_resampler_destroy(rs->resampler);
-
-    free(rs->bufs);
-
-    free(rs);
 }
 
 typedef struct {
--- a/src/diag_range.c
+++ b/src/diag_range.c
@@ -216,7 +216,7 @@
    return count;
 }
 
-void save_range(FILE *frange, int frame_size, unsigned char *packet, int nbBytes, opus_uint32 *rngs, int nb_streams){
+void save_range(FILE *frange, int frame_size, const unsigned char *packet, int nbBytes, opus_uint32 *rngs, int nb_streams){
   int i, parsed_size;
   const unsigned char *subpkt;
   static const char *bw_strings[5]={"NB","MB","WB","SWB","FB"};
--- a/src/diag_range.h
+++ b/src/diag_range.h
@@ -25,4 +25,4 @@
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-void save_range(FILE *frange, int frame_size, unsigned char *packet, int nbBytes, opus_uint32 *rngs, int nb_streams);
+void save_range(FILE *frange, int frame_size, const unsigned char *packet, int nbBytes, opus_uint32 *rngs, int nb_streams);
--- /dev/null
+++ b/src/encoder.h
@@ -1,0 +1,103 @@
+#ifndef __OPUSENC_H
+#define __OPUSENC_H
+
+#include <opus_types.h>
+#include <opusenc.h>
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(X) gettext(X)
+#else
+#define _(X) (X)
+#define textdomain(X)
+#define bindtextdomain(X, Y)
+#endif
+#ifdef gettext_noop
+#define N_(X) gettext_noop(X)
+#else
+#define N_(X) (X)
+#endif
+
+typedef long (*audio_read_func)(void *src, float *buffer, int samples);
+
+typedef struct
+{
+    audio_read_func read_samples;
+    void *readdata;
+    opus_int64 total_samples_per_channel;
+    int rawmode;
+    int channels;
+    long rate;
+    int gain;
+    int samplesize;
+    int endianness;
+    char *infilename;
+    int ignorelength;
+    int skip;
+    int extraout;
+    OggOpusComments *comments;
+    int copy_comments;
+    int copy_pictures;
+} oe_enc_opt;
+
+void setup_scaler(oe_enc_opt *opt, float scale);
+void clear_scaler(oe_enc_opt *opt);
+int setup_downmix(oe_enc_opt *opt, int out_channels);
+void clear_downmix(oe_enc_opt *opt);
+
+typedef struct
+{
+    int (*id_func)(unsigned char *buf, int len); /* Returns true if can load file */
+    int id_data_len; /* Amount of data needed to id whether this can load the file */
+    int (*open_func)(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+    void (*close_func)(void *);
+    char *format;
+    char *description;
+} input_format;
+
+typedef struct {
+    unsigned short format;
+    unsigned short channels;
+    unsigned int samplerate;
+    unsigned int bytespersec;
+    unsigned short align;
+    unsigned short samplesize;
+    unsigned int mask;
+} wav_fmt;
+
+typedef struct {
+    unsigned short channels;
+    short samplesize;
+    opus_int64 totalsamples;
+    opus_int64 samplesread;
+    FILE *f;
+    short bigendian;
+    short unsigned8bit;
+    int *channel_permute;
+} wavfile;
+
+typedef struct {
+    short channels;
+    unsigned int totalframes;
+    short samplesize;
+    double rate;
+    unsigned int offset;
+    unsigned int blocksize;
+} aiff_fmt;
+
+typedef wavfile aifffile; /* They're the same */
+
+input_format *open_audio_file(FILE *in, oe_enc_opt *opt);
+
+int raw_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+int wav_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+int aiff_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+int wav_id(unsigned char *buf, int len);
+int aiff_id(unsigned char *buf, int len);
+void wav_close(void *);
+void raw_close(void *);
+
+long wav_read(void *, float *buffer, int samples);
+long wav_ieee_read(void *, float *buffer, int samples);
+
+#endif /* __OPUSENC_H */
--- a/src/flac.c
+++ b/src/flac.c
@@ -168,7 +168,7 @@
                _("Discarding comment not in the form name=value\n"));
             continue;
           }
-          comment_add(&inopt->comments,&inopt->comments_length,NULL,entry);
+          ope_comments_add_string(inopt->comments,entry);
         }
         setlocale(LC_NUMERIC,saved_locale);
         /*Set the header gain to the album gain after converting to the R128
@@ -184,8 +184,7 @@
           gain=256*(track_gain-album_gain)+0.5;
           track_gain_val=gain<-32768?-32768:gain<32767?(int)floor(gain):32767;
           sprintf(track_gain_buf,"%i",track_gain_val);
-          comment_add(&inopt->comments,&inopt->comments_length,
-             "R128_TRACK_GAIN",track_gain_buf);
+          ope_comments_add(inopt->comments, "R128_TRACK_GAIN",track_gain_buf);
         }
       }
       break;
@@ -231,8 +230,7 @@
         b64=(char *)malloc(b64_sz);
         base64_encode(b64,buf,buf_sz);
         free(buf);
-        comment_add(&inopt->comments,&inopt->comments_length,
-           "METADATA_BLOCK_PICTURE",b64);
+        ope_comments_add(inopt->comments, "METADATA_BLOCK_PICTURE", b64);
         free(b64);
       }
       break;
--- a/src/flac.h
+++ b/src/flac.h
@@ -26,7 +26,7 @@
 # define __FLAC_H
 # include <stdio.h>
 # include "os_support.h"
-# include "opusenc.h"
+# include "encoder.h"
 # if defined(HAVE_LIBFLAC)
 #  include <FLAC/stream_decoder.h>
 #  include <FLAC/metadata.h>
--- a/src/lpc.c
+++ /dev/null
@@ -1,166 +1,0 @@
-/********************************************************************
- *                                                                  *
- * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
- * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
- * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
- * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
- *                                                                  *
- * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009             *
- * by the Xiph.Org Foundation http://www.xiph.org/                  *
- *                                                                  *
- ********************************************************************
-
-  function: LPC low level routines
-  last mod: $Id: lpc.c 16227 2009-07-08 06:58:46Z xiphmont $
-
- ********************************************************************/
-
-/* Some of these routines (autocorrelator, LPC coefficient estimator)
-   are derived from code written by Jutta Degener and Carsten Bormann;
-   thus we include their copyright below.  The entirety of this file
-   is freely redistributable on the condition that both of these
-   copyright notices are preserved without modification.  */
-
-/* Preserved Copyright: *********************************************/
-
-/* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
-Technische Universita"t Berlin
-
-Any use of this software is permitted provided that this notice is not
-removed and that neither the authors nor the Technische Universita"t
-Berlin are deemed to have made any representations as to the
-suitability of this software for any purpose nor are held responsible
-for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR
-THIS SOFTWARE.
-
-As a matter of courtesy, the authors request to be informed about uses
-this software has found, about bugs in this software, and about any
-improvements that may be of general interest.
-
-Berlin, 28.11.1994
-Jutta Degener
-Carsten Bormann
-
-*********************************************************************/
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "lpc.h"
-
-/* Autocorrelation LPC coeff generation algorithm invented by
-   N. Levinson in 1947, modified by J. Durbin in 1959. */
-
-/* Input : n elements of time doamin data
-   Output: m lpc coefficients, excitation energy */
-
-float vorbis_lpc_from_data(float *data,float *lpci,int n,int m,int stride){
-  double *aut=malloc(sizeof(*aut)*(m+1));
-  double *lpc=malloc(sizeof(*lpc)*(m));
-  double error;
-  double epsilon;
-  int i,j;
-
-  if(!aut || !lpc)return 0;
-
-  /* autocorrelation, p+1 lag coefficients */
-  j=m+1;
-  while(j--){
-    double d=0; /* double needed for accumulator depth */
-    for(i=j;i<n;i++)d+=(double)data[i*stride]*data[(i-j)*stride];
-    aut[j]=d;
-  }
-
-  /* Generate lpc coefficients from autocorr values */
-
-  /* set our noise floor to about -100dB */
-  error=aut[0] * (1. + 1e-10);
-  epsilon=1e-9*aut[0]+1e-10;
-
-  for(i=0;i<m;i++){
-    double r= -aut[i+1];
-
-    if(error<epsilon){
-      memset(lpc+i,0,(m-i)*sizeof(*lpc));
-      goto done;
-    }
-
-    /* Sum up this iteration's reflection coefficient; note that in
-       Vorbis we don't save it.  If anyone wants to recycle this code
-       and needs reflection coefficients, save the results of 'r' from
-       each iteration. */
-
-    for(j=0;j<i;j++)r-=lpc[j]*aut[i-j];
-    r/=error;
-
-    /* Update LPC coefficients and total error */
-
-    lpc[i]=r;
-    for(j=0;j<i/2;j++){
-      double tmp=lpc[j];
-
-      lpc[j]+=r*lpc[i-1-j];
-      lpc[i-1-j]+=r*tmp;
-    }
-    if(i&1)lpc[j]+=lpc[j]*r;
-
-    error*=1.-r*r;
-
-  }
-
- done:
-
-  /* slightly damp the filter */
-  {
-    double g = .99;
-    double damp = g;
-    for(j=0;j<m;j++){
-      lpc[j]*=damp;
-      damp*=g;
-    }
-  }
-
-  for(j=0;j<m;j++)lpci[j]=(float)lpc[j];
-
-  /* we need the error value to know how big an impulse to hit the
-     filter with later */
-  free(aut);
-  free(lpc);
-  return error;
-}
-
-void vorbis_lpc_predict(float *coeff,float *prime,int m,
-                     float *data,long n,int stride){
-
-  /* in: coeff[0...m-1] LPC coefficients
-         prime[0...m-1] initial values (allocated size of n+m-1)
-    out: data[0...n-1] data samples */
-
-  long i,j,o,p;
-  float y;
-  float *work=malloc(sizeof(*work)*(m+n));
-
-  if(!work)return;
-
-  if(!prime)
-    for(i=0;i<m;i++)
-      work[i]=0.f;
-  else
-    for(i=0;i<m;i++)
-      work[i]=prime[i*stride];
-
-  for(i=0;i<n;i++){
-    y=0;
-    o=i;
-    p=m;
-    for(j=0;j<m;j++)
-      y-=work[o++]*coeff[--p];
-
-    data[i*stride]=work[o]=y;
-  }
-  free(work);
-}
--- a/src/lpc.h
+++ /dev/null
@@ -1,27 +1,0 @@
-/********************************************************************
- *                                                                  *
- * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
- * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
- * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
- * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
- *                                                                  *
- * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007             *
- * by the Xiph.Org Foundation http://www.xiph.org/                  *
- *                                                                  *
- ********************************************************************
-
-  function: LPC low level routines
-  last mod: $Id: lpc.h 16037 2009-05-26 21:10:58Z xiphmont $
-
- ********************************************************************/
-
-#ifndef _V_LPC_H_
-#define _V_LPC_H_
-
-/* simple linear scale LPC code */
-extern float vorbis_lpc_from_data(float *data,float *lpc,int n,int m,int stride);
-
-extern void vorbis_lpc_predict(float *coeff,float *prime,int m,
-                               float *data,long n,int stride);
-
-#endif
--- a/src/opus_header.c
+++ b/src/opus_header.c
@@ -58,49 +58,11 @@
 */
 
 typedef struct {
-   unsigned char *data;
-   int maxlen;
-   int pos;
-} Packet;
-
-typedef struct {
    const unsigned char *data;
    int maxlen;
    int pos;
 } ROPacket;
 
-static int write_uint32(Packet *p, ogg_uint32_t val)
-{
-   if (p->pos>p->maxlen-4)
-      return 0;
-   p->data[p->pos  ] = (val    ) & 0xFF;
-   p->data[p->pos+1] = (val>> 8) & 0xFF;
-   p->data[p->pos+2] = (val>>16) & 0xFF;
-   p->data[p->pos+3] = (val>>24) & 0xFF;
-   p->pos += 4;
-   return 1;
-}
-
-static int write_uint16(Packet *p, ogg_uint16_t val)
-{
-   if (p->pos>p->maxlen-2)
-      return 0;
-   p->data[p->pos  ] = (val    ) & 0xFF;
-   p->data[p->pos+1] = (val>> 8) & 0xFF;
-   p->pos += 2;
-   return 1;
-}
-
-static int write_chars(Packet *p, const unsigned char *str, int nb_chars)
-{
-   int i;
-   if (p->pos>p->maxlen-nb_chars)
-      return 0;
-   for (i=0;i<nb_chars;i++)
-      p->data[p->pos++] = str[i];
-   return 1;
-}
-
 static int read_uint32(ROPacket *p, ogg_uint32_t *val)
 {
    if (p->pos>p->maxlen-4)
@@ -214,61 +176,6 @@
    if ((h->version==0 || h->version==1) && p.pos != len)
       return 0;
    return 1;
-}
-
-int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len)
-{
-   int i;
-   Packet p;
-   unsigned char ch;
-
-   p.data = packet;
-   p.maxlen = len;
-   p.pos = 0;
-   if (len<19)return 0;
-   if (!write_chars(&p, (const unsigned char*)"OpusHead", 8))
-      return 0;
-   /* Version is 1 */
-   ch = 1;
-   if (!write_chars(&p, &ch, 1))
-      return 0;
-
-   ch = h->channels;
-   if (!write_chars(&p, &ch, 1))
-      return 0;
-
-   if (!write_uint16(&p, h->preskip))
-      return 0;
-
-   if (!write_uint32(&p, h->input_sample_rate))
-      return 0;
-
-   if (!write_uint16(&p, h->gain))
-      return 0;
-
-   ch = h->channel_mapping;
-   if (!write_chars(&p, &ch, 1))
-      return 0;
-
-   if (h->channel_mapping != 0)
-   {
-      ch = h->nb_streams;
-      if (!write_chars(&p, &ch, 1))
-         return 0;
-
-      ch = h->nb_coupled;
-      if (!write_chars(&p, &ch, 1))
-         return 0;
-
-      /* Multi-stream support */
-      for (i=0;i<h->channels;i++)
-      {
-         if (!write_chars(&p, &h->stream_map[i], 1))
-            return 0;
-      }
-   }
-
-   return p.pos;
 }
 
 /* This is just here because it's a convenient file linked by both opusenc and
--- a/src/opus_header.h
+++ b/src/opus_header.h
@@ -44,7 +44,6 @@
 } OpusHeader;
 
 int opus_header_parse(const unsigned char *header, int len, OpusHeader *h);
-int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len);
 
 extern const int wav_permute_matrix[8][8];
 
--- a/src/opusenc.c
+++ b/src/opusenc.c
@@ -61,14 +61,13 @@
 
 #include <opus.h>
 #include <opus_multistream.h>
-#include <ogg/ogg.h>
 #include "wav_io.h"
 
-#include "picture.h"
 #include "opus_header.h"
-#include "opusenc.h"
+#include "encoder.h"
 #include "diag_range.h"
 #include "cpusupport.h"
+#include <opusenc.h>
 
 #ifdef VALGRIND
 #include <valgrind/memcheck.h>
@@ -79,19 +78,6 @@
 #define VG_CHECK(x,y)
 #endif
 
-static void comment_init(char **comments, int* length, const char *vendor_string);
-static void comment_pad(char **comments, int* length, int amount);
-
-/*Write an Ogg page to a file pointer*/
-static inline int oe_write_page(ogg_page *page, FILE *fp)
-{
-   int written;
-   written=fwrite(page->header,1,page->header_len, fp);
-   written+=fwrite(page->body,1,page->body_len, fp);
-   return written;
-}
-
-#define MAX_FRAME_BYTES 61295
 #define IMIN(a,b) ((a) < (b) ? (a) : (b))   /**< Minimum int value.   */
 #define IMAX(a,b) ((a) > (b) ? (a) : (b))   /**< Maximum int value.   */
 
@@ -206,25 +192,14 @@
   printf("  be specified to attach multiple pictures. There may only be one\n");
   printf("  picture each of type 1 and 2 in a file.\n");
   printf("\n");
-  printf("  MEDIA-TYPE is optional. If left blank, it will be detected from the\n");
-  printf("  file. For best compatibility with players, use pictures with a\n");
-  printf("  MEDIA-TYPE of image/jpeg or image/png. The MEDIA-TYPE can also be\n");
-  printf("  \"-->\" to mean that FILENAME is actually a URL to an image, though\n");
-  printf("  this use is discouraged. The file at the URL will not be fetched.\n");
-  printf("  The URL itself is stored in the metadata.\n");
+  printf("  MEDIA-TYPE is optional and is now ignored.\n");
   printf("\n");
   printf("  DESCRIPTION is optional. The default is an empty string.\n");
   printf("\n");
-  printf("  The next part specifies the resolution and color information. If\n");
-  printf("  the MEDIA-TYPE is image/jpeg, image/png, or image/gif, this can\n");
-  printf("  usually be left empty and the information will be read from the\n");
-  printf("  file.  Otherwise, you must specify the width in pixels, height in\n");
-  printf("  pixels, and color depth in bits-per-pixel. If the image has indexed\n");
-  printf("  colors you should also specify the number of colors used. If possible,\n");
-  printf("  these are checked against the file for accuracy.\n");
+  printf("  The next part specifies the resolution and color information, but\n");
+  printf("  is now ignored.\n");
   printf("\n");
-  printf("  FILENAME is the path to the picture file to be imported, or the URL\n");
-  printf("  if the MEDIA-TYPE is \"-->\".\n");
+  printf("  FILENAME is the path to the picture file to be imported.\n");
 }
 
 static inline void print_time(double seconds)
@@ -241,6 +216,62 @@
   if(seconds>0)fprintf(stderr," %0.4g second%s",seconds,seconds!=1?"s":"");
 }
 
+typedef struct {
+  OggOpusEnc *enc;
+  FILE *fout;
+  opus_int64 total_bytes;
+  opus_int64 bytes_written;
+  opus_int64 nb_encoded;
+  opus_int64 pages_out;
+  opus_int64 packets_out;
+  int peak_bytes;
+  int min_bytes;
+  int last_length;
+  FILE *frange;
+} EncData;
+
+int write_callback(void *user_data, const unsigned char *ptr, opus_int32 len) {
+  EncData *data = (EncData*)user_data;
+  data->bytes_written += len;
+  data->pages_out++;
+  return fwrite(ptr, 1, len, data->fout) != (size_t)len;
+}
+
+int close_callback(void *user_data) {
+  EncData *obj = (EncData*)user_data;
+  int ret = 0;
+  if (obj->fout) ret = fclose(obj->fout);
+  return ret;
+}
+
+int packet_callback(void *user_data, const unsigned char *packet_ptr, opus_int32 packet_len, opus_uint32 flags) {
+  int frame_size;
+  EncData *data = (EncData*)user_data;
+  if (packet_ptr[0] == 'O' && packet_ptr[1] == 'p') return 0;
+  data->total_bytes+=packet_len;
+  data->peak_bytes=IMAX(packet_len,data->peak_bytes);
+  data->min_bytes=IMIN(packet_len,data->min_bytes);
+  frame_size = opus_packet_get_samples_per_frame(packet_ptr, 48000);
+  data->nb_encoded += frame_size;
+  data->packets_out++;
+  data->last_length = packet_len;
+  if(data->frange!=NULL){
+    int ret;
+    opus_uint32 rngs[256];
+    OpusEncoder *oe;
+    int nb_streams;
+    for(nb_streams=0;;nb_streams++){
+      ret=ope_encoder_ctl(data->enc,OPUS_MULTISTREAM_GET_ENCODER_STATE(nb_streams,&oe));
+      if (ret != 0 || oe == NULL) break;
+      ret=opus_encoder_ctl(oe,OPUS_GET_FINAL_RANGE(&rngs[nb_streams]));
+    }
+    save_range(data->frange,frame_size,packet_ptr,packet_len,
+               rngs,nb_streams);
+  }
+  (void)flags;
+  return 0;
+}
+
 int main(int argc, char **argv)
 {
   static const input_format raw_format = {NULL, 0, raw_open, wav_close, "raw",N_("RAW file reader")};
@@ -288,9 +319,10 @@
   };
   int i, ret;
   int                cline_size;
-  OpusMSEncoder      *st;
+  OpusEncCallbacks   callbacks = {write_callback, close_callback};
+  OggOpusEnc         *enc;
+  EncData            data;
   const char         *opus_version;
-  unsigned char      *packet;
   float              *input;
   /*I/O*/
   oe_enc_opt         inopt;
@@ -299,28 +331,11 @@
   char               *outFile;
   char               *range_file;
   FILE               *fin;
-  FILE               *fout;
-  FILE               *frange;
-  ogg_stream_state   os;
-  ogg_page           og;
-  ogg_packet         op;
-  ogg_int64_t        last_granulepos=0;
-  ogg_int64_t        enc_granulepos=0;
-  ogg_int64_t        original_samples=0;
-  ogg_int32_t        id=-1;
-  int                last_segments=0;
-  OpusHeader         header;
+  int                eos;
   char               ENCODER_string[1024];
   /*Counters*/
-  opus_int64         nb_encoded=0;
-  opus_int64         bytes_written=0;
-  opus_int64         pages_out=0;
-  opus_int64         total_bytes=0;
   opus_int64         total_samples=0;
-  opus_int32         nbBytes;
   opus_int32         nb_samples;
-  opus_int32         peak_bytes=0;
-  opus_int32         min_bytes;
   time_t             start_time;
   time_t             stop_time;
   time_t             last_spin=0;
@@ -327,11 +342,10 @@
   int                last_spin_len=0;
   /*Settings*/
   int                quiet=0;
-  int                max_frame_bytes;
   opus_int32         bitrate=-1;
   opus_int32         rate=48000;
-  opus_int32         coding_rate=48000;
   opus_int32         frame_size=960;
+  opus_int32         opus_frame_param = OPUS_FRAMESIZE_20_MS;
   int                chan=2;
   int                with_hard_cbr=0;
   int                with_cvbr=0;
@@ -345,6 +359,8 @@
   int                comment_padding=512;
   int                serialno;
   opus_int32         lookahead=0;
+  int                nb_streams;
+  int                nb_coupled;
 #ifdef WIN_UNICODE
    int argc_utf8;
    char **argv_utf8;
@@ -363,11 +379,10 @@
 #endif
 
   opt_ctls_ctlval=NULL;
-  frange=NULL;
   range_file=NULL;
   in_format=NULL;
   inopt.channels=chan;
-  inopt.rate=coding_rate=rate;
+  inopt.rate=rate;
   /* 0 dB gain is recommended unless you know what you're doing */
   inopt.gain=0;
   inopt.samplesize=16;
@@ -381,15 +396,16 @@
   srand(((getpid()&65535)<<15)^start_time);
   serialno=rand();
 
+  inopt.comments = ope_comments_create();
   opus_version=opus_get_version_string();
   /*Vendor string should just be the encoder library,
     the ENCODER comment specifies the tool used.*/
-  comment_init(&inopt.comments, &inopt.comments_length, opus_version);
   snprintf(ENCODER_string, sizeof(ENCODER_string), "opusenc from %s %s",PACKAGE_NAME,PACKAGE_VERSION);
-  comment_add(&inopt.comments, &inopt.comments_length, "ENCODER", ENCODER_string);
+  ope_comments_add(inopt.comments, "ENCODER", ENCODER_string);
 
   /*Process command-line options*/
   cline_size=0;
+  data.frange = NULL;
   while(1){
     int c;
     int save_cmd=1;
@@ -473,6 +489,18 @@
             exit(1);
           }
         }else if(strcmp(long_options[option_index].name,"framesize")==0){
+          if(strcmp(optarg,"2.5")==0)opus_frame_param=OPUS_FRAMESIZE_2_5_MS;
+          else if(strcmp(optarg,"5")==0)opus_frame_param=OPUS_FRAMESIZE_5_MS;
+          else if(strcmp(optarg,"10")==0)opus_frame_param=OPUS_FRAMESIZE_10_MS;
+          else if(strcmp(optarg,"20")==0)opus_frame_param=OPUS_FRAMESIZE_20_MS;
+          else if(strcmp(optarg,"40")==0)opus_frame_param=OPUS_FRAMESIZE_40_MS;
+          else if(strcmp(optarg,"60")==0)opus_frame_param=OPUS_FRAMESIZE_60_MS;
+          else{
+            fprintf(stderr,"Invalid framesize: %s\n",optarg);
+            fprintf(stderr,"Framesize must be 2.5, 5, 10, 20, 40, or 60.\n");
+            exit(1);
+          }
+
           if(strcmp(optarg,"2.5")==0)frame_size=120;
           else if(strcmp(optarg,"5")==0)frame_size=240;
           else if(strcmp(optarg,"10")==0)frame_size=480;
@@ -525,9 +553,9 @@
           opt_ctls_ctlval[opt_ctls*3+2]=atoi(spos+1);
           opt_ctls++;
         }else if(strcmp(long_options[option_index].name,"save-range")==0){
-          frange=fopen_utf8(optarg,"w");
+          data.frange=fopen_utf8(optarg,"w");
           save_cmd=0;
-          if(frange==NULL){
+          if(data.frange==NULL){
             perror(optarg);
             fprintf(stderr,"Could not open save-range file: %s\n",optarg);
             fprintf(stderr,"Must provide a writable file name.\n");
@@ -541,35 +569,93 @@
             fprintf(stderr, "Comments must be of the form name=value\n");
             exit(1);
           }
-          comment_add(&inopt.comments, &inopt.comments_length, NULL, optarg);
+          ope_comments_add_string(inopt.comments, optarg);
         }else if(strcmp(long_options[option_index].name,"artist")==0){
           save_cmd=0;
-          comment_add(&inopt.comments, &inopt.comments_length, "artist", optarg);
+          ope_comments_add(inopt.comments, "artist", optarg);
         } else if(strcmp(long_options[option_index].name,"title")==0){
           save_cmd=0;
-          comment_add(&inopt.comments, &inopt.comments_length, "title", optarg);
+          ope_comments_add(inopt.comments, "title", optarg);
         } else if(strcmp(long_options[option_index].name,"album")==0){
           save_cmd=0;
-          comment_add(&inopt.comments, &inopt.comments_length, "album", optarg);
+          ope_comments_add(inopt.comments, "album", optarg);
         } else if(strcmp(long_options[option_index].name,"date")==0){
           save_cmd=0;
-          comment_add(&inopt.comments, &inopt.comments_length, "date", optarg);
+          ope_comments_add(inopt.comments, "date", optarg);
         } else if(strcmp(long_options[option_index].name,"genre")==0){
           save_cmd=0;
-          comment_add(&inopt.comments, &inopt.comments_length, "genre", optarg);
+          ope_comments_add(inopt.comments, "genre", optarg);
         } else if(strcmp(long_options[option_index].name,"picture")==0){
-          const char *error_message;
-          char       *picture_data;
-          save_cmd=0;
-          picture_data=parse_picture_specification(optarg,&error_message,
-                                                   &seen_file_icons);
-          if(picture_data==NULL){
-            fprintf(stderr,"Error parsing picture option: %s\n",error_message);
+          const char    *media_type;
+          const char    *media_type_end;
+          const char    *description;
+          const char    *description_end;
+          const char    *filename;
+          const char    *spec;
+          char *description_copy;
+          FILE *picture_file;
+          int picture_type;
+          spec = optarg;
+          picture_type=3;
+          media_type=media_type_end=description=description_end=filename=spec;
+          picture_file=fopen_utf8(filename,"rb");
+          description_copy=NULL;
+          if(picture_file==NULL&&strchr(spec,'|')){
+            const char *p;
+            char       *q;
+            unsigned long val;
+            /*We don't have a plain file, and there is a pipe character: assume it's
+              the full form of the specification.*/
+            val=strtoul(spec,&q,10);
+            if(*q!='|'||val>20){
+              fprintf(stderr, "Invalid picture type: %.*s\n",
+                (int)strcspn(spec,"|"), spec);
+              exit(1);
+            }
+            /*An empty field implies a default of 'Cover (front)'.*/
+            if(spec!=q)picture_type=val;
+            media_type=q+1;
+            media_type_end=media_type+strcspn(media_type,"|");
+            if(*media_type_end=='|'){
+              description=media_type_end+1;
+              description_end=description+strcspn(description,"|");
+              if(*description_end=='|'){
+                p=description_end+1;
+                /*Ignore WIDTHxHEIGHTxDEPTH/COLORS.*/
+                p+=strcspn(p,"|");
+                if(*p=='|'){
+                  filename=p+1;
+                }
+              }
+            }
+            if (filename==spec) {
+              fprintf(stderr, "Not enough fields in picture specification: %s\n", spec);
+              exit(1);
+            }
+            if (media_type_end-media_type==3 && strncmp("-->",media_type,3)==0) {
+              fprintf(stderr, "Picture URLs are no longer supported.\n");
+              exit(1);
+            }
+            if (picture_type>=1&&picture_type<=2&&(seen_file_icons&picture_type)) {
+              fprintf(stderr, "Only one picture of type %d (%s) is allowed.\n",
+                picture_type, picture_type==1 ? "32x32 icon" : "icon");
+              exit(1);
+            }
+          }
+          if (picture_file) fclose(picture_file);
+          if (description_end-description != 0) {
+            size_t len = description_end-description;
+            description_copy = malloc(len+1);
+            memcpy(description_copy, description, len);
+            description_copy[len]=0;
+          }
+          ret = ope_comments_add_picture(inopt.comments, filename, picture_type, description_copy);
+          if (ret != OPE_OK) {
+            fprintf(stderr, "Error: %s: %s\n", ope_strerror(ret), filename);
             exit(1);
           }
-          comment_add(&inopt.comments,&inopt.comments_length,
-                      "METADATA_BLOCK_PICTURE",picture_data);
-          free(picture_data);
+          if (description_copy) free(description_copy);
+          if (picture_type>=1&&picture_type<=2) seen_file_icons|=picture_type;
         } else if(strcmp(long_options[option_index].name,"padding")==0){
           comment_padding=atoi(optarg);
         } else if(strcmp(long_options[option_index].name,"discard-comments")==0){
@@ -619,7 +705,7 @@
   inFile=argv_utf8[optind];
   outFile=argv_utf8[optind+1];
 
-  if(cline_size>0)comment_add(&inopt.comments, &inopt.comments_length, "ENCODER_OPTIONS", ENCODER_string);
+  if(cline_size>0)ope_comments_add(inopt.comments, "ENCODER_OPTIONS", ENCODER_string);
 
   if(strcmp(inFile, "-")==0){
 #if defined WIN32 || defined _WIN32
@@ -669,52 +755,23 @@
   chan=inopt.channels;
   inopt.skip=0;
 
-  /*In order to code the complete length we'll need to do a little padding*/
-  setup_padder(&inopt,&original_samples);
-
-  if(rate>24000)coding_rate=48000;
-  else if(rate>16000)coding_rate=24000;
-  else if(rate>12000)coding_rate=16000;
-  else if(rate>8000)coding_rate=12000;
-  else coding_rate=8000;
-
-  frame_size=frame_size/(48000/coding_rate);
-
-  /*Scale the resampler complexity, but only for 48000 output because
-    the near-cutoff behavior matters a lot more at lower rates.*/
-  if(rate!=coding_rate)setup_resample(&inopt,coding_rate==48000?(complexity+1)/2:5,coding_rate);
-
-  if(rate!=coding_rate&&complexity!=10&&!quiet){
-    fprintf(stderr,"Notice: Using resampling with complexity<10.\n");
-    fprintf(stderr,"Opusenc is fastest with 48, 24, 16, 12, or 8kHz input.\n\n");
-  }
-
-  /*OggOpus headers*/ /*FIXME: broke forcemono*/
-  header.channels=chan;
-  header.channel_mapping=header.channels>8?255:chan>2;
-  header.input_sample_rate=rate;
-  header.gain=inopt.gain;
-
   /*Initialize Opus encoder*/
-  /*Frame sizes <10ms can only use the MDCT modes, so we switch on RESTRICTED_LOWDELAY
-    to save the extra 4ms of codec lookahead when we'll be using only small frames.*/
-  st=opus_multistream_surround_encoder_create(coding_rate, chan, header.channel_mapping, &header.nb_streams, &header.nb_coupled,
-     header.stream_map, frame_size<480/(48000/coding_rate)?OPUS_APPLICATION_RESTRICTED_LOWDELAY:OPUS_APPLICATION_AUDIO, &ret);
-  if(ret!=OPUS_OK){
-    fprintf(stderr, "Error cannot create encoder: %s\n", opus_strerror(ret));
-    exit(1);
+  enc = ope_encoder_create_callbacks(&callbacks, &data, inopt.comments, rate, chan, chan>8?255:chan>2, NULL);
+  ope_encoder_ctl(enc, OPE_SET_MUXING_DELAY(max_ogg_delay));
+  ope_encoder_ctl(enc, OPE_SET_SERIALNO(serialno));
+  ope_encoder_ctl(enc, OPE_SET_PACKET_CALLBACK(packet_callback, &data));
+  ope_encoder_ctl(enc, OPUS_SET_EXPERT_FRAME_DURATION(opus_frame_param));
+  ope_encoder_ctl(enc, OPE_SET_COMMENT_PADDING(comment_padding));
+  for(nb_streams=0;;nb_streams++){
+    OpusEncoder *oe;
+    ret=ope_encoder_ctl(enc,OPUS_MULTISTREAM_GET_ENCODER_STATE(nb_streams,&oe));
+    if (ret != 0 || oe == NULL) break;
   }
+  nb_coupled = chan - nb_streams;
 
-  min_bytes=max_frame_bytes=(1275*3+7)*header.nb_streams;
-  packet=malloc(sizeof(unsigned char)*max_frame_bytes);
-  if(packet==NULL){
-    fprintf(stderr,"Error allocating packet buffer.\n");
-    exit(1);
-  }
-
   if(bitrate<0){
     /*Lower default rate for sampling rates [8000-44100) by a factor of (rate+16k)/(64k)*/
-    bitrate=((64000*header.nb_streams+32000*header.nb_coupled)*
+    bitrate=((64000*nb_streams+32000*nb_coupled)*
              (IMIN(48,IMAX(8,((rate<44100?rate:48000)+1000)/1000))+16)+32)>>6;
   }
 
@@ -725,13 +782,13 @@
   }
   bitrate=IMIN(chan*256000,bitrate);
 
-  ret=opus_multistream_encoder_ctl(st, OPUS_SET_BITRATE(bitrate));
+  ret = ope_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
   if(ret!=OPUS_OK){
     fprintf(stderr,"Error OPUS_SET_BITRATE returned: %s\n",opus_strerror(ret));
     exit(1);
   }
 
-  ret=opus_multistream_encoder_ctl(st, OPUS_SET_VBR(!with_hard_cbr));
+  ret = ope_encoder_ctl(enc, OPUS_SET_VBR(!with_hard_cbr));
   if(ret!=OPUS_OK){
     fprintf(stderr,"Error OPUS_SET_VBR returned: %s\n",opus_strerror(ret));
     exit(1);
@@ -738,7 +795,7 @@
   }
 
   if(!with_hard_cbr){
-    ret=opus_multistream_encoder_ctl(st, OPUS_SET_VBR_CONSTRAINT(with_cvbr));
+    ret = ope_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(with_cvbr));
     if(ret!=OPUS_OK){
       fprintf(stderr,"Error OPUS_SET_VBR_CONSTRAINT returned: %s\n",opus_strerror(ret));
       exit(1);
@@ -745,13 +802,13 @@
     }
   }
 
-  ret=opus_multistream_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity));
+  ret = ope_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
   if(ret!=OPUS_OK){
     fprintf(stderr,"Error OPUS_SET_COMPLEXITY returned: %s\n",opus_strerror(ret));
     exit(1);
   }
 
-  ret=opus_multistream_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(expect_loss));
+  ret = ope_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(expect_loss));
   if(ret!=OPUS_OK){
     fprintf(stderr,"Error OPUS_SET_PACKET_LOSS_PERC returned: %s\n",opus_strerror(ret));
     exit(1);
@@ -758,7 +815,7 @@
   }
 
 #ifdef OPUS_SET_LSB_DEPTH
-  ret=opus_multistream_encoder_ctl(st, OPUS_SET_LSB_DEPTH(IMAX(8,IMIN(24,inopt.samplesize))));
+  ret = ope_encoder_ctl(enc, OPUS_SET_LSB_DEPTH(IMAX(8,IMIN(24,inopt.samplesize))));
   if(ret!=OPUS_OK){
     fprintf(stderr,"Warning OPUS_SET_LSB_DEPTH returned: %s\n",opus_strerror(ret));
   }
@@ -768,14 +825,14 @@
   for(i=0;i<opt_ctls;i++){
     int target=opt_ctls_ctlval[i*3];
     if(target==-1){
-      ret=opus_multistream_encoder_ctl(st,opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2]);
+      ret = ope_encoder_ctl(enc, opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2]);
       if(ret!=OPUS_OK){
         fprintf(stderr,"Error opus_multistream_encoder_ctl(st,%d,%d) returned: %s\n",opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2],opus_strerror(ret));
         exit(1);
       }
-    }else if(target<header.nb_streams){
+    }else if(target<nb_streams){
       OpusEncoder *oe;
-      opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(target,&oe));
+      ope_encoder_ctl(enc, OPUS_MULTISTREAM_GET_ENCODER_STATE(target,&oe));
       ret=opus_encoder_ctl(oe, opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2]);
       if(ret!=OPUS_OK){
         fprintf(stderr,"Error opus_encoder_ctl(st[%d],%d,%d) returned: %s\n",target,opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2],opus_strerror(ret));
@@ -782,28 +839,25 @@
         exit(1);
       }
     }else{
-      fprintf(stderr,"Error --set-ctl-int target stream %d is higher than the maximum stream number %d.\n",target,header.nb_streams-1);
+      fprintf(stderr,"Error --set-ctl-int target stream %d is higher than the maximum stream number %d.\n",target,nb_streams-1);
       exit(1);
     }
   }
 
   /*We do the lookahead check late so user CTLs can change it*/
-  ret=opus_multistream_encoder_ctl(st, OPUS_GET_LOOKAHEAD(&lookahead));
+  ret = ope_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&lookahead));
   if(ret!=OPUS_OK){
     fprintf(stderr,"Error OPUS_GET_LOOKAHEAD returned: %s\n",opus_strerror(ret));
     exit(1);
   }
   inopt.skip+=lookahead;
-  /*Regardless of the rate we're coding at the ogg timestamping/skip is
-    always timed at 48000.*/
-  header.preskip=inopt.skip*(48000./coding_rate);
   /* Extra samples that need to be read to compensate for the pre-skip */
-  inopt.extraout=(int)header.preskip*(rate/48000.);
+  inopt.extraout=(int)lookahead*(rate/48000.);
 
   if(!quiet){
     int opus_app;
     fprintf(stderr,"Encoding using %s",opus_version);
-    opus_multistream_encoder_ctl(st,OPUS_GET_APPLICATION(&opus_app));
+    ope_encoder_ctl(enc, OPUS_GET_APPLICATION(&opus_app));
     if(opus_app==OPUS_APPLICATION_VOIP)fprintf(stderr," (VoIP)\n");
     else if(opus_app==OPUS_APPLICATION_AUDIO)fprintf(stderr," (audio)\n");
     else if(opus_app==OPUS_APPLICATION_RESTRICTED_LOWDELAY)fprintf(stderr," (low-delay)\n");
@@ -810,18 +864,18 @@
     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,"  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,
-       "%s%d uncoupled",header.nb_coupled>0?", ":"",
-       header.nb_streams-header.nb_coupled);
+            rate/1000.,chan,chan<2?"":"s");
+    fprintf(stderr,"  Output: %d channel%s (",chan,chan<2?"":"s");
+    if(nb_coupled>0)fprintf(stderr,"%d coupled",nb_coupled*2);
+    if(nb_streams-nb_coupled>0)fprintf(stderr,
+       "%s%d uncoupled",nb_coupled>0?", ":"",
+       nb_streams-nb_coupled);
     fprintf(stderr,")\n          %0.2gms packets, %0.6gkbit/sec%s\n",
-       frame_size/(coding_rate/1000.), bitrate/1000.,
+       frame_size/(48000/1000.), bitrate/1000.,
        with_hard_cbr?" CBR":with_cvbr?" CVBR":" VBR");
-    fprintf(stderr," Preskip: %d\n",header.preskip);
+    fprintf(stderr," Preskip: %d\n",lookahead);
 
-    if(frange!=NULL)fprintf(stderr,"         Writing final range file %s\n",range_file);
+    if(data.frange!=NULL)fprintf(stderr,"         Writing final range file %s\n",range_file);
     fprintf(stderr,"\n");
   }
 
@@ -829,72 +883,24 @@
 #if defined WIN32 || defined _WIN32
     _setmode(_fileno(stdout), _O_BINARY);
 #endif
-    fout=stdout;
+    data.fout=stdout;
   }else{
-    fout=fopen_utf8(outFile, "wb");
-    if(!fout){
+    data.fout=fopen_utf8(outFile, "wb");
+    if(!data.fout){
       perror(outFile);
       exit(1);
     }
   }
+  data.enc = enc;
+  data.total_bytes = 0;
+  data.bytes_written = 0;
+  data.nb_encoded = 0;
+  data.packets_out = 0;
+  data.peak_bytes = 0;
+  data.min_bytes = 256*1275*6;
+  data.pages_out = 0;
+  data.last_length = 0;
 
-  /*Initialize Ogg stream struct*/
-  if(ogg_stream_init(&os, serialno)==-1){
-    fprintf(stderr,"Error: stream init failed\n");
-    exit(1);
-  }
-
-  /*Write header*/
-  {
-    /*The Identification Header is 19 bytes, plus a Channel Mapping Table for
-      mapping families other than 0. The Channel Mapping Table is 2 bytes +
-      1 byte per channel. Because the maximum number of channels is 255, the
-      maximum size of this header is 19 + 2 + 255 = 276 bytes.*/
-    unsigned char header_data[276];
-    int packet_size=opus_header_to_packet(&header, header_data, sizeof(header_data));
-    op.packet=header_data;
-    op.bytes=packet_size;
-    op.b_o_s=1;
-    op.e_o_s=0;
-    op.granulepos=0;
-    op.packetno=0;
-    ogg_stream_packetin(&os, &op);
-
-    while((ret=ogg_stream_flush(&os, &og))){
-      if(!ret)break;
-      ret=oe_write_page(&og, fout);
-      if(ret!=og.header_len+og.body_len){
-        fprintf(stderr,"Error: failed writing header to output stream\n");
-        exit(1);
-      }
-      bytes_written+=ret;
-      pages_out++;
-    }
-
-    comment_pad(&inopt.comments, &inopt.comments_length, comment_padding);
-    op.packet=(unsigned char *)inopt.comments;
-    op.bytes=inopt.comments_length;
-    op.b_o_s=0;
-    op.e_o_s=0;
-    op.granulepos=0;
-    op.packetno=1;
-    ogg_stream_packetin(&os, &op);
-  }
-
-  /* writing the rest of the Opus header packets */
-  while((ret=ogg_stream_flush(&os, &og))){
-    if(!ret)break;
-    ret=oe_write_page(&og, fout);
-    if(ret!=og.header_len + og.body_len){
-      fprintf(stderr,"Error: failed writing header to output stream\n");
-      exit(1);
-    }
-    bytes_written+=ret;
-    pages_out++;
-  }
-
-  free(inopt.comments);
-
   input=malloc(sizeof(float)*frame_size*chan);
   if(input==NULL){
     fprintf(stderr,"Error: couldn't allocate sample buffer.\n");
@@ -901,149 +907,37 @@
     exit(1);
   }
 
+  eos = 0;
   /*Main encoding loop (one frame per iteration)*/
-  nb_samples=-1;
-  while(!op.e_o_s){
-    int size_segments,cur_frame_size;
-    id++;
+  while(!eos){
+    nb_samples = inopt.read_samples(inopt.readdata,input,frame_size);
+    total_samples+=nb_samples;
 
-    if(nb_samples<0){
-      nb_samples = inopt.read_samples(inopt.readdata,input,frame_size);
-      total_samples+=nb_samples;
-    }
-
+    ope_encoder_write_float(enc, input, nb_samples);
     if(start_time==0){
       start_time = time(NULL);
     }
 
-    cur_frame_size=frame_size;
+    if (nb_samples < frame_size) eos = 1;
 
-    if(nb_samples<cur_frame_size){
-      op.e_o_s=1;
-      /*Avoid making the final packet 20ms or more longer than needed.*/
-      cur_frame_size-=((cur_frame_size-(nb_samples>0?nb_samples:1))
-        /(coding_rate/50))*(coding_rate/50);
-      /*No fancy end padding, just fill with zeros for now.*/
-      for(i=nb_samples*chan;i<cur_frame_size*chan;i++)input[i]=0;
-    }
-
-    /*Encode current frame*/
-    VG_UNDEF(packet,max_frame_bytes);
-    VG_CHECK(input,sizeof(float)*chan*cur_frame_size);
-    nbBytes=opus_multistream_encode_float(st, input, cur_frame_size, packet, max_frame_bytes);
-    if(nbBytes<0){
-      fprintf(stderr, "Encoding failed: %s. Aborting.\n", opus_strerror(nbBytes));
-      break;
-    }
-    VG_CHECK(packet,nbBytes);
-    VG_UNDEF(input,sizeof(float)*chan*cur_frame_size);
-    nb_encoded+=cur_frame_size;
-    enc_granulepos+=cur_frame_size*48000/coding_rate;
-    total_bytes+=nbBytes;
-    size_segments=(nbBytes+255)/255;
-    peak_bytes=IMAX(nbBytes,peak_bytes);
-    min_bytes=IMIN(nbBytes,min_bytes);
-
-    if(frange!=NULL){
-      opus_uint32 rngs[256];
-      OpusEncoder *oe;
-      for(i=0;i<header.nb_streams;i++){
-        ret=opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(i,&oe));
-        ret=opus_encoder_ctl(oe,OPUS_GET_FINAL_RANGE(&rngs[i]));
-      }
-      save_range(frange,cur_frame_size*(48000/coding_rate),packet,nbBytes,
-                 rngs,header.nb_streams);
-    }
-
-    /*Flush early if adding this packet would make us end up with a
-      continued page which we wouldn't have otherwise.*/
-    while((((size_segments<=255)&&(last_segments+size_segments>255))||
-           (enc_granulepos-last_granulepos>max_ogg_delay))&&
-#ifdef OLD_LIBOGG
-           ogg_stream_flush(&os, &og)){
-#else
-           ogg_stream_flush_fill(&os, &og,255*255)){
-#endif
-      if(ogg_page_packets(&og)!=0)last_granulepos=ogg_page_granulepos(&og);
-      last_segments-=og.header[26];
-      ret=oe_write_page(&og, fout);
-      if(ret!=og.header_len+og.body_len){
-         fprintf(stderr,"Error: failed writing data to output stream\n");
-         exit(1);
-      }
-      bytes_written+=ret;
-      pages_out++;
-    }
-
-    /*The downside of early reading is if the input is an exact
-      multiple of the frame_size you'll get an extra frame that needs
-      to get cropped off. The downside of late reading is added delay.
-      If your ogg_delay is 120ms or less we'll assume you want the
-      low delay behavior.*/
-    if((!op.e_o_s)&&max_ogg_delay>5760){
-      nb_samples = inopt.read_samples(inopt.readdata,input,frame_size);
-      total_samples+=nb_samples;
-      if(nb_samples==0)op.e_o_s=1;
-    } else nb_samples=-1;
-
-    op.packet=(unsigned char *)packet;
-    op.bytes=nbBytes;
-    op.b_o_s=0;
-    op.granulepos=enc_granulepos;
-    if(op.e_o_s){
-      /*We compute the final GP as ceil(len*48k/input_rate)+preskip. When a
-        resampling decoder does the matching floor((len-preskip)*input_rate/48k)
-        conversion, the resulting output length will exactly equal the original
-        input length when 0<input_rate<=48000.*/
-      op.granulepos=((original_samples*48000+rate-1)/rate)+header.preskip;
-    }
-    op.packetno=2+id;
-    ogg_stream_packetin(&os, &op);
-    last_segments+=size_segments;
-
-    /*If the stream is over or we're sure that the delayed flush will fire,
-      go ahead and flush now to avoid adding delay.*/
-    while((op.e_o_s||(enc_granulepos+(frame_size*48000/coding_rate)-last_granulepos>max_ogg_delay)||
-           (last_segments>=255))?
-#ifdef OLD_LIBOGG
-    /*Libogg > 1.2.2 allows us to achieve lower overhead by
-      producing larger pages. For 20ms frames this is only relevant
-      above ~32kbit/sec.*/
-           ogg_stream_flush(&os, &og):
-           ogg_stream_pageout(&os, &og)){
-#else
-           ogg_stream_flush_fill(&os, &og,255*255):
-           ogg_stream_pageout_fill(&os, &og,255*255)){
-#endif
-      if(ogg_page_packets(&og)!=0)last_granulepos=ogg_page_granulepos(&og);
-      last_segments-=og.header[26];
-      ret=oe_write_page(&og, fout);
-      if(ret!=og.header_len+og.body_len){
-         fprintf(stderr,"Error: failed writing data to output stream\n");
-         exit(1);
-      }
-      bytes_written+=ret;
-      pages_out++;
-    }
-
     if(!quiet){
       stop_time = time(NULL);
       if(stop_time>last_spin){
         double estbitrate;
-        double coded_seconds=nb_encoded/(double)coding_rate;
+        double coded_seconds=data.nb_encoded/48000.;
         double wall_time=(stop_time-start_time)+1e-6;
         char sbuf[55];
         static const char spinner[]="|/-\\";
         if(!with_hard_cbr){
           double tweight=1./(1+exp(-((coded_seconds/10.)-3.)));
-          estbitrate=(total_bytes*8.0/coded_seconds)*tweight+
+          estbitrate=(data.total_bytes*8.0/coded_seconds)*tweight+
                       bitrate*(1.-tweight);
-        }else estbitrate=nbBytes*8*((double)coding_rate/frame_size);
+        }else estbitrate=data.last_length*8*(48000./frame_size);
         fprintf(stderr,"\r");
         for(i=0;i<last_spin_len;i++)fprintf(stderr," ");
-        if(inopt.total_samples_per_channel>0 && nb_encoded<inopt.total_samples_per_channel){
+        if(inopt.total_samples_per_channel>0 && data.nb_encoded<inopt.total_samples_per_channel){
           snprintf(sbuf,54,"\r[%c] %2d%% ",spinner[last_spin&3],
-          (int)floor(nb_encoded/(double)(inopt.total_samples_per_channel+inopt.skip)*100.));
+          (int)floor(data.nb_encoded/(double)(inopt.total_samples_per_channel+lookahead)*100.));
         }else{
           snprintf(sbuf,54,"\r[%c] ",spinner[last_spin&3]);
         }
@@ -1067,8 +961,10 @@
   for(i=0;i<last_spin_len;i++)fprintf(stderr," ");
   if(last_spin_len)fprintf(stderr,"\r");
 
+  ope_encoder_drain(enc);
+
   if(!quiet){
-    double coded_seconds=nb_encoded/(double)coding_rate;
+    double coded_seconds=data.nb_encoded/48000.;
     double wall_time=(stop_time-start_time)+1e-6;
     fprintf(stderr,"Encoding complete\n");
     fprintf(stderr,"-----------------------------------------------------\n");
@@ -1077,129 +973,27 @@
     fprintf(stderr,"\n       Runtime:");
     print_time(wall_time);
     fprintf(stderr,"\n                (%0.4gx realtime)\n",coded_seconds/wall_time);
-    fprintf(stderr,"         Wrote: %" I64FORMAT " bytes, %d packets, %" I64FORMAT " pages\n",bytes_written,id+1,pages_out);
+    fprintf(stderr,"         Wrote: %" I64FORMAT " bytes, %" I64FORMAT " packets, %" I64FORMAT " pages\n",data.bytes_written,data.packets_out-1,data.pages_out);
     fprintf(stderr,"       Bitrate: %0.6gkbit/s (without overhead)\n",
-            total_bytes*8.0/(coded_seconds)/1000.0);
+            data.total_bytes*8.0/(coded_seconds)/1000.0);
     fprintf(stderr," Instant rates: %0.6gkbit/s to %0.6gkbit/s\n                (%d to %d bytes per packet)\n",
-            min_bytes*8*((double)coding_rate/frame_size/1000.),
-            peak_bytes*8*((double)coding_rate/frame_size/1000.),min_bytes,peak_bytes);
-    fprintf(stderr,"      Overhead: %0.3g%% (container+metadata)\n",(bytes_written-total_bytes)/(double)bytes_written*100.);
-#ifdef OLD_LIBOGG
-    if(max_ogg_delay>(frame_size*(48000/coding_rate)*4))fprintf(stderr,"    (use libogg 1.3 or later for lower overhead)\n");
-#endif
+            data.min_bytes*8*(48000./frame_size/1000.),
+            data.peak_bytes*8*(48000./frame_size/1000.),data.min_bytes,data.peak_bytes);
+    fprintf(stderr,"      Overhead: %0.3g%% (container+metadata)\n",(data.bytes_written-data.total_bytes)/(double)data.bytes_written*100.);
     fprintf(stderr,"\n");
   }
 
-  opus_multistream_encoder_destroy(st);
-  ogg_stream_clear(&os);
-  free(packet);
+  ope_encoder_destroy(enc);
+  ope_comments_destroy(inopt.comments);
   free(input);
   if(opt_ctls)free(opt_ctls_ctlval);
 
-  if(rate!=coding_rate)clear_resample(&inopt);
-  clear_padder(&inopt);
   if(downmix)clear_downmix(&inopt);
   in_format->close_func(inopt.readdata);
   if(fin)fclose(fin);
-  if(fout)fclose(fout);
-  if(frange)fclose(frange);
+  if(data.frange)fclose(data.frange);
 #ifdef WIN_UNICODE
    free_commandline_arguments_utf8(&argc_utf8, &argv_utf8);
 #endif
   return 0;
 }
-
-/*
- Comments will be stored in the Vorbis style.
- It is described in the "Structure" section of
-    http://www.xiph.org/ogg/vorbis/doc/v-comment.html
-
- However, Opus and other non-vorbis formats omit the "framing_bit".
-
-The comment header is decoded as follows:
-  1) [vendor_length] = read an unsigned integer of 32 bits
-  2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
-  3) [user_comment_list_length] = read an unsigned integer of 32 bits
-  4) iterate [user_comment_list_length] times {
-     5) [length] = read an unsigned integer of 32 bits
-     6) this iteration's user comment = read a UTF-8 vector as [length] octets
-     }
-  7) done.
-*/
-
-#define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
-                           ((buf[base+2]<<16)&0xff0000)| \
-                           ((buf[base+1]<<8)&0xff00)| \
-                           (buf[base]&0xff))
-#define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
-                                     buf[base+2]=((val)>>16)&0xff; \
-                                     buf[base+1]=((val)>>8)&0xff; \
-                                     buf[base]=(val)&0xff; \
-                                 }while(0)
-
-static void comment_init(char **comments, int* length, const char *vendor_string)
-{
-  /*The 'vendor' field should be the actual encoding library used.*/
-  int vendor_length=strlen(vendor_string);
-  int user_comment_list_length=0;
-  int len=8+4+vendor_length+4;
-  char *p=(char*)malloc(len);
-  if(p==NULL){
-    fprintf(stderr, "malloc failed in comment_init()\n");
-    exit(1);
-  }
-  memcpy(p, "OpusTags", 8);
-  writeint(p, 8, vendor_length);
-  memcpy(p+12, vendor_string, vendor_length);
-  writeint(p, 12+vendor_length, user_comment_list_length);
-  *length=len;
-  *comments=p;
-}
-
-void comment_add(char **comments, int* length, char *tag, char *val)
-{
-  char* p=*comments;
-  int vendor_length=readint(p, 8);
-  int user_comment_list_length=readint(p, 8+4+vendor_length);
-  int tag_len=(tag?strlen(tag)+1:0);
-  int val_len=strlen(val);
-  int len=(*length)+4+tag_len+val_len;
-
-  p=(char*)realloc(p, len);
-  if(p==NULL){
-    fprintf(stderr, "realloc failed in comment_add()\n");
-    exit(1);
-  }
-
-  writeint(p, *length, tag_len+val_len);      /* length of comment */
-  if(tag){
-    memcpy(p+*length+4, tag, tag_len);        /* comment tag */
-    (p+*length+4)[tag_len-1] = '=';           /* separator */
-  }
-  memcpy(p+*length+4+tag_len, val, val_len);  /* comment */
-  writeint(p, 8+4+vendor_length, user_comment_list_length+1);
-  *comments=p;
-  *length=len;
-}
-
-static void comment_pad(char **comments, int* length, int amount)
-{
-  if(amount>0){
-    int i;
-    int newlen;
-    char* p=*comments;
-    /*Make sure there is at least amount worth of padding free, and
-       round up to the maximum that fits in the current ogg segments.*/
-    newlen=(*length+amount+255)/255*255-1;
-    p=realloc(p,newlen);
-    if(p==NULL){
-      fprintf(stderr,"realloc failed in comment_pad()\n");
-      exit(1);
-    }
-    for(i=*length;i<newlen;i++)p[i]=0;
-    *comments=p;
-    *length=newlen;
-  }
-}
-#undef readint
-#undef writeint
--- a/src/opusenc.h
+++ /dev/null
@@ -1,109 +1,0 @@
-#ifndef __OPUSENC_H
-#define __OPUSENC_H
-
-#include <opus_types.h>
-#include <ogg/ogg.h>
-
-#ifdef ENABLE_NLS
-#include <libintl.h>
-#define _(X) gettext(X)
-#else
-#define _(X) (X)
-#define textdomain(X)
-#define bindtextdomain(X, Y)
-#endif
-#ifdef gettext_noop
-#define N_(X) gettext_noop(X)
-#else
-#define N_(X) (X)
-#endif
-
-typedef long (*audio_read_func)(void *src, float *buffer, int samples);
-
-typedef struct
-{
-    audio_read_func read_samples;
-    void *readdata;
-    opus_int64 total_samples_per_channel;
-    int rawmode;
-    int channels;
-    long rate;
-    int gain;
-    int samplesize;
-    int endianness;
-    char *infilename;
-    int ignorelength;
-    int skip;
-    int extraout;
-    char *comments;
-    int comments_length;
-    int copy_comments;
-    int copy_pictures;
-} oe_enc_opt;
-
-void setup_scaler(oe_enc_opt *opt, float scale);
-void clear_scaler(oe_enc_opt *opt);
-void setup_padder(oe_enc_opt *opt, ogg_int64_t *original_samples);
-void clear_padder(oe_enc_opt *opt);
-int setup_downmix(oe_enc_opt *opt, int out_channels);
-void clear_downmix(oe_enc_opt *opt);
-void comment_add(char **comments, int* length, char *tag, char *val);
-
-typedef struct
-{
-    int (*id_func)(unsigned char *buf, int len); /* Returns true if can load file */
-    int id_data_len; /* Amount of data needed to id whether this can load the file */
-    int (*open_func)(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
-    void (*close_func)(void *);
-    char *format;
-    char *description;
-} input_format;
-
-typedef struct {
-    unsigned short format;
-    unsigned short channels;
-    unsigned int samplerate;
-    unsigned int bytespersec;
-    unsigned short align;
-    unsigned short samplesize;
-    unsigned int mask;
-} wav_fmt;
-
-typedef struct {
-    unsigned short channels;
-    short samplesize;
-    opus_int64 totalsamples;
-    opus_int64 samplesread;
-    FILE *f;
-    short bigendian;
-    short unsigned8bit;
-    int *channel_permute;
-} wavfile;
-
-typedef struct {
-    short channels;
-    unsigned int totalframes;
-    short samplesize;
-    double rate;
-    unsigned int offset;
-    unsigned int blocksize;
-} aiff_fmt;
-
-typedef wavfile aifffile; /* They're the same */
-
-input_format *open_audio_file(FILE *in, oe_enc_opt *opt);
-
-int raw_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
-int wav_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
-int aiff_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
-int wav_id(unsigned char *buf, int len);
-int aiff_id(unsigned char *buf, int len);
-void wav_close(void *);
-void raw_close(void *);
-int setup_resample(oe_enc_opt *opt, int complexity, long outfreq);
-void clear_resample(oe_enc_opt *opt);
-
-long wav_read(void *, float *buffer, int samples);
-long wav_ieee_read(void *, float *buffer, int samples);
-
-#endif /* __OPUSENC_H */
--- a/src/picture.c
+++ b/src/picture.c
@@ -225,275 +225,3 @@
     }
   }
 }
-
-#define IMAX(a,b) ((a) > (b) ? (a) : (b))
-
-/*Parse a picture SPECIFICATION as given on the command-line.
-  spec: The specification.
-  error_message: Returns an error message on error.
-  seen_file_icons: Bit flags used to track if any pictures of type 1 or type 2
-   have already been added, to ensure only one is allowed.
-  Return: A Base64-encoded string suitable for use in a METADATA_BLOCK_PICTURE
-   tag.*/
-char *parse_picture_specification(const char *spec,
-                                  const char **error_message,
-                                  int *seen_file_icons){
-  FILE          *picture_file;
-  unsigned long  picture_type;
-  unsigned long  width;
-  unsigned long  height;
-  unsigned long  depth;
-  unsigned long  colors;
-  const char    *mime_type;
-  const char    *mime_type_end;
-  const char    *description;
-  const char    *description_end;
-  const char    *filename;
-  unsigned char *buf;
-  char          *out;
-  size_t         cbuf;
-  size_t         nbuf;
-  size_t         data_offset;
-  size_t         data_length;
-  size_t         b64_length;
-  int            is_url;
-  /*If a filename has a '|' in it, there's no way we can distinguish it from a
-     full specification just from the spec string.
-    Instead, try to open the file.
-    If it exists, the user probably meant the file.*/
-  picture_type=3;
-  width=height=depth=colors=0;
-  mime_type=mime_type_end=description=description_end=filename=spec;
-  is_url=0;
-  picture_file=fopen(filename,"rb");
-  if(picture_file==NULL&&strchr(spec,'|')){
-    const char *p;
-    char       *q;
-    /*We don't have a plain file, and there is a pipe character: assume it's
-       the full form of the specification.*/
-    picture_type=strtoul(spec,&q,10);
-    if(*q!='|'||picture_type>20){
-      *error_message="invalid picture type";
-      return NULL;
-    }
-    if(picture_type>=1&&picture_type<=2&&(*seen_file_icons&picture_type)){
-      *error_message=picture_type==1?
-       "only one picture of type 1 (32x32 icon) allowed":
-       "only one picture of type 2 (icon) allowed";
-      return NULL;
-    }
-    /*An empty field implies a default of 'Cover (front)'.*/
-    if(spec==q)picture_type=3;
-    mime_type=q+1;
-    mime_type_end=mime_type+strcspn(mime_type,"|");
-    if(*mime_type_end!='|'){
-      *error_message="invalid picture specification: not enough fields";
-      return NULL;
-    }
-    /*The media type must be composed of ASCII printable characters 0x20-0x7E.*/
-    for(p=mime_type;p<mime_type_end;p++)if(*p<0x20||*p>0x7E){
-      *error_message="invalid characters in media type";
-      return NULL;
-    }
-    is_url=mime_type_end-mime_type==3
-     &&strncmp("-->",mime_type,mime_type_end-mime_type)==0;
-    description=mime_type_end+1;
-    description_end=description+strcspn(description,"|");
-    if(*description_end!='|'){
-      *error_message="invalid picture specification: not enough fields";
-      return NULL;
-    }
-    p=description_end+1;
-    if(*p!='|'){
-      width=strtoul(p,&q,10);
-      if(*q!='x'){
-        *error_message=
-         "invalid picture specification: can't parse resolution/color field";
-        return NULL;
-      }
-      p=q+1;
-      height=strtoul(p,&q,10);
-      if(*q!='x'){
-        *error_message=
-         "invalid picture specification: can't parse resolution/color field";
-        return NULL;
-      }
-      p=q+1;
-      depth=strtoul(p,&q,10);
-      if(*q=='/'){
-        p=q+1;
-        colors=strtoul(p,&q,10);
-      }
-      if(*q!='|'){
-        *error_message=
-         "invalid picture specification: can't parse resolution/color field";
-        return NULL;
-      }
-      p=q;
-    }
-    filename=p+1;
-    if(!is_url)picture_file=fopen(filename,"rb");
-  }
-  /*Buffer size: 8 static 4-byte fields plus 2 dynamic fields, plus the
-     file/URL data.
-    We reserve at least 10 bytes for the media type, in case we still need to
-     extract it from the file.*/
-  data_offset=32+(description_end-description)+IMAX(mime_type_end-mime_type,10);
-  buf=NULL;
-  if(is_url){
-    /*Easy case: just stick the URL at the end.
-      We don't do anything to verify it's a valid URL.*/
-    data_length=strlen(filename);
-    cbuf=nbuf=data_offset+data_length;
-    buf=(unsigned char *)malloc(cbuf);
-    memcpy(buf+data_offset,filename,data_length);
-  }
-  else{
-    ogg_uint32_t file_width;
-    ogg_uint32_t file_height;
-    ogg_uint32_t file_depth;
-    ogg_uint32_t file_colors;
-    int          has_palette;
-    /*Complicated case: we have a real file.
-      Read it in, attempt to parse the media type and image dimensions if
-       necessary, and validate what the user passed in.*/
-    if(picture_file==NULL){
-      *error_message="error opening picture file";
-      return NULL;
-    }
-    nbuf=data_offset;
-    /*Add a reasonable starting image file size.*/
-    cbuf=data_offset+65536;
-    for(;;){
-      unsigned char *new_buf;
-      size_t         nread;
-      new_buf=realloc(buf,cbuf);
-      if(new_buf==NULL){
-        fclose(picture_file);
-        free(buf);
-        *error_message="insufficient memory";
-        return NULL;
-      }
-      buf=new_buf;
-      nread=fread(buf+nbuf,1,cbuf-nbuf,picture_file);
-      nbuf+=nread;
-      if(nbuf<cbuf){
-        int error;
-        error=ferror(picture_file);
-        fclose(picture_file);
-        if(error){
-          free(buf);
-          *error_message="error reading picture file";
-          return NULL;
-        }
-        break;
-      }
-      if(cbuf==0xFFFFFFFF){
-        fclose(picture_file);
-        free(buf);
-        *error_message="file too large";
-        return NULL;
-      }
-      else if(cbuf>0x7FFFFFFFU)cbuf=0xFFFFFFFFU;
-      else cbuf=cbuf<<1|1;
-    }
-    data_length=nbuf-data_offset;
-    /*If there was no media type, try to extract it from the file data.*/
-    if(mime_type_end==mime_type){
-      if(is_jpeg(buf+data_offset,data_length)){
-        mime_type="image/jpeg";
-        mime_type_end=mime_type+10;
-      }
-      else if(is_png(buf+data_offset,data_length)){
-        mime_type="image/png";
-        mime_type_end=mime_type+9;
-      }
-      else if(is_gif(buf+data_offset,data_length)){
-        mime_type="image/gif";
-        mime_type_end=mime_type+9;
-      }
-      else{
-        free(buf);
-        *error_message="unable to guess media type from file, "
-         "must set it explicitly";
-        return NULL;
-      }
-    }
-    /*Try to extract the image dimensions/color information from the file.*/
-    file_width=file_height=file_depth=file_colors=0;
-    has_palette=-1;
-    if(mime_type_end-mime_type==9
-     &&oi_strncasecmp("image/png",mime_type,mime_type_end-mime_type)==0){
-      extract_png_params(buf+data_offset,data_length,
-       &file_width,&file_height,&file_depth,&file_colors,&has_palette);
-    }
-    else if(mime_type_end-mime_type==9
-     &&oi_strncasecmp("image/gif",mime_type,mime_type_end-mime_type)==0){
-      extract_gif_params(buf+data_offset,data_length,
-       &file_width,&file_height,&file_depth,&file_colors,&has_palette);
-    }
-    else if(mime_type_end-mime_type==10
-     &&oi_strncasecmp("image/jpeg",mime_type,mime_type_end-mime_type)==0){
-      extract_jpeg_params(buf+data_offset,data_length,
-       &file_width,&file_height,&file_depth,&file_colors,&has_palette);
-    }
-    if(!width)width=file_width;
-    if(!height)height=file_height;
-    if(!depth)depth=file_depth;
-    if(!colors)colors=file_colors;
-    if((file_width&&width!=file_width)
-     ||(file_height&&height!=file_height)
-     ||(file_depth&&depth!=file_depth)
-     /*We use has_palette to ensure we also reject non-0 user color counts for
-        images we've positively identified as non-paletted.*/
-     ||(has_palette>=0&&colors!=file_colors)){
-      free(buf);
-      *error_message="invalid picture specification: "
-       "resolution/color field does not match file";
-      return NULL;
-    }
-  }
-  /*These fields MUST be set correctly OR all set to zero.
-    So if any of them (except colors, for which 0 is a valid value) are still
-     zero, clear the rest to zero.*/
-  if(width==0||height==0||depth==0)width=height=depth=colors=0;
-  if(picture_type==1&&(width!=32||height!=32
-   ||mime_type_end-mime_type!=9
-   ||oi_strncasecmp("image/png",mime_type,mime_type_end-mime_type)!=0)){
-    free(buf);
-    *error_message="pictures of type 1 MUST be 32x32 PNGs";
-    return NULL;
-  }
-  /*Build the METADATA_BLOCK_PICTURE buffer.
-    We do this backwards from data_offset, because we didn't necessarily know
-     how big the media type string was before we read the data in.*/
-  data_offset-=4;
-  WRITE_U32_BE(buf+data_offset,(unsigned long)data_length);
-  data_offset-=4;
-  WRITE_U32_BE(buf+data_offset,colors);
-  data_offset-=4;
-  WRITE_U32_BE(buf+data_offset,depth);
-  data_offset-=4;
-  WRITE_U32_BE(buf+data_offset,height);
-  data_offset-=4;
-  WRITE_U32_BE(buf+data_offset,width);
-  data_offset-=description_end-description;
-  memcpy(buf+data_offset,description,description_end-description);
-  data_offset-=4;
-  WRITE_U32_BE(buf+data_offset,(unsigned long)(description_end-description));
-  data_offset-=mime_type_end-mime_type;
-  memcpy(buf+data_offset,mime_type,mime_type_end-mime_type);
-  data_offset-=4;
-  WRITE_U32_BE(buf+data_offset,(unsigned long)(mime_type_end-mime_type));
-  data_offset-=4;
-  WRITE_U32_BE(buf+data_offset,picture_type);
-  data_length=nbuf-data_offset;
-  b64_length=BASE64_LENGTH(data_length);
-  out=(char *)malloc(b64_length+1);
-  if(out!=NULL){
-    base64_encode(out,(char *)buf+data_offset,data_length);
-    if(picture_type>=1&&picture_type<=2)*seen_file_icons|=picture_type;
-  }
-  free(buf);
-  return out;
-}
--- a/src/picture.h
+++ b/src/picture.h
@@ -34,10 +34,6 @@
                          ogg_uint32_t *depth, ogg_uint32_t *colors,
                          int *has_palette);
 
-char *parse_picture_specification(const char *spec,
-                                  const char **error_message,
-                                  int *seen_file_icons);
-
 #define WRITE_U32_BE(buf, val) \
   do{ \
     (buf)[0]=(unsigned char)((val)>>24); \