shithub: opus-tools

Download patch

ref: 6dd8086d4d1bed7dcd5948651d24649809f90e1f
parent: 5be25490b689959d4c9fc62074e8be2f8eb666ab
author: Gregory Maxwell <greg@xiph.org>
date: Tue Nov 22 13:17:36 EST 2011

First cut at working multichannel support.

--- a/src/Makefile
+++ b/src/Makefile
@@ -1,5 +1,5 @@
 CC=gcc
-CFLAGS=-DHAVE_SYS_SOUNDCARD_H -O2 -g -fstack-protector-all -c -Wall -Wextra -DHAVE_GETOPT_H -DEXPORT= -DRANDOM_PREFIX=opustools -DOUTSIDE_SPEEX -DFLOATING_POINT
+CFLAGS=-DHAVE_SYS_SOUNDCARD_H -DVALGRIND -O2 -g -fstack-protector-all -c -Wall -Wextra -DHAVE_GETOPT_H -DEXPORT= -DRANDOM_PREFIX=opustools -DOUTSIDE_SPEEX -DFLOATING_POINT
 INCLUDES=-I../../opus/include
 
 all: opusenc opusdec
@@ -7,8 +7,8 @@
 .c.o:
 	$(CC) $(CFLAGS) $(INCLUDES) $< -o $@
 
-opusenc: opus_header.o wav_io.o opusenc.o resample.o
-	gcc opus_header.o wav_io.o opusenc.o resample.o -o opusenc ../../opus/.libs/libopus.a -lm -logg
+opusenc: opus_header.o opusenc.o resample.o audio-in.o
+	gcc opus_header.o audio-in.o opusenc.o resample.o -o opusenc ../../opus/.libs/libopus.a -lm -logg
 
 opusdec: opus_header.o wav_io.o wave_out.o opusdec.o resample.o
 	gcc wave_out.o opus_header.o wav_io.o opusdec.o resample.o -o opusdec ../../opus/.libs/libopus.a -lm -logg
--- /dev/null
+++ b/src/audio-in.c
@@ -1,0 +1,917 @@
+/* Copyright 2000-2002, Michael Smith <msmith@xiph.org>
+             2010, Monty <monty@xiph.org>
+   AIFF/AIFC support from OggSquish, (c) 1994-1996 Monty <xiphmont@xiph.org>
+   (From GPL code in oggenc relicensed by permission from Monty and Msmith)
+   File: audio-in.c
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <math.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
+
+#include "opusenc.h"
+#include "speex_resampler.h"
+
+#ifdef HAVE_LIBFLAC
+#include "flac.h"
+#endif
+
+/* Macros to read header data */
+#define READ_U32_LE(buf) \
+    (((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U16_LE(buf) \
+    (((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U32_BE(buf) \
+    (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))
+
+#define READ_U16_BE(buf) \
+    (((buf)[0]<<8)|((buf)[1]&0xff))
+
+/* Define the supported formats here */
+input_format formats[] = {
+    {wav_id, 12, wav_open, wav_close, "wav", N_("WAV file reader")},
+    {aiff_id, 12, aiff_open, wav_close, "aiff", N_("AIFF/AIFC file reader")},
+#ifdef HAVE_LIBFLAC
+    {flac_id,     4, flac_open, flac_close, "flac", N_("FLAC file reader")},
+    {oggflac_id, 32, flac_open, flac_close, "ogg", N_("Ogg FLAC file reader")},
+#endif
+    {NULL, 0, NULL, NULL, NULL, NULL}
+};
+
+input_format *open_audio_file(FILE *in, oe_enc_opt *opt)
+{
+    int j=0;
+    unsigned char *buf=NULL;
+    int buf_size=0, buf_filled=0;
+    int size,ret;
+
+    while(formats[j].id_func)
+    {
+        size = formats[j].id_data_len;
+        if(size >= buf_size)
+        {
+            buf = realloc(buf, size);
+            buf_size = size;
+        }
+
+        if(size > buf_filled)
+        {
+            ret = fread(buf+buf_filled, 1, buf_size-buf_filled, in);
+            buf_filled += ret;
+
+            if(buf_filled < size)
+            { /* File truncated */
+                j++;
+                continue;
+            }
+        }
+
+        if(formats[j].id_func(buf, buf_filled))
+        {
+            /* ok, we now have something that can handle the file */
+            if(formats[j].open_func(in, opt, buf, buf_filled)) {
+                free(buf);
+                return &formats[j];
+            }
+        }
+        j++;
+    }
+
+    free(buf);
+
+    return NULL;
+}
+
+static int seek_forward(FILE *in, unsigned int length)
+{
+    if(fseek(in, length, SEEK_CUR))
+    {
+        /* Failed. Do it the hard way. */
+        unsigned char buf[1024];
+        unsigned int seek_needed = length;
+        int seeked;
+        while(seek_needed > 0)
+        {
+            seeked = fread(buf, 1, seek_needed>1024?1024:seek_needed, in);
+            if(!seeked)
+                return 0; /* Couldn't read more, can't read file */
+            else
+                seek_needed -= seeked;
+        }
+    }
+    return 1;
+}
+
+static int find_wav_chunk(FILE *in, char *type, unsigned int *len)
+{
+    unsigned char buf[8];
+
+    while(1)
+    {
+        if(fread(buf,1,8,in) < 8) /* Suck down a chunk specifier */
+        {
+            fprintf(stderr, _("Warning: Unexpected EOF in reading WAV header\n"));
+            return 0; /* EOF before reaching the appropriate chunk */
+        }
+
+        if(memcmp(buf, type, 4))
+        {
+            *len = READ_U32_LE(buf+4);
+            if(!seek_forward(in, *len))
+                return 0;
+
+            buf[4] = 0;
+            fprintf(stderr, _("Skipping chunk of type \"%s\", length %d\n"), buf, *len);
+        }
+        else
+        {
+            *len = READ_U32_LE(buf+4);
+            return 1;
+        }
+    }
+}
+
+static int find_aiff_chunk(FILE *in, char *type, unsigned int *len)
+{
+    unsigned char buf[8];
+    int restarted = 0;
+
+    while(1)
+    {
+        if(fread(buf,1,8,in) <8)
+        {
+            if(!restarted) {
+                /* Handle out of order chunks by seeking back to the start
+                 * to retry */
+                restarted = 1;
+                fseek(in, 12, SEEK_SET);
+                continue;
+            }
+            fprintf(stderr, _("Warning: Unexpected EOF in AIFF chunk\n"));
+            return 0;
+        }
+
+        *len = READ_U32_BE(buf+4);
+
+        if(memcmp(buf,type,4))
+        {
+            if((*len) & 0x1)
+                (*len)++;
+
+            if(!seek_forward(in, *len))
+                return 0;
+        }
+        else
+            return 1;
+    }
+}
+
+double read_IEEE80(unsigned char *buf)
+{
+    int s=buf[0]&0xff;
+    int e=((buf[0]&0x7f)<<8)|(buf[1]&0xff);
+    double f=((unsigned long)(buf[2]&0xff)<<24)|
+        ((buf[3]&0xff)<<16)|
+        ((buf[4]&0xff)<<8) |
+         (buf[5]&0xff);
+
+    if(e==32767)
+    {
+        if(buf[2]&0x80)
+            return HUGE_VAL; /* Really NaN, but this won't happen in reality */
+        else
+        {
+            if(s)
+                return -HUGE_VAL;
+            else
+                return HUGE_VAL;
+        }
+    }
+
+    f=ldexp(f,32);
+    f+= ((buf[6]&0xff)<<24)|
+        ((buf[7]&0xff)<<16)|
+        ((buf[8]&0xff)<<8) |
+         (buf[9]&0xff);
+
+    return ldexp(f, e-16446);
+}
+
+/* AIFF/AIFC support adapted from the old OggSQUISH application */
+int aiff_id(unsigned char *buf, int len)
+{
+    if(len<12) return 0; /* Truncated file, probably */
+
+    if(memcmp(buf, "FORM", 4))
+        return 0;
+
+    if(memcmp(buf+8, "AIF",3))
+        return 0;
+
+    if(buf[11]!='C' && buf[11]!='F')
+        return 0;
+
+    return 1;
+}
+
+static int aiff_permute_matrix[6][6] =
+{
+  {0},              /* 1.0 mono   */
+  {0,1},            /* 2.0 stereo */
+  {0,2,1},          /* 3.0 channel ('wide') stereo */
+  {0,1,2,3},        /* 4.0 discrete quadraphonic (WARN) */
+  {0,2,1,3,4},      /* 5.0 surround (WARN) */
+  {0,1,2,3,4,5},    /* 5.1 surround (WARN)*/
+};
+
+int aiff_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen)
+{
+    int aifc; /* AIFC or AIFF? */
+    unsigned int len;
+    unsigned char *buffer;
+    unsigned char buf2[8];
+    aiff_fmt format;
+    aifffile *aiff = malloc(sizeof(aifffile));
+    int i;
+    (void)buflen;/*unused*/
+
+    if(buf[11]=='C')
+        aifc=1;
+    else
+        aifc=0;
+
+    if(!find_aiff_chunk(in, "COMM", &len))
+    {
+        fprintf(stderr, _("Warning: No common chunk found in AIFF file\n"));
+        return 0; /* EOF before COMM chunk */
+    }
+
+    if(len < 18)
+    {
+        fprintf(stderr, _("Warning: Truncated common chunk in AIFF header\n"));
+        return 0; /* Weird common chunk */
+    }
+
+    buffer = alloca(len);
+
+    if(fread(buffer,1,len,in) < len)
+    {
+        fprintf(stderr, _("Warning: Unexpected EOF in reading AIFF header\n"));
+        return 0;
+    }
+
+    format.channels = READ_U16_BE(buffer);
+    format.totalframes = READ_U32_BE(buffer+2);
+    format.samplesize = READ_U16_BE(buffer+6);
+    format.rate = (int)read_IEEE80(buffer+8);
+
+    aiff->bigendian = 1;
+
+    if(aifc)
+    {
+        if(len < 22)
+        {
+            fprintf(stderr, _("Warning: AIFF-C header truncated.\n"));
+            return 0;
+        }
+
+        if(!memcmp(buffer+18, "NONE", 4))
+        {
+            aiff->bigendian = 1;
+        }
+        else if(!memcmp(buffer+18, "sowt", 4))
+        {
+            aiff->bigendian = 0;
+        }
+        else
+        {
+            fprintf(stderr, _("Warning: Can't handle compressed AIFF-C (%c%c%c%c)\n"), *(buffer+18), *(buffer+19), *(buffer+20), *(buffer+21));
+            return 0; /* Compressed. Can't handle */
+        }
+    }
+
+    if(!find_aiff_chunk(in, "SSND", &len))
+    {
+        fprintf(stderr, _("Warning: No SSND chunk found in AIFF file\n"));
+        return 0; /* No SSND chunk -> no actual audio */
+    }
+
+    if(len < 8)
+    {
+        fprintf(stderr, _("Warning: Corrupted SSND chunk in AIFF header\n"));
+        return 0;
+    }
+
+    if(fread(buf2,1,8, in) < 8)
+    {
+        fprintf(stderr, _("Warning: Unexpected EOF reading AIFF header\n"));
+        return 0;
+    }
+
+    format.offset = READ_U32_BE(buf2);
+    format.blocksize = READ_U32_BE(buf2+4);
+
+    if( format.blocksize == 0 &&
+        (format.samplesize == 16 || format.samplesize == 8))
+    {
+        /* From here on, this is very similar to the wav code. Oh well. */
+
+        opt->rate = format.rate;
+        opt->channels = format.channels;
+        opt->read_samples = wav_read; /* Similar enough, so we use the same */
+        opt->total_samples_per_channel = format.totalframes;
+
+        aiff->f = in;
+        aiff->samplesread = 0;
+        aiff->channels = format.channels;
+        aiff->samplesize = format.samplesize;
+        aiff->totalsamples = format.totalframes;
+
+        if(aiff->channels>3)
+          fprintf(stderr,"WARNING: AIFF[-C] files with greater than three channels use\n"
+                  "speaker locations incompatable with Vorbis suppound definitions.\n"
+                  "Not performaing channel location mapping.\n");
+
+        opt->readdata = (void *)aiff;
+
+        aiff->channel_permute = malloc(aiff->channels * sizeof(int));
+        if (aiff->channels <= 6)
+            /* Where we know the mappings, use them. */
+            memcpy(aiff->channel_permute, aiff_permute_matrix[aiff->channels-1],
+                    sizeof(int) * aiff->channels);
+        else
+            /* Use a default 1-1 mapping */
+            for (i=0; i < aiff->channels; i++)
+                aiff->channel_permute[i] = i;
+
+        seek_forward(in, format.offset); /* Swallow some data */
+        return 1;
+    }
+    else
+    {
+        fprintf(stderr,
+                _("Warning: OggEnc does not support this type of AIFF/AIFC file\n"
+                " Must be 8 or 16 bit PCM.\n"));
+        return 0;
+    }
+}
+
+int wav_id(unsigned char *buf, int len)
+{
+    unsigned int flen;
+
+    if(len<12) return 0; /* Something screwed up */
+
+    if(memcmp(buf, "RIFF", 4))
+        return 0; /* Not wave */
+
+    flen = READ_U32_LE(buf+4); /* We don't use this */
+
+    if(memcmp(buf+8, "WAVE",4))
+        return 0; /* RIFF, but not wave */
+
+    return 1;
+}
+
+static int wav_permute_matrix[8][8] =
+{
+  {0},              /* 1.0 mono   */
+  {0,1},            /* 2.0 stereo */
+  {0,2,1},          /* 3.0 channel ('wide') stereo */
+  {0,1,2,3},        /* 4.0 discrete quadraphonic */
+  {0,2,1,3,4},      /* 5.0 surround */
+  {0,2,1,4,5,3},    /* 5.1 surround */
+  {0,2,1,5,6,4,3},  /* 6.1 surround */
+  {0,2,1,6,7,4,5,3} /* 7.1 surround (classic theater 8-track) */
+};
+
+int wav_open(FILE *in, oe_enc_opt *opt, unsigned char *oldbuf, int buflen)
+{
+    unsigned char buf[40];
+    unsigned int len;
+    int samplesize;
+    wav_fmt format;
+    wavfile *wav = malloc(sizeof(wavfile));
+    int i;
+    (void)buflen;/*unused*/
+    (void)oldbuf;/*unused*/
+
+    /* Ok. At this point, we know we have a WAV file. Now we have to detect
+     * whether we support the subtype, and we have to find the actual data
+     * We don't (for the wav reader) need to use the buffer we used to id this
+     * as a wav file (oldbuf)
+     */
+
+    if(!find_wav_chunk(in, "fmt ", &len))
+        return 0; /* EOF */
+
+    if(len < 16)
+    {
+        fprintf(stderr, _("Warning: Unrecognised format chunk in WAV header\n"));
+        return 0; /* Weird format chunk */
+    }
+
+    /* A common error is to have a format chunk that is not 16, 18 or
+     * 40 bytes in size.  This is incorrect, but not fatal, so we only
+     * warn about it instead of refusing to work with the file.
+     * Please, if you have a program that's creating format chunks of
+     * sizes other than 16 or 18 bytes in size, report a bug to the
+     * author.
+     */
+    if(len!=16 && len!=18 && len!=40)
+        fprintf(stderr,
+                _("Warning: INVALID format chunk in wav header.\n"
+                " Trying to read anyway (may not work)...\n"));
+
+    if(len>40)len=40;
+
+    if(fread(buf,1,len,in) < len)
+    {
+        fprintf(stderr, _("Warning: Unexpected EOF in reading WAV header\n"));
+        return 0;
+    }
+
+    format.format =      READ_U16_LE(buf);
+    format.channels =    READ_U16_LE(buf+2);
+    format.samplerate =  READ_U32_LE(buf+4);
+    format.bytespersec = READ_U32_LE(buf+8);
+    format.align =       READ_U16_LE(buf+12);
+    format.samplesize =  READ_U16_LE(buf+14);
+
+    if(format.format == -2) /* WAVE_FORMAT_EXTENSIBLE */
+    {
+      if(len<40)
+      {
+        fprintf(stderr,"ERROR: Extended WAV format header invalid (too small)\n");
+        return 0;
+      }
+
+      format.mask = READ_U32_LE(buf+20);
+      /* warn the user if the format mask is not a supported/expected type */
+      switch(format.mask){
+      case 1539: /* 4.0 using side surround instead of back */
+        fprintf(stderr,"WARNING: WAV file uses side surround instead of rear for quadraphonic;\n"
+                "remapping side speakers to rear in encoding.\n");
+        break;
+      case 1551: /* 5.1 using side instead of rear */
+        fprintf(stderr,"WARNING: WAV file uses side surround instead of rear for 5.1;\n"
+                "remapping side speakers to rear in encoding.\n");
+        break;
+      case 319:  /* 6.1 using rear instead of side */
+        fprintf(stderr,"WARNING: WAV file uses rear surround instead of side for 6.1;\n"
+                "remapping rear speakers to side in encoding.\n");
+        break;
+      case 255:  /* 7.1 'Widescreen' */
+        fprintf(stderr,"WARNING: WAV file is a 7.1 'Widescreen' channel mapping;\n"
+                "remapping speakers to Vorbis 7.1 format.\n");
+        break;
+      case 0:    /* default/undeclared */
+      case 1:    /* mono */
+      case 3:    /* stereo */
+      case 51:   /* quad */
+      case 55:   /* 5.0 */
+      case 63:   /* 5.1 */
+      case 1807: /* 6.1 */
+      case 1599: /* 7.1 */
+        break;
+      default:
+        fprintf(stderr,"WARNING: Unknown WAV surround channel mask: %d\n"
+                "blindly mapping speakers using default SMPTE/ITU ordering.\n",
+                format.mask);
+        break;
+      }
+      format.format = READ_U16_LE(buf+24);
+    }
+
+    if(!find_wav_chunk(in, "data", &len))
+        return 0; /* EOF */
+
+    if(format.format == 1)
+    {
+        samplesize = format.samplesize/8;
+        opt->read_samples = wav_read;
+    }
+    else if(format.format == 3)
+    {
+        samplesize = 4;
+        opt->read_samples = wav_ieee_read;
+    }
+    else
+    {
+        fprintf(stderr,
+                _("ERROR: Wav file is unsupported type (must be standard PCM\n"
+                " or type 3 floating point PCM\n"));
+        return 0;
+    }
+
+    if(format.align != format.channels * samplesize) {
+        /* This is incorrect according to the spec. Warn loudly, then ignore
+         * this value.
+         */
+        fprintf(stderr, _("Warning: WAV 'block alignment' value is incorrect, "
+                    "ignoring.\n"
+                    "The software that created this file is incorrect.\n"));
+    }
+
+    if(format.samplesize == samplesize*8 &&
+            (format.samplesize == 24 || format.samplesize == 16 ||
+             format.samplesize == 8 ||
+         (format.samplesize == 32 && format.format == 3)))
+    {
+        /* OK, good - we have the one supported format,
+           now we want to find the size of the file */
+        opt->rate = format.samplerate;
+        opt->channels = format.channels;
+
+        wav->f = in;
+        wav->samplesread = 0;
+        wav->bigendian = 0;
+        wav->channels = format.channels; /* This is in several places. The price
+                                            of trying to abstract stuff. */
+        wav->samplesize = format.samplesize;
+
+        if(len)
+        {
+            opt->total_samples_per_channel = len/(format.channels*samplesize);
+        }
+        else
+        {
+            long pos;
+            pos = ftell(in);
+            if(fseek(in, 0, SEEK_END) == -1)
+            {
+                opt->total_samples_per_channel = 0; /* Give up */
+            }
+            else
+            {
+                opt->total_samples_per_channel = (ftell(in) - pos)/
+                    (format.channels*samplesize);
+                fseek(in,pos, SEEK_SET);
+            }
+        }
+        wav->totalsamples = opt->total_samples_per_channel;
+
+        opt->readdata = (void *)wav;
+
+        wav->channel_permute = malloc(wav->channels * sizeof(int));
+        if (wav->channels <= 8)
+            /* Where we know the mappings, use them. */
+            memcpy(wav->channel_permute, wav_permute_matrix[wav->channels-1],
+                    sizeof(int) * wav->channels);
+        else
+            /* Use a default 1-1 mapping */
+            for (i=0; i < wav->channels; i++)
+                wav->channel_permute[i] = i;
+
+        return 1;
+    }
+    else
+    {
+        fprintf(stderr,
+                _("ERROR: Wav file is unsupported subformat (must be 8,16, or 24 bit PCM\n"
+                "or floating point PCM\n"));
+        return 0;
+    }
+}
+
+long wav_read(void *in, float *buffer, int samples)
+{
+    wavfile *f = (wavfile *)in;
+    int sampbyte = f->samplesize / 8;
+    signed char *buf = alloca(samples*sampbyte*f->channels);
+    long bytes_read = fread(buf, 1, samples*sampbyte*f->channels, f->f);
+    int i,j;
+    long realsamples;
+    int *ch_permute = f->channel_permute;
+
+    if(f->totalsamples && f->samplesread +
+            bytes_read/(sampbyte*f->channels) > f->totalsamples) {
+        bytes_read = sampbyte*f->channels*(f->totalsamples - f->samplesread);
+    }
+
+    realsamples = bytes_read/(sampbyte*f->channels);
+    f->samplesread += realsamples;
+
+    if(f->samplesize==8)
+    {
+        unsigned char *bufu = (unsigned char *)buf;
+        for(i = 0; i < realsamples; i++)
+        {
+            for(j=0; j < f->channels; j++)
+            {
+                buffer[i*f->channels+j]=((int)(bufu[i*f->channels + ch_permute[j]])-128)/128.0f;
+            }
+        }
+    }
+    else if(f->samplesize==16)
+    {
+        if(!f->bigendian)
+        {
+            for(i = 0; i < realsamples; i++)
+            {
+                for(j=0; j < f->channels; j++)
+                {
+                    buffer[i*f->channels+j] = ((buf[i*2*f->channels + 2*ch_permute[j] + 1]<<8) |
+                                    (buf[i*2*f->channels + 2*ch_permute[j]] & 0xff))/32768.0f;
+                }
+            }
+        }
+        else
+        {
+            for(i = 0; i < realsamples; i++)
+            {
+                for(j=0; j < f->channels; j++)
+                {
+                    buffer[i*f->channels+j]=((buf[i*2*f->channels + 2*ch_permute[j]]<<8) |
+                                  (buf[i*2*f->channels + 2*ch_permute[j] + 1] & 0xff))/32768.0f;
+                }
+            }
+        }
+    }
+    else if(f->samplesize==24)
+    {
+        if(!f->bigendian) {
+            for(i = 0; i < realsamples; i++)
+            {
+                for(j=0; j < f->channels; j++)
+                {
+                    buffer[i*f->channels+j] = ((buf[i*3*f->channels + 3*ch_permute[j] + 2] << 16) |
+                      (((unsigned char *)buf)[i*3*f->channels + 3*ch_permute[j] + 1] << 8) |
+                      (((unsigned char *)buf)[i*3*f->channels + 3*ch_permute[j]] & 0xff))
+                        / 8388608.0f;
+
+                }
+            }
+        }
+        else {
+            fprintf(stderr, _("Big endian 24 bit PCM data is not currently "
+                              "supported, aborting.\n"));
+            return 0;
+        }
+    }
+    else {
+        fprintf(stderr, _("Internal error: attempt to read unsupported "
+                          "bitdepth %d\n"), f->samplesize);
+        return 0;
+    }
+
+    return realsamples;
+}
+
+long wav_ieee_read(void *in, float *buffer, int samples)
+{
+    wavfile *f = (wavfile *)in;
+    float *buf = alloca(samples*4*f->channels); /* de-interleave buffer */
+    long bytes_read = fread(buf,1,samples*4*f->channels, f->f);
+    int i,j;
+    long realsamples;
+
+    if(f->totalsamples && f->samplesread +
+            bytes_read/(4*f->channels) > f->totalsamples)
+        bytes_read = 4*f->channels*(f->totalsamples - f->samplesread);
+    realsamples = bytes_read/(4*f->channels);
+    f->samplesread += realsamples;
+
+    for(i=0; i < realsamples; i++)
+        for(j=0; j < f->channels; j++)
+            buffer[i*f->channels+j] = buf[i*f->channels + f->channel_permute[j]];
+
+    return realsamples;
+}
+
+void wav_close(void *info)
+{
+    wavfile *f = (wavfile *)info;
+    free(f->channel_permute);
+
+    free(f);
+}
+
+int raw_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen)
+{
+    wav_fmt format; /* fake wave header ;) */
+    wavfile *wav = malloc(sizeof(wavfile));
+    int i;
+    (void)buf;/*unused*/
+    (void)buflen;/*unused*/
+
+    /* construct fake wav header ;) */
+    format.format =      2;
+    format.channels =    opt->channels;
+    format.samplerate =  opt->rate;
+    format.samplesize =  opt->samplesize;
+    format.bytespersec = opt->channels * opt->rate * opt->samplesize / 8;
+    format.align =       format.bytespersec;
+    wav->f =             in;
+    wav->samplesread =   0;
+    wav->bigendian =     opt->endianness;
+    wav->channels =      format.channels;
+    wav->samplesize =    opt->samplesize;
+    wav->totalsamples =  0;
+    wav->channel_permute = malloc(wav->channels * sizeof(int));
+    for (i=0; i < wav->channels; i++)
+      wav->channel_permute[i] = i;
+
+    opt->read_samples = wav_read;
+    opt->readdata = (void *)wav;
+    opt->total_samples_per_channel = 0; /* raw mode, don't bother */
+    return 1;
+}
+
+typedef struct {
+    audio_read_func real_reader;
+    void *real_readdata;
+    int channels;
+    float scale_factor;
+} scaler;
+
+static long read_scaler(void *data, float *buffer, int samples) {
+    scaler *d = data;
+    long in_samples = d->real_reader(d->real_readdata, buffer, samples);
+    int i;
+
+    for(i=0; i < d->channels*in_samples; i++) {
+       buffer[i] *= d->scale_factor;
+    }
+
+    return in_samples;
+}
+
+void setup_scaler(oe_enc_opt *opt, float scale) {
+    scaler *d = calloc(1, sizeof(scaler));
+
+    d->real_reader = opt->read_samples;
+    d->real_readdata = opt->readdata;
+
+    opt->read_samples = read_scaler;
+    opt->readdata = d;
+    d->channels = opt->channels;
+    d->scale_factor = scale;
+}
+
+typedef struct {
+    audio_read_func real_reader;
+    void *real_readdata;
+    int channels;
+    int *extra_samples;
+} padder;
+
+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;
+
+    if(in_samples<samples){
+      extra=samples-in_samples;
+      if(extra>*d->extra_samples)extra=*d->extra_samples;
+      *d->extra_samples-=extra;
+    }
+    for(i=0;i<extra*d->channels;i++)buffer[(in_samples*d->channels)+i]=0;
+    return in_samples+extra;
+}
+
+void setup_padder(oe_enc_opt *opt) {
+    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;
+}
+
+void clear_padder(oe_enc_opt *opt) {
+    padder *d = opt->readdata;
+
+    opt->read_samples = d->real_reader;
+    opt->readdata = d->real_readdata;
+
+    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;
+      reading=1024-*inbuf;
+      ret=rs->real_reader(rs->real_readdata, pcmbuf+*inbuf*rs->channels, reading);
+      *inbuf+=ret;
+      in_len=*inbuf;
+      out_len=samples-out_samples;
+      speex_resampler_process_interleaved_float(rs->resampler, pcmbuf, &in_len, buffer+out_samples*rs->channels, &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;
+      }
+      out_samples+=out_len;
+      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 = 2048; /* Shrug */
+    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));
+
+    opt->skip+=speex_resampler_get_output_latency(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 = (int)((float)opt->total_samples_per_channel *
+            ((float)outfreq/(float)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);
+}
--- a/src/opus_header.c
+++ b/src/opus_header.c
@@ -9,9 +9,9 @@
   - Pre-skip (16 bits)
   - Sampling rate (32 bits)
   - Gain in dB (16 bits, S7.8)
-  - Mapping (8 bits, 0=single stream (mono/stereo) 1=Vorbis mapping, 
+  - Mapping (8 bits, 0=single stream (mono/stereo) 1=Vorbis mapping,
              2..254: reserved, 255: multistream with no mapping)
-  
+
   - if (mapping != 0)
      - N = totel number of streams (8 bits)
      - M = number of paired streams (8 bits)
@@ -23,7 +23,7 @@
                else
                  - right
           - else
-             - stream = byte-2*M
+             - stream = byte-M
 */
 
 typedef struct {
@@ -109,7 +109,7 @@
    ROPacket p;
    unsigned char ch;
    opus_uint16 shortval;
-   
+
    p.data = packet;
    p.maxlen = len;
    p.pos = 0;
@@ -117,11 +117,11 @@
    read_chars(&p, (unsigned char*)str, 8);
    if (strcmp(str, "OpusHead")!=0)
       return 0;
-   
+
    if (!read_chars(&p, &ch, 1))
       return 0;
    h->version = ch;
-   
+
    if (!read_chars(&p, &ch, 1))
       return 0;
    h->channels = ch;
@@ -142,17 +142,17 @@
    if (!read_chars(&p, &ch, 1))
       return 0;
    h->channel_mapping = ch;
-   
+
    if (h->channel_mapping != 0)
    {
       if (!read_chars(&p, &ch, 1))
          return 0;
       h->nb_streams = ch;
-      
+
       if (!read_chars(&p, &ch, 1))
          return 0;
       h->nb_coupled = ch;
-      
+
       /* Multi-stream support */
       for (i=0;i<h->channels;i++)
       {
@@ -159,6 +159,11 @@
          if (!read_chars(&p, &h->stream_map[i], 1))
             return 0;
       }
+   } else {
+      h->nb_streams = 1;
+      h->nb_coupled = h->channels>1;
+      h->stream_map[0]=0;
+      h->stream_map[1]=1;
    }
    if (h->version==0 && p.pos != len)
       return 0;
@@ -170,7 +175,7 @@
    int i;
    Packet p;
    unsigned char ch;
-   
+
    p.data = packet;
    p.maxlen = len;
    p.pos = 0;
@@ -180,11 +185,11 @@
    ch = 0;
    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;
 
@@ -203,7 +208,7 @@
       ch = h->nb_streams;
       if (!write_chars(&p, &ch, 1))
          return 0;
-      
+
       ch = h->nb_coupled;
       if (!write_chars(&p, &ch, 1))
          return 0;
--- a/src/opusdec.c
+++ b/src/opusdec.c
@@ -94,7 +94,7 @@
 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
                            ((buf[base+2]<<16)&0xff0000)| \
                            ((buf[base+1]<<8)&0xff00)| \
-  	           	    (buf[base]&0xff))
+                           (buf[base]&0xff))
 
 typedef struct shapestate shapestate;
 struct shapestate {
@@ -394,7 +394,6 @@
    int err;
    OpusMSDecoder *st;
    OpusHeader header;
-   unsigned char mapping[256] = {0,1};
 
    if (opus_header_parse(op->packet, op->bytes, &header)==0)
    {
@@ -402,12 +401,6 @@
       return NULL;
    }
 
-   if (header.channels>2 || header.channels<1)
-   {
-      fprintf (stderr, "Unsupported number of channels: %d\n", header.channels);
-      return NULL;
-   }
-
    *channels = header.channels;
 
    if(!*rate)*rate=header.input_sample_rate;
@@ -419,7 +412,7 @@
    }
 
    *preskip = header.preskip;
-   st = opus_multistream_decoder_create(48000, header.channels, 1, header.channels==2 ? 1 : 0, mapping, &err);
+   st = opus_multistream_decoder_create(48000, header.channels, header.nb_streams, header.nb_coupled, header.stream_map, &err);
    if(err != OPUS_OK){
      fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(err));
      return NULL;
@@ -438,11 +431,7 @@
    {
       fprintf (stderr, "Decoding %d Hz audio", *rate);
 
-      if (*channels==1)
-         fprintf (stderr, " (mono");
-      else
-         fprintf (stderr, " (stereo");
-      fprintf(stderr, ")\n");
+      fprintf(stderr, " (%d channel%s)\n",*channels,*channels>1?"s":"");
    }
 
    return st;
@@ -452,8 +441,8 @@
 {
    int i,tmp_skip;
    unsigned out_len;
-   short out[MAX_FRAME_SIZE*2];
-   float buf[MAX_FRAME_SIZE*2];
+   short out[MAX_FRAME_SIZE*channels];
+   float buf[MAX_FRAME_SIZE*channels];
    float *output;
 
    do {
@@ -500,7 +489,7 @@
    int option_index = 0;
    char *inFile, *outFile;
    FILE *fin, *fout=NULL;
-   float output[MAX_FRAME_SIZE*2];
+   float *output;
    int frame_size=0;
    OpusMSDecoder *st=NULL;
    int packet_count=0;
@@ -540,6 +529,7 @@
    SpeexResamplerState *resampler=NULL;
    float gain=1;
 
+   output=0;
    shapemem.a_buf=0;
    shapemem.b_buf=0;
    shapemem.mute=960;
@@ -661,10 +651,10 @@
             ogg_stream_init(&os, ogg_page_serialno(&og));
             stream_init = 1;
          }
-	 if (ogg_page_serialno(&og) != os.serialno) {
-	    /* so all streams are read. */
-	    ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));
-	 }
+         if (ogg_page_serialno(&og) != os.serialno) {
+            /* so all streams are read. */
+            ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));
+         }
          /*Add page to the bitstream*/
          ogg_stream_pagein(&os, &og);
          page_granule = ogg_page_granulepos(&og);
@@ -671,11 +661,11 @@
          /*Extract all available packets*/
          while (!eos && ogg_stream_packetout(&os, &op) == 1)
          {
-	    if (op.bytes>=8 && !memcmp(op.packet, "OpusHead", 8)) {
-	       opus_serialno = os.serialno;
-	    }
-	    if (opus_serialno == -1 || os.serialno != opus_serialno)
-	       break;
+            if (op.bytes>=8 && !memcmp(op.packet, "OpusHead", 8)) {
+               opus_serialno = os.serialno;
+            }
+            if (opus_serialno == -1 || os.serialno != opus_serialno)
+               break;
             /*If first packet, process as OPUS header*/
             if (packet_count==0)
             {
@@ -687,6 +677,9 @@
                shapemem.a_buf=calloc(channels,sizeof(float)*4);
                shapemem.b_buf=calloc(channels,sizeof(float)*4);
                shapemem.fs=rate;
+               if(output)
+                 free(output);
+               output=malloc(sizeof(float)*MAX_FRAME_SIZE*channels);
                /* Converting preskip to output sampling rate */
                preskip = preskip*(rate/48000.);
                if (!st)
@@ -822,6 +815,8 @@
 
    if(shapemem.a_buf)free(shapemem.a_buf);
    if(shapemem.b_buf)free(shapemem.b_buf);
+
+   if(output)free(output);
 
    if (close_in)
       fclose(fin);
--- a/src/opusenc.c
+++ b/src/opusenc.c
@@ -39,9 +39,6 @@
 #include <sys/time.h>
 #include <math.h>
 
-#include "opus_header.h"
-#include "speex_resampler.h"
-
 #ifdef _MSC_VER
 #define snprintf _snprintf
 #endif
@@ -52,6 +49,9 @@
 #include <ogg/ogg.h>
 #include "wav_io.h"
 
+#include "opus_header.h"
+#include "opusenc.h"
+
 #if defined WIN32 || defined _WIN32
 /* We need the following two to set stdout to binary */
 #include <io.h>
@@ -58,6 +58,15 @@
 #include <fcntl.h>
 #endif
 
+#ifdef VALGRIND
+#include <valgrind/memcheck.h>
+#define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y))
+#define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y))
+#else
+#define VG_UNDEF(x,y)
+#define VG_CHECK(x,y)
+#endif
+
 void comment_init(char **comments, int* length, char *vendor_string);
 void comment_add(char **comments, int* length, char *tag, char *val);
 
@@ -70,103 +79,10 @@
    return written;
 }
 
-#define MAX_FRAME_SIZE (960*6)
 #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.   */
 
-/* Convert input audio bits, endians and channels */
-static int read_samples_pcm(FILE *fin,int frame_size, int bits, int channels,
-                            int lsb, float * input, char *buff, opus_int32 *size,
-                            int *extra_samples)
-{
-  short s[MAX_FRAME_SIZE*2];
-  unsigned char *in=(unsigned char*)s;
-  int i;
-  int nb_read;
-  int extra=0;
-
-  /*Read input audio*/
-  if(buff){
-    for(i=0;i<12;i++)in[i]=buff[i];
-    nb_read=fread(in+12, 1, bits/8*channels*frame_size-12, fin) + 12;
-    if(size)*size+=12;
-  }else{
-    int tmp=frame_size;
-    if(size&&tmp>*size)tmp=*size;
-    nb_read=fread(in, 1, bits/8*channels* tmp, fin);
-  }
-  nb_read/=bits/8*channels;
-
-  /* Make sure to return *extra_samples zeros at the end */
-  if(nb_read<frame_size){
-    extra=frame_size-nb_read;
-    if(extra > *extra_samples)
-      extra=*extra_samples;
-    *extra_samples-=extra;
-  }
-  /*fprintf(stderr, "%d\n", nb_read);*/
-  if(nb_read==0 && extra==0)return 0;
-
-  if(bits==8){
-    /* Convert 8->16 bits */
-    for(i=frame_size*channels-1;i>=0;i--){
-      s[i]=(in[i]<<8)^0x8000;
-    }
-  }else{
-    /* convert to our endian format */
-    for(i=0;i<frame_size*channels;i++){
-      if(lsb)s[i]=le_short(s[i]);
-      else s[i]=be_short(s[i]);
-    }
-  }
-
-  /* copy to float input buffer */
-  for(i=0;i<frame_size*channels;i++)input[i]=s[i]*(1/32768.f);
-  for(i=nb_read*channels;i<frame_size*channels;i++)input[i]=0;
-
-  nb_read+=extra;
-  if(size){
-    int tmp=bits/8*channels*nb_read;
-    if(tmp>*size)*size=0;
-    else *size-=tmp;
-  }
-  return nb_read;
-}
-
-static int read_samples(FILE *fin,int frame_size, int bits, int channels,
-                        int lsb, float * input, char *buff, opus_int32 *size,
-                        SpeexResamplerState *resampler, int *extra_samples)
-{
-  if(resampler){
-    /* FIXME: This is a hack, get rid of these static variables */
-    static float pcmbuf[2048];
-    static int inbuf=0;
-    int out_samples=0;
-    while(out_samples<frame_size){
-      int i;
-      int reading, ret;
-      unsigned in_len, out_len;
-      reading=1024-inbuf;
-      ret=read_samples_pcm(fin, reading, bits, channels, lsb, pcmbuf+inbuf*channels, buff, size, extra_samples);
-      inbuf+=ret;
-      in_len=inbuf;
-      out_len=frame_size-out_samples;
-      speex_resampler_process_interleaved_float(resampler, pcmbuf, &in_len, input+out_samples*channels, &out_len);
-      if(ret==0&&in_len==0){
-        for(i=out_samples*channels;i<frame_size*channels;i++)input[i]=0;
-        return out_samples;
-      }
-      out_samples+=out_len;
-      for(i=0;i<channels*(inbuf-in_len);i++)pcmbuf[i]=pcmbuf[i+channels*in_len];
-      inbuf-=in_len;
-    }
-    return out_samples;
-  }else{
-    return read_samples_pcm(fin, frame_size, bits, channels, lsb, input, buff, size, extra_samples);
-  }
-}
-
 void version(const char *version)
 {
   printf("opusenc (using %s)\n",version);
@@ -183,7 +99,7 @@
 {
   printf("Usage: opusenc [options] input_file output_file.oga\n");
   printf("\n");
-  printf("Encodes input_file using Opus. It can read the WAV or raw files.\n");
+  printf("Encodes input_file using Opus. It can read the WAV, AIFF, or raw files.\n");
   printf("\nGeneral options:\n");
   printf(" -h, --help         This help\n");
   printf(" -v, --version      Version information\n");
@@ -190,8 +106,7 @@
   printf(" --quiet            Quiet mode\n");
   printf("\n");
   printf("input_file can be:\n");
-  printf("  filename.wav      wav file\n");
-  printf("  filename.*        Raw PCM file (any extension other than .wav)\n");
+  printf("  filename.wav      file\n");
   printf("  -                 stdin\n");
   printf("\n");
   printf("output_file can be:\n");
@@ -208,7 +123,8 @@
   printf(" --framesize n      Maximum frame size in milliseconds\n");
   printf("                      (2.5, 5, 10, 20, 40, 60, default: 20)\n");
   printf(" --expect-loss      Percentage packet loss to expect (default: 0)\n");
-  printf(" --force-mono       Force mono output\n");
+  printf(" --downmix-mono     Downmix to mono\n");
+  printf(" --downmix-stereo   Downmix to to stereo (if >2 channels)\n");
   printf(" --max-ogg-delay n  Maximum container delay in milliseconds\n");
   printf("                      (0-1000, default: 1000)\n");
   printf("\nDiagnostic options:\n");
@@ -215,20 +131,18 @@
   printf(" --save-range file  Saves check values for every frame to a file\n");
   printf(" --set-ctl-int x=y  Pass the encoder control x with value y (advanced)\n");
   printf("                      This may be used multiple times\n");
+  printf(" --uncoupled        Use one mono stream per channel\n");
   printf("\nMetadata options:\n");
   printf(" --comment          Add the given string as an extra comment\n");
   printf("                      This may be used multiple times\n");
   printf(" --author           Author of this track\n");
   printf(" --title            Title for this track\n");
-  printf("\nRaw input options:\n");
-  printf(" --rate n           Sampling rate for raw input\n");
-  printf(" --mono             Consider raw input as mono\n");
-  printf(" --stereo           Consider raw input as stereo\n");
-  printf(" --le               Raw input is little-endian\n");
-  printf(" --be               Raw input is big-endian\n");
-  printf(" --8bit             Raw input is 8-bit unsigned\n");
-  printf(" --16bit            Raw input is 16-bit signed\n");
-  printf("Default raw PCM input is 48kHz, 16-bit, little-endian, stereo\n");
+  printf("\nInput options:\n");
+  printf(" --raw              Raw input\n");
+  printf(" --raw-bits n       Set bits/sample for raw input (default: 16)\n");
+  printf(" --raw-rate n       Set sampling rate for raw input (default: 48000)\n");
+  printf(" --raw-chan n       Set number of channels for raw input (default: 2)\n");
+  printf(" --raw-endianness n 1 for bigendian, 0 for little (defaults to 0)\n");
 }
 
 static inline void print_time(double seconds)
@@ -247,32 +161,10 @@
 
 int main(int argc, char **argv)
 {
-  int i, c, nb_samples;
   int option_index=0;
-  char *inFile, *outFile;
-  FILE *fin, *fout, *frange;
-  char *range_file;
-  float input[MAX_FRAME_SIZE*2];
-  opus_int32 frame_size=960;
-  opus_int64 nb_encoded, bytes_written=0, pages_out=0, total_bytes=0, total_samples=0;
-  struct timeval start_time, stop_time;
-  int last_spin_len=0;
-  time_t last_spin=0;
-  int quiet=0;
-  int nbBytes;
-  OpusMSEncoder *st;
-  unsigned char *packet;
-  int with_hard_cbr=0;
-  int with_cvbr=0;
-  opus_int32 peak_bytes=0;
-  opus_int32 min_bytes=MAX_FRAME_BYTES;
-  int expect_loss=0;
-  int force_mono=0;
-  int *opt_ctls_ctlval;
-  int opt_ctls=0;
-  int max_ogg_delay=48000;
   struct option long_options[] =
   {
+    {"quiet", no_argument, NULL, 0},
     {"bitrate", required_argument, NULL, 0},
     {"hard-cbr",no_argument,NULL, 0},
     {"vbr",no_argument,NULL, 0},
@@ -280,18 +172,18 @@
     {"comp", required_argument, NULL, 0},
     {"framesize", required_argument, NULL, 0},
     {"expect-loss", required_argument, NULL, 0},
-    {"force-mono",no_argument,NULL, 0},
+    {"downmix-mono",no_argument,NULL, 0},
+    {"downmix-stereo",no_argument,NULL, 0},
     {"max-ogg-delay", required_argument, NULL, 0},
     {"save-range", required_argument, NULL, 0},
     {"set-ctl-int", required_argument, NULL, 0},
+    {"uncoupled", no_argument, NULL, 0},
     {"help", no_argument, NULL, 0},
-    {"quiet", no_argument, NULL, 0},
-    {"le", no_argument, NULL, 0},
-    {"be", no_argument, NULL, 0},
-    {"8bit", no_argument, NULL, 0},
-    {"16bit", no_argument, NULL, 0},
-    {"mono", no_argument, NULL, 0},
-    {"stereo", no_argument, NULL, 0},
+    {"raw", no_argument, NULL, 0},
+    {"raw-bits", required_argument, NULL, 0},
+    {"raw-rate", required_argument, NULL, 0},
+    {"raw-chan", required_argument, NULL, 0},
+    {"raw-endianness", required_argument, NULL, 0},
     {"rate", required_argument, NULL, 0},
     {"music", no_argument, NULL, 0},
     {"speech", no_argument, NULL, 0},
@@ -302,40 +194,77 @@
     {"title", required_argument, NULL, 0},
     {0, 0, 0, 0}
   };
-  opus_int32 rate=48000;
-  opus_int32 coding_rate=48000;
-  opus_int32 size;
-  int chan=1;
-  int fmt=16;
-  int lsb=1;
+  int i, ret;
+  OpusMSEncoder    *st;
+  const char       *opus_version;
+  unsigned char    *packet;
+  float            *input;
+  /*I/O*/
+  oe_enc_opt       inopt;
+  input_format     *in_format;
+  char             *inFile;
+  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;
-  int ret, result;
-  int id=-1;
-  OpusHeader header;
-  char vendor_string[64];
-  char *comments;
-  int comments_length;
-  int close_in=0, close_out=0;
-  int eos=0;
-  opus_int32 bitrate=-1;
-  char first_bytes[12];
-  int wave_input=0;
-  opus_int32 lookahead=0;
-  int bytes_per_packet=-1;
-  int complexity=10;
-  const char *opus_version;
-  SpeexResamplerState *resampler=NULL;
-  int extra_samples;
-  int signal=OPUS_AUTO;
-  unsigned char mapping[256]={0, 1};
-  int err;
+  ogg_page         og;
+  ogg_packet       op;
+  ogg_int64_t      last_granulepos=0;
+  ogg_int32_t      id=-1;
+  int              eos=0;
+  OpusHeader       header;
+  int              comments_length;
+  char             vendor_string[64];
+  char             *comments;
+  /*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=MAX_FRAME_BYTES;
+  struct timeval   start_time;
+  struct timeval   stop_time;
+  time_t           last_spin=0;
+  int              last_spin_len=0;
+  /*Settings*/
+  int              quiet=0;
+  opus_int32       bitrate=-1;
+  opus_int32       rate=48000;
+  opus_int32       coding_rate=48000;
+  opus_int32       frame_size=960;
+  int              chan=2;
+  int              with_hard_cbr=0;
+  int              with_cvbr=0;
+  int              signal=OPUS_AUTO;
+  int              expect_loss=0;
+  int              complexity=10;
+  int              downmix=0;
+  int              uncoupled=0;
+  int              *opt_ctls_ctlval;
+  int              opt_ctls=0;
+  int              max_ogg_delay=48000; /*@48kHz*/
+  opus_int32       lookahead=0;
+  unsigned char    mapping[256];
+  int              force_narrow=0;
 
   opt_ctls_ctlval=NULL;
   frange=NULL;
   range_file=NULL;
+  in_format=NULL;
+  inopt.channels=chan;
+  inopt.rate=coding_rate=rate;
+  inopt.samplesize=16;
+  inopt.endianness=0;
+  inopt.rawmode=0;
+
+  for(i=0;i<256;i++)mapping[i]=i;
+
   opus_version=opus_get_version_string();
   snprintf(vendor_string, sizeof(vendor_string), "%s\n",opus_version);
   comment_init(&comments, &comments_length, vendor_string);
@@ -348,6 +277,7 @@
 
   /*Process command-line options*/
   while(1){
+    int c;
     c=getopt_long(argc, argv, "hv",
                   long_options, &option_index);
     if(c==-1)
@@ -355,7 +285,9 @@
 
     switch(c){
       case 0:
-        if(strcmp(long_options[option_index].name,"bitrate")==0){
+        if(strcmp(long_options[option_index].name,"quiet")==0){
+          quiet=1;
+        }else if(strcmp(long_options[option_index].name,"bitrate")==0){
           bitrate=atof(optarg)*1000.;
         }else if(strcmp(long_options[option_index].name,"hard-cbr")==0){
           with_hard_cbr=1;
@@ -369,8 +301,6 @@
         }else if(strcmp(long_options[option_index].name,"help")==0){
           usage();
           exit(0);
-        }else if(strcmp(long_options[option_index].name,"quiet")==0){
-          quiet=1;
         }else if(strcmp(long_options[option_index].name,"version")==0){
           version(opus_version);
           exit(0);
@@ -377,26 +307,33 @@
         }else if(strcmp(long_options[option_index].name,"version-short")==0){
           version_short(opus_version);
           exit(0);
-        }else if(strcmp(long_options[option_index].name,"le")==0){
-          lsb=1;
-        }else if(strcmp(long_options[option_index].name,"be")==0){
-          lsb=0;
-        }else if(strcmp(long_options[option_index].name,"8bit")==0){
-          fmt=8;
-        }else if(strcmp(long_options[option_index].name,"16bit")==0){
-          fmt=16;
-        }else if(strcmp(long_options[option_index].name,"stereo")==0){
-          chan=2;
-        }else if(strcmp(long_options[option_index].name,"mono")==0){
-          chan=1;
-        } else if(strcmp(long_options[option_index].name,"rate")==0){
-          rate=atoi(optarg);
+        }else if(strcmp(long_options[option_index].name,"raw")==0){
+          inopt.rawmode=1;
+        }else if(strcmp(long_options[option_index].name,"raw-bits")==0){
+          inopt.rawmode=1;
+          inopt.samplesize=atoi(optarg);
+          if(inopt.samplesize!=8&&inopt.samplesize!=16&&inopt.samplesize!=24){
+            fprintf(stderr,"Invalid bit-depth: %s\n",optarg);
+            fprintf(stderr,"--raw-bits must be one of 8,16, or 24\n");
+            exit(1);
+          }
+        }else if(strcmp(long_options[option_index].name,"raw-rate")==0){
+          inopt.rawmode=1;
+          inopt.rate=atoi(optarg);
+        }else if(strcmp(long_options[option_index].name,"raw-chan")==0){
+          inopt.rawmode=1;
+          inopt.channels=atoi(optarg);
+        }else if(strcmp(long_options[option_index].name,"raw-endianness")==0){
+          inopt.rawmode=1;
+          inopt.endianness=atoi(optarg);
         }else if(strcmp(long_options[option_index].name,"music")==0){
           signal=OPUS_SIGNAL_MUSIC;
         }else if(strcmp(long_options[option_index].name,"speech")==0){
           signal=OPUS_SIGNAL_VOICE;
-        }else if(strcmp(long_options[option_index].name,"force-mono")==0){
-          force_mono=1;
+        }else if(strcmp(long_options[option_index].name,"downmix-mono")==0){
+          downmix=1;
+        }else if(strcmp(long_options[option_index].name,"downmix-stereo")==0){
+          downmix=2;
         }else if(strcmp(long_options[option_index].name,"expect-loss")==0){
           expect_loss=atoi(optarg);
           if(expect_loss>100||expect_loss<0){
@@ -458,6 +395,8 @@
             exit(1);
           }
           range_file=optarg;
+        }else if(strcmp(long_options[option_index].name,"uncoupled")==0){
+          uncoupled=1;
         }else if(strcmp(long_options[option_index].name,"comment")==0){
           if(!strchr(optarg,'=')){
             fprintf(stderr, "Invalid comment: %s\n", optarg);
@@ -513,16 +452,26 @@
       perror(inFile);
       exit(1);
     }
-    close_in=1;
   }
 
-  err=fread(first_bytes, 1, 12, fin);
-  if(strncmp(first_bytes,"RIFF",4)==0 && strncmp(first_bytes,"RIFF",4)==0){
-    if(read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)exit(1);
-    wave_input=1;
-    lsb=1; /* CHECK: exists big-endian .wav ?? */
+  if(inopt.rawmode){
+    static input_format raw_format = {NULL, 0, raw_open, wav_close, "raw",N_("RAW file reader")};
+    in_format = &raw_format;
+    in_format->open_func(fin, &inopt, NULL, 0);
+  }else in_format=open_audio_file(fin,&inopt);
+
+  if(!in_format){
+    fprintf(stderr,"Error parsing input file: %s\n",inFile);
+    exit(1);
   }
 
+  rate=inopt.rate;
+  chan=inopt.channels;
+  inopt.skip=0;
+
+  /*In order to code the complete length we'll need to do a little padding*/
+  setup_padder(&inopt);
+
   if(rate>24000)coding_rate=48000;
   else if(rate>16000)coding_rate=24000;
   else if(rate>12000)coding_rate=12000;
@@ -530,105 +479,126 @@
 
   frame_size=frame_size/(48000/coding_rate);
 
-  bitrate=bitrate>0?bitrate:32000*(force_mono?1:chan)+32000;
-  bytes_per_packet=MAX_FRAME_BYTES;
 
-  if(bitrate>2048000||bitrate<500){
-    fprintf(stderr,"Error: Bitrate %d bits/sec is insane.\nDid you mistake bits for kilobits?\n",bitrate);
-    fprintf(stderr,"--bitrate values from 6-256 kbit/sec per channel are meaningful.\n");
-    exit(1);
-  }
-  bitrate=IMIN((force_mono?1:chan)*256000,bitrate);
+  /*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/2:5,coding_rate);
 
-  if(rate!=coding_rate){
-    /*if(!quiet)fprintf(stderr, "Resampling from %dHz to %dHz before encoding\n", rate, coding_rate);*/
-    resampler=speex_resampler_init(chan, rate, coding_rate, 5, &err);
-    if(err!=0)fprintf(stderr, "resampler error: %s\n", speex_resampler_strerror(err));
-    /* Using pre-skip to skip the zeros */
-    /*speex_resampler_skip_zeros(resampler);*/
+  /*OggOpus headers*/ /*FIXME: broke forcemono*/
+  header.channels=chan;
+  header.nb_streams=header.channels;
+  header.nb_coupled=0;
+  if(header.channels<=8&&!uncoupled){
+    static const unsigned char opusenc_streams[8][10]={
+      /*Coupled, NB_bitmap, mapping...*/
+      /*1*/ {0,   0, 0},
+      /*2*/ {1,   0, 0,1},
+      /*3*/ {1,   0, 0,2,1},
+      /*4*/ {2,   0, 0,1,2,3},
+      /*5*/ {2,   0, 0,4,1,2,3},
+      /*6*/ {2,1<<5, 0,4,1,2,3,5},
+      /*7*/ {2,1<<6, 0,4,1,2,3,5,6},
+      /*6*/ {3,1<<7, 0,6,1,2,3,4,5,7}
+    };
+    for(i=0;i<header.channels;i++)mapping[i]=opusenc_streams[header.channels-1][i+2];
+    force_narrow=opusenc_streams[header.channels-1][1];
+    header.nb_coupled=opusenc_streams[header.channels-1][0];
+    header.nb_streams=header.channels-header.nb_coupled;
   }
+  header.channel_mapping=header.channels>8?255:header.nb_streams>1;
+  if(header.channel_mapping>0)for(i=0;i<header.channels;i++)header.stream_map[i]=mapping[i];
+  /* 0 dB gain is the recommended unless you know what you're doing */
+  header.gain=0;
+  header.input_sample_rate=rate;
 
-
   /*Initialize OPUS encoder*/
-  st=opus_multistream_encoder_create(coding_rate, chan, 1, chan==2, mapping, OPUS_APPLICATION_AUDIO, &err);
-  if(err!=OPUS_OK){
-    fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(err));
+  st=opus_multistream_encoder_create(coding_rate, chan, header.nb_streams, header.nb_coupled, mapping, OPUS_APPLICATION_AUDIO, &ret);
+  if(ret!=OPUS_OK){
+    fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(ret));
     exit(1);
   }
 
-  err=opus_multistream_encoder_ctl(st, OPUS_SET_SIGNAL(signal));
-  if(err!=OPUS_OK){
-    fprintf(stderr,"OPUS_SET_SIGNAL returned: %s\n",opus_strerror(err));
+  ret=opus_multistream_encoder_ctl(st, OPUS_SET_SIGNAL(signal));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"OPUS_SET_SIGNAL returned: %s\n",opus_strerror(ret));
     exit(1);
   }
-  header.channels=force_mono?1:chan;
-  header.channel_mapping=0;
-  header.nb_streams=1;
-  header.nb_coupled=1;
-  /* 0 dB gain is the recommended unless you know what you're doing */
-  header.gain=0;
-  header.input_sample_rate=rate;
 
-  if(force_mono){
-    OpusEncoder *oe;
-    opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(0,&oe));
-    err=opus_encoder_ctl(oe, OPUS_SET_FORCE_CHANNELS(1));
-    if(err!=OPUS_OK){
-      fprintf(stderr,"OPUS_SET_FORCE_CHANNELS returned: %s\n",opus_strerror(err));
-      exit(1);
+  if(force_narrow!=0){
+    for(i=0;i<header.nb_streams;i++){
+      if(force_narrow&(1<<i)){
+        OpusEncoder *oe;
+        opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(i,&oe));
+        ret=opus_encoder_ctl(oe, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
+        if(ret!=OPUS_OK){
+          fprintf(stderr,"OPUS_SET_MAX_BANDWIDTH on stream %d returned: %s\n",i,opus_strerror(ret));
+          exit(1);
+        }
+      }
     }
   }
 
-  err=opus_multistream_encoder_ctl(st, OPUS_SET_BITRATE(bitrate));
-  if(err!=OPUS_OK){
-    fprintf(stderr,"OPUS_SET_BITRATE returned: %s\n",opus_strerror(err));
+  bitrate=bitrate>0?bitrate:64000*header.nb_streams+32000*header.nb_coupled;
+
+  if(bitrate>2048000||bitrate<500){
+    fprintf(stderr,"Error: Bitrate %d bits/sec is insane.\nDid you mistake bits for kilobits?\n",bitrate);
+    fprintf(stderr,"--bitrate values from 6-256 kbit/sec per channel are meaningful.\n");
     exit(1);
   }
+  bitrate=IMIN(chan*256000,bitrate);
 
-  err=opus_multistream_encoder_ctl(st, OPUS_SET_VBR(!with_hard_cbr));
-  if(err!=OPUS_OK){
-    fprintf(stderr,"OPUS_SET_VBR returned: %s\n",opus_strerror(err));
+  ret=opus_multistream_encoder_ctl(st, OPUS_SET_BITRATE(bitrate));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"OPUS_SET_BITRATE returned: %s\n",opus_strerror(ret));
     exit(1);
   }
 
+  ret=opus_multistream_encoder_ctl(st, OPUS_SET_VBR(!with_hard_cbr));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"OPUS_SET_VBR returned: %s\n",opus_strerror(ret));
+    exit(1);
+  }
+
   if(!with_hard_cbr){
-    err=opus_multistream_encoder_ctl(st, OPUS_SET_VBR_CONSTRAINT(with_cvbr));
-    if(err!=OPUS_OK){
-      fprintf(stderr,"OPUS_SET_VBR_CONSTRAINT returned: %s\n",opus_strerror(err));
+    ret=opus_multistream_encoder_ctl(st, OPUS_SET_VBR_CONSTRAINT(with_cvbr));
+    if(ret!=OPUS_OK){
+      fprintf(stderr,"OPUS_SET_VBR_CONSTRAINT returned: %s\n",opus_strerror(ret));
       exit(1);
     }
   }
 
-  err=opus_multistream_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity));
-  if(err!=OPUS_OK){
-    fprintf(stderr,"OPUS_SET_COMPLEXITY returned: %s\n",opus_strerror(err));
+  ret=opus_multistream_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"OPUS_SET_COMPLEXITY returned: %s\n",opus_strerror(ret));
     exit(1);
   }
 
-  err=opus_multistream_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(expect_loss));
-  if(err!=OPUS_OK){
-    fprintf(stderr,"OPUS_SET_PACKET_LOSS_PERC returned: %s\n",opus_strerror(err));
+  ret=opus_multistream_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(expect_loss));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"OPUS_SET_PACKET_LOSS_PERC returned: %s\n",opus_strerror(ret));
     exit(1);
   }
 
   for(i=0;i<opt_ctls;i++){
-    err=opus_multistream_encoder_ctl(st,opt_ctls_ctlval[i<<1],opt_ctls_ctlval[(i<<1)+1]);
-    if(err!=OPUS_OK){
-      fprintf(stderr,"opus_multistream_encoder_ctl(st,%d,%d) returned: %s\n",opt_ctls_ctlval[i<<1],opt_ctls_ctlval[(i<<1)+1],opus_strerror(err));
+    ret=opus_multistream_encoder_ctl(st,opt_ctls_ctlval[i<<1],opt_ctls_ctlval[(i<<1)+1]);
+    if(ret!=OPUS_OK){
+      fprintf(stderr,"opus_multistream_encoder_ctl(st,%d,%d) returned: %s\n",opt_ctls_ctlval[i<<1],opt_ctls_ctlval[(i<<1)+1],opus_strerror(ret));
       exit(1);
     }
   }
 
   /*We do the lookahead check late so user CTLs can change it*/
-  err=opus_multistream_encoder_ctl(st, OPUS_GET_LOOKAHEAD(&lookahead));
-  if(err!=OPUS_OK){
-    fprintf(stderr,"OPUS_GET_LOOKAHEAD returned: %s\n",opus_strerror(err));
+  ret=opus_multistream_encoder_ctl(st, OPUS_GET_LOOKAHEAD(&lookahead));
+  if(ret!=OPUS_OK){
+    fprintf(stderr,"OPUS_GET_LOOKAHEAD returned: %s\n",opus_strerror(ret));
     exit(1);
   }
-  header.preskip=lookahead*(48000/coding_rate);
-  if(resampler)header.preskip+=speex_resampler_get_output_latency(resampler)*(48000/coding_rate);
+  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 */
-  extra_samples=(int)header.preskip*(rate/48000.);
+  inopt.extraout=(int)header.preskip*(rate/48000.);
 
   if(!quiet){
     int opus_app;
@@ -641,8 +611,12 @@
     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, %0.2gms packets, %0.6gkbit/sec%s\n",
-       force_mono?1:chan,(force_mono?1: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);
+    fprintf(stderr,")\n         %0.2gms packets, %0.6gkbit/sec%s\n",
        frame_size/(coding_rate/1000.), bitrate/1000.,
        with_hard_cbr?" CBR":with_cvbr?" CVBR":" VBR");
     fprintf(stderr," Pregap: %d\n",header.preskip);
@@ -662,7 +636,6 @@
       perror(outFile);
       exit(1);
     }
-    close_out=1;
   }
 
   /*Write header*/
@@ -677,8 +650,8 @@
     op.packetno=0;
     ogg_stream_packetin(&os, &op);
 
-    while((result=ogg_stream_flush(&os, &og))){
-      if(!result)break;
+    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");
@@ -698,8 +671,8 @@
   }
 
   /* writing the rest of the opus header packets */
-  while((result=ogg_stream_flush(&os, &og))){
-    if(!result)break;
+  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");
@@ -711,9 +684,14 @@
 
   free(comments);
 
-  if(!wave_input)nb_samples=read_samples(fin,frame_size,fmt,chan,lsb,input, first_bytes, NULL, resampler, &extra_samples);
-  else nb_samples=read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size, resampler, &extra_samples);
+  input=malloc(sizeof(float)*frame_size*chan);
+  if(input==NULL){
+    fprintf(stderr,"Error: couldn't allocate sample buffer.\n");
+    exit(1);
+  }
 
+  nb_samples = inopt.read_samples(inopt.readdata,input,frame_size);
+
   if(nb_samples==0)eos=1;
   total_samples+=nb_samples;
   nb_encoded=-header.preskip;
@@ -722,11 +700,21 @@
     id++;
     /*Encode current frame*/
 
-    nbBytes=opus_multistream_encode_float(st, input, frame_size, packet, bytes_per_packet);
+    if(nb_samples<frame_size){
+    /*FIXME*/
+      //printf("X: %d %d\n",nb_samples,frame_size);
+      for(i=nb_samples*chan;i<frame_size*chan;i++)input[i]=0;
+    }
+
+    VG_UNDEF(packet,MAX_FRAME_BYTES);
+    VG_CHECK(input,sizeof(float)*chan*frame_size);
+    nbBytes=opus_multistream_encode_float(st, input, 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*frame_size);
     nb_encoded+=frame_size;
     total_bytes+=nbBytes;
     peak_bytes=IMAX(nbBytes,peak_bytes);
@@ -743,14 +731,13 @@
          bw_strings[opus_packet_get_bandwidth(packet)-OPUS_BANDWIDTH_NARROWBAND],
          packet[0]&4?'S':'M',opus_packet_get_samples_per_frame(packet,48000));
       for(i=0;i<streams;i++){
-        err=opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(i,&oe));
-        err=opus_encoder_ctl(oe,OPUS_GET_FINAL_RANGE(&rng));
+        ret=opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(i,&oe));
+        ret=opus_encoder_ctl(oe,OPUS_GET_FINAL_RANGE(&rng));
         fprintf(frange,"%llu%c",(unsigned long long)rng,i+1==streams?'\n':' ');
       }
     }
 
-    if(wave_input)nb_samples=read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size, resampler, &extra_samples);
-    else nb_samples=read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, NULL, resampler, &extra_samples);
+    nb_samples = inopt.read_samples(inopt.readdata,input,frame_size);
     if(nb_samples==0)eos=1;
     if(eos && total_samples<=nb_encoded)op.e_o_s=1;
     else op.e_o_s=0;
@@ -763,6 +750,7 @@
     /*Is this redundent?*/
     if(eos && total_samples<=nb_encoded)op.e_o_s=1;
     else op.e_o_s=0;
+    /*FIXME: this doesn't cope with the frame size changing*/
     op.granulepos=(id+1)*frame_size*(48000/coding_rate);
     if(op.granulepos>total_samples)op.granulepos=total_samples*(48000/coding_rate);
     op.packetno=2+id;
@@ -810,8 +798,15 @@
                       bitrate*(1.-tweight);
         }else estbitrate=nbBytes*8*((double)coding_rate/frame_size);
         for(i=0;i<last_spin_len;i++)fprintf(stderr," ");
-        snprintf(sbuf,54,"\r[%c] %02d:%02d:%02d.%02d %4.3gx realtime, %5.4gkbit/s\r",
-          spinner[last_spin&3],
+        if(inopt.total_samples_per_channel>0 && inopt.total_samples_per_channel<nb_encoded){
+          snprintf(sbuf,54,"\r[%c] %02d%% ",spinner[last_spin&3],
+          (int)floor(nb_encoded/(double)(inopt.total_samples_per_channel+inopt.skip)*100.));
+        }else{
+          snprintf(sbuf,54,"\r[%c] ",spinner[last_spin&3]);
+        }
+        last_spin_len=strlen(sbuf);
+        snprintf(sbuf+last_spin_len,54-last_spin_len,
+          "%02d:%02d:%02d.%02d %4.3gx realtime, %5.4gkbit/s\r",
           (int)(coded_seconds/3600),(int)(coded_seconds/60)%60,
           (int)(coded_seconds)%60,(int)(coded_seconds*100)%100,
           coded_seconds/wall_time,
@@ -849,7 +844,7 @@
     fprintf(stderr,"\n    Runtime:");
     print_time(wall_time);
     fprintf(stderr,"\n             (%0.4gx realtime)\n",coded_seconds/wall_time);
-    fprintf(stderr,"      Wrote: %lld bytes, %d packets, %lld pages\n",total_bytes,id,pages_out);
+    fprintf(stderr,"      Wrote: %lld bytes, %d packets, %lld pages\n",total_bytes,id+1,pages_out);
     fprintf(stderr,"    Bitrate: %0.6gkbit/s (without overhead)\n",
             total_bytes*8.0/(coded_seconds)/1000.0);
     fprintf(stderr," Rate range: %0.6gkbit/s to %0.6gkbit/s\n             (%d to %d bytes per packet)\n",
@@ -859,15 +854,20 @@
 #ifdef OLD_LIBOGG
     if(max_ogg_delay>(frame_size*(48000/coding_rate)*4))fprintf(stderr,"   (use libogg 1.2.2 or later for lower overhead)\n");
 #endif
+    fprintf(stderr,"\n");
   }
 
   opus_multistream_encoder_destroy(st);
   ogg_stream_clear(&os);
   free(packet);
+  free(input);
   if(opt_ctls)free(opt_ctls_ctlval);
-  if(rate!=coding_rate)speex_resampler_destroy(resampler);
-  if(close_in)fclose(fin);
-  if(close_out)fclose(fout);
+
+  if(rate!=coding_rate)clear_resample(&inopt);
+  clear_padder(&inopt);
+  in_format->close_func(inopt.readdata);
+  if(fin)fclose(fin);
+  if(fout)fclose(fout);
   if(frange)fclose(frange);
   return 0;
 }
--- /dev/null
+++ b/src/opusenc.h
@@ -1,0 +1,97 @@
+#ifndef __OPUSENC_H
+#define __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;
+    long total_samples_per_channel;
+    int rawmode;
+    int channels;
+    long rate;
+    int samplesize;
+    int endianness;
+    char *infilename;
+    int ignorelength;
+    int skip;
+    int extraout;
+} 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);
+void clear_padder(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 {
+    short format;
+    short channels;
+    int samplerate;
+    int bytespersec;
+    short align;
+    short samplesize;
+    unsigned int mask;
+} wav_fmt;
+
+typedef struct {
+    short channels;
+    short samplesize;
+    long totalsamples;
+    long samplesread;
+    FILE *f;
+    short bigendian;
+        int *channel_permute;
+} wavfile;
+
+typedef struct {
+    short channels;
+    int totalframes;
+    short samplesize;
+    int rate;
+    int offset;
+    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/wav_io.c
+++ b/src/wav_io.c
@@ -35,150 +35,6 @@
 #include "opus_types.h"
 #include "wav_io.h"
 
-
-int read_wav_header(FILE *file, int *rate, int *channels, int *format, opus_int32 *size)
-{
-   char ch[5];
-   opus_int32 itmp;
-   opus_int16 stmp;
-   opus_int32 bpersec;
-   opus_int16 balign;
-   int skip_bytes;
-   int i;
-   int ret;
-
-   ch[4]=0;
-#if 0
-   fread(ch, 1, 4, file);
-   if (strcmp(ch, "RIFF")!=0)
-   {
-      fseek(file, 0, SEEK_SET);
-      return 0;
-   }
-
-   fread(&itmp, 4, 1, file);
-   *size = le_int(itmp-36);
-
-   fread(ch, 1, 4, file);
-   if (strcmp(ch, "WAVE")!=0)
-   {
-      fprintf (stderr, "RIFF file is not a WAVE file\n");
-      return -1;
-   }
-#endif
-   ret = fread(ch, 1, 4, file);
-   while (strcmp(ch, "fmt ")!=0)
-   {
-      ret = fread(&itmp, 4, 1, file);
-      itmp = le_int(itmp);
-      /*fprintf (stderr, "skip=%d\n", itmp);*/
-      /*strange way of seeking, but it works even for pipes*/
-      for (i=0;i<itmp;i++)
-         fgetc(file);
-      /*fseek(file, itmp, SEEK_CUR);*/
-      ret = fread(ch, 1, 4, file);
-      if (feof(file))
-      {
-         fprintf (stderr, "Corrupted WAVE file: no \"fmt \"\n");
-         return -1;
-      }
-   }
-   /*if (strcmp(ch, "fmt ")!=0)
-   {
-      fprintf (stderr, "Corrupted WAVE file: no \"fmt \"\n");
-      return -1;
-      }*/
-   
-   ret = fread(&itmp, 4, 1, file);
-   itmp = le_int(itmp);
-   skip_bytes=itmp-16;
-   /*fprintf (stderr, "skip=%d\n", skip_bytes);*/
-   
-   ret = fread(&stmp, 2, 1, file);
-   stmp = le_short(stmp);
-   if (stmp!=1)
-   {
-      fprintf (stderr, "Only PCM encoding is supported\n");
-      return -1;
-   }
-
-   ret = fread(&stmp, 2, 1, file);
-   stmp = le_short(stmp);
-   *channels = stmp;
-   
-   if (stmp>2)
-   {
-      fprintf (stderr, "Only mono and (intensity) stereo supported\n");
-      return -1;
-   }
-
-   ret = fread(&itmp, 4, 1, file);
-   itmp = le_int(itmp);
-   *rate = itmp;
-
-   ret = fread(&itmp, 4, 1, file);
-   bpersec = le_int(itmp);
-
-   ret = fread(&stmp, 2, 1, file);
-   balign = le_short(stmp);
-
-   ret = fread(&stmp, 2, 1, file);
-   stmp = le_short(stmp);
-   if (stmp!=16 && stmp!=8)
-   {
-      fprintf (stderr, "Only 8/16-bit linear supported\n");
-      return -1;
-   }
-   *format=stmp;
-
-   if (bpersec!=*rate**channels*stmp/8)
-   {
-      fprintf (stderr, "Corrupted header: ByteRate mismatch\n");
-      return -1;
-   }
-
-   if (balign!=*channels*stmp/8)
-   {
-      fprintf (stderr, "Corrupted header: BlockAlign mismatch\n");
-      return -1;
-   }
-
-   
-   /*strange way of seeking, but it works even for pipes*/
-   if (skip_bytes>0)
-      for (i=0;i<skip_bytes;i++)
-         fgetc(file);
-
-   /*fseek(file, skip_bytes, SEEK_CUR);*/
-
-   ret = fread(ch, 1, 4, file);
-   while (strcmp(ch, "data")!=0)
-   {
-      ret = fread(&itmp, 4, 1, file);
-      itmp = le_int(itmp);
-      /*strange way of seeking, but it works even for pipes*/
-      for (i=0;i<itmp;i++)
-         fgetc(file);
-      /*fseek(file, itmp, SEEK_CUR);*/
-      ret = fread(ch, 1, 4, file);
-      if (feof(file))
-      {
-         fprintf (stderr, "Corrupted WAVE file: no \"data\"\n");
-         return -1;
-      }
-   }
-
-   /*Ignore this for now*/
-   ret = fread(&itmp, 4, 1, file);
-   itmp = le_int(itmp);
-
-   *size=itmp;
-
-   return 1;
-}
-
-
-
 void write_wav_header(FILE *file, int rate, int channels, int format, int size)
 {
    char ch[5];
--- a/src/wav_io.h
+++ b/src/wav_io.h
@@ -55,8 +55,6 @@
 #endif
 }
 
-int read_wav_header(FILE *file, int *rate, int *channels, int *format, opus_int32 *size);
-
 void write_wav_header(FILE *file, int rate, int channels, int format, int size);
 
 #endif