shithub: opus-tools

Download patch

ref: 5be25490b689959d4c9fc62074e8be2f8eb666ab
parent: 9cdde2e9f347629ab8c90ec38311d9318217ada6
author: Gregory Maxwell <greg@xiph.org>
date: Sat Nov 19 23:33:28 EST 2011

Incremental work rewriting opusenc.

--- a/src/Makefile
+++ b/src/Makefile
@@ -1,5 +1,5 @@
 CC=gcc
-CFLAGS=-DHAVE_SYS_SOUNDCARD_H -O2 -g -fstack-protector-all -c -Wall -DHAVE_GETOPT_H -DEXPORT= -DRANDOM_PREFIX=opustools -DOUTSIDE_SPEEX -DFLOATING_POINT
+CFLAGS=-DHAVE_SYS_SOUNDCARD_H -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
--- a/src/opusdec.c
+++ b/src/opusdec.c
@@ -410,12 +410,17 @@
 
    *channels = header.channels;
 
-   if (!*rate)
-      *rate = header.input_sample_rate;
+   if(!*rate)*rate=header.input_sample_rate;
+   /*If the rate is unspecified we decode to 48000*/
+   if(*rate==0)*rate=48000;
+   if(*rate<8000||*rate>192000){
+     fprintf(stderr,"Warning: Crazy input_rate %d, decoding to 48000 instead.\n",*rate);
+     *rate=48000;
+   }
+
    *preskip = header.preskip;
    st = opus_multistream_decoder_create(48000, header.channels, 1, header.channels==2 ? 1 : 0, mapping, &err);
-   if (err != OPUS_OK)
-   {
+   if(err != OPUS_OK){
      fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(err));
      return NULL;
    }
@@ -814,6 +819,9 @@
    if (strlen(outFile)==0)
       WIN_Audio_close ();
 #endif
+
+   if(shapemem.a_buf)free(shapemem.a_buf);
+   if(shapemem.b_buf)free(shapemem.b_buf);
 
    if (close_in)
       fclose(fin);
--- a/src/opusenc.c
+++ b/src/opusenc.c
@@ -1,19 +1,19 @@
-/* Copyright (c) 2002-2011 Jean-Marc Valin
-   Copyright (c) 2007-2011 Xiph.Org Foundation
-   Copyright (c) 2008-2010 Gregory Maxwell
+/* Copyright (C)2002-2011 Jean-Marc Valin
+   Copyright (C)2007-2012 Xiph.Org Foundation
+   Copyright (C)2008-2012 Gregory Maxwell
    File: opusenc.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
@@ -36,7 +36,8 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <time.h>
+#include <sys/time.h>
+#include <math.h>
 
 #include "opus_header.h"
 #include "speex_resampler.h"
@@ -60,14 +61,12 @@
 void comment_init(char **comments, int* length, char *vendor_string);
 void comment_add(char **comments, int* length, char *tag, char *val);
 
-
 /*Write an Ogg page to a file pointer*/
 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);
-
+   written=fwrite(page->header,1,page->header_len, fp);
+   written+=fwrite(page->body,1,page->body_len, fp);
    return written;
 }
 
@@ -81,80 +80,58 @@
                             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;
+  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;
+  /*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;
+  /* 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]);
-      }
-   }
+  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);
-   }
+  /* 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;
 
-   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;
+  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,
@@ -161,589 +138,738 @@
                         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;
+  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;
       }
-      return out_samples;
-   } else {
-      return read_samples_pcm(fin, frame_size, bits, channels, lsb, input, buff, size, extra_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 (based on %s)\n",version);
-   printf ("Copyright (C) 2008-2011 Xiph.Org Foundation (written by Jean-Marc Valin)\n");
+  printf("opusenc (using %s)\n",version);
+  printf("Copyright (C) 2008-2012 Xiph.Org Foundation\n");
 }
 
 void version_short(const char *version)
 {
-   printf ("opusenc (based on %s)\n",version);
-   printf ("Copyright (C) 2008-2011 Xiph.Org Foundation (written by Jean-Marc Valin)\n");
+  printf("opusenc (using %s)\n",version);
+  printf("Copyright (C) 2008-2012 Xiph.Org Foundation\n");
 }
 
 void usage(void)
 {
-   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 ("\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 ("  -                 stdin\n");
-   printf ("\n");
-   printf ("output_file can be:\n");
-   printf ("  filename.oga      compressed file\n");
-   printf ("  -                 stdout\n");
-   printf ("\n");
-   printf ("Options:\n");
-   printf (" --speech           Optimize for speech\n");
-   printf (" --music            Optimize for music\n");
-   printf (" --bitrate n        Encoding bit-rate in kbit/sec\n");
-   printf (" --cbr              Use constant bitrate encoding\n");
-   printf (" --comp n           Encoding complexity (0-10)\n");
-   printf (" --framesize n      Frame size (Default: 960)\n");
-   printf (" --nopf             Do not use the prefilter/postfilter\n");
-   printf (" --independent      Encode frames independently (implies nopf)\n");
-   printf (" --comment          Add the given string as an extra comment. This may be\n");
-   printf ("                     used multiple times\n");
-   printf (" --author           Author of this track\n");
-   printf (" --title            Title for this track\n");
-   printf (" -h, --help         This help\n");
-   printf (" -v, --version      Version information\n");
-   printf (" -V                 Verbose mode (show bit-rate)\n");
-   printf ("Raw 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("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("\nGeneral options:\n");
+  printf(" -h, --help         This help\n");
+  printf(" -v, --version      Version information\n");
+  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("  -                 stdin\n");
+  printf("\n");
+  printf("output_file can be:\n");
+  printf("  filename.oga      compressed file\n");
+  printf("  -                 stdout\n");
+  printf("\nEncoding options:\n");
+  printf(" --speech           Optimize for speech\n");
+  printf(" --music            Optimize for music\n");
+  printf(" --bitrate n.nnn    Encoding bit-rate in kbit/sec (6-256 per-channel)\n");
+  printf(" --vbr              Use variable bitrate encoding (default)\n");
+  printf(" --cvbr             Use constrained variable bitrate encoding\n");
+  printf(" --hard-cbr         Use hard constant bitrate encoding\n");
+  printf(" --comp n           Encoding complexity (0-10, default: 10)\n");
+  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(" --max-ogg-delay n  Maximum container delay in milliseconds\n");
+  printf("                      (0-1000, default: 1000)\n");
+  printf("\nDiagnostic options:\n");
+  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("\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");
 }
 
+static inline void print_time(double seconds)
+{
+  long long hours, minutes;
+  hours=seconds/3600;
+  seconds-=hours*3600.;
+  minutes=seconds/60;
+  seconds-=minutes*60.;
+  if(hours)fprintf(stderr," %lld hour%s%s",hours,hours>1?"s":"",
+                   minutes&&!(seconds>0)?" and":"");
+  if(minutes)fprintf(stderr,"%s%lld minute%s%s",hours?", ":" ",minutes,
+                     minutes>1?"s":"",!hours&&seconds>0?" and":seconds>0?", and":"");
+  if(seconds>0)fprintf(stderr," %0.4g second%s",seconds,seconds!=1?"s":"");
+}
 
 int main(int argc, char **argv)
 {
-   int nb_samples, total_samples=0, nb_encoded;
-   int c;
-   int option_index = 0;
-   char *inFile, *outFile;
-   FILE *fin, *fout;
-   float input[MAX_FRAME_SIZE*2];
-   opus_int32 frame_size = 960;
-   int quiet=0;
-   int nbBytes;
-   OpusMSEncoder *st;
-   unsigned char bits[MAX_FRAME_BYTES];
-   int with_cbr = 0;
-   int with_cvbr = 0;
-   int total_bytes = 0;
-   int peak_bytes = 0;
-   struct option long_options[] =
-   {
-      {"bitrate", required_argument, NULL, 0},
-      {"cbr",no_argument,NULL, 0},
-      {"cvbr",no_argument,NULL, 0},
-      {"comp", required_argument, NULL, 0},
-      {"nopf", no_argument, NULL, 0},
-      {"independent", no_argument, NULL, 0},
-      {"framesize", required_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},
-      {"rate", required_argument, NULL, 0},
-      {"music", no_argument, NULL, 0},
-      {"speech", no_argument, NULL, 0},
-      {"version", no_argument, NULL, 0},
-      {"version-short", no_argument, NULL, 0},
-      {"comment", required_argument, NULL, 0},
-      {"author", required_argument, NULL, 0},
-      {"title", required_argument, NULL, 0},
-      {0, 0, 0, 0}
-   };
-   int print_bitrate=0;
-   opus_int32 rate=48000;
-   opus_int32 size;
-   int chan=1;
-   int fmt=16;
-   int lsb=1;
-   ogg_stream_state os;
-   ogg_page 		 og;
-   ogg_packet 		 op;
-   ogg_int64_t last_granulepos = 0;
-   int bytes_written=0, 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;
-   float bitrate=-1;
-   char first_bytes[12];
-   int wave_input=0;
-   opus_int32 lookahead = 0;
-   int bytes_per_packet=-1;
-   int complexity=-127;
-   const char *opus_version;
-   SpeexResamplerState *resampler=NULL;
-   int extra_samples;
-   int signal = OPUS_AUTO;
-   unsigned char mapping[256] = {0, 1};
-   int err;
+  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[] =
+  {
+    {"bitrate", required_argument, NULL, 0},
+    {"hard-cbr",no_argument,NULL, 0},
+    {"vbr",no_argument,NULL, 0},
+    {"cvbr",no_argument,NULL, 0},
+    {"comp", required_argument, NULL, 0},
+    {"framesize", required_argument, NULL, 0},
+    {"expect-loss", required_argument, NULL, 0},
+    {"force-mono",no_argument,NULL, 0},
+    {"max-ogg-delay", required_argument, NULL, 0},
+    {"save-range", required_argument, NULL, 0},
+    {"set-ctl-int", required_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},
+    {"rate", required_argument, NULL, 0},
+    {"music", no_argument, NULL, 0},
+    {"speech", no_argument, NULL, 0},
+    {"version", no_argument, NULL, 0},
+    {"version-short", no_argument, NULL, 0},
+    {"comment", required_argument, NULL, 0},
+    {"author", required_argument, NULL, 0},
+    {"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;
+  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;
+
+  opt_ctls_ctlval=NULL;
+  frange=NULL;
+  range_file=NULL;
+  opus_version=opus_get_version_string();
+  snprintf(vendor_string, sizeof(vendor_string), "%s\n",opus_version);
+  comment_init(&comments, &comments_length, vendor_string);
 
-   opus_version = opus_get_version_string();
-   snprintf(vendor_string, sizeof(vendor_string), "%s\n",opus_version);
-   comment_init(&comments, &comments_length, vendor_string);
+  packet=malloc(sizeof(unsigned char)*MAX_FRAME_BYTES);
+  if(packet==NULL){
+    fprintf(stderr,"Error allocating packet buffer.\n");
+    exit(1);
+  }
 
-   /*Process command-line options*/
-   while(1)
-   {
-      c = getopt_long (argc, argv, "hvV",
-                       long_options, &option_index);
-      if (c==-1)
-         break;
+  /*Process command-line options*/
+  while(1){
+    c=getopt_long(argc, argv, "hv",
+                  long_options, &option_index);
+    if(c==-1)
+       break;
 
-      switch(c)
-      {
+    switch(c){
       case 0:
-         if (strcmp(long_options[option_index].name,"bitrate")==0)
-         {
-            bitrate = atof (optarg);
-         } else if (strcmp(long_options[option_index].name,"cbr")==0)
-         {
-            with_cbr=1;
-         } else if (strcmp(long_options[option_index].name,"cvbr")==0)
-         {
-            with_cvbr=1;
-         } 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);
-         } 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,"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,"comp")==0)
-         {
-            complexity=atoi (optarg);
-         } else if (strcmp(long_options[option_index].name,"framesize")==0)
-         {
-            frame_size=IMIN(atoi(optarg),MAX_FRAME_SIZE);
-         } else if (strcmp(long_options[option_index].name,"comment")==0)
-         {
-	   if (!strchr(optarg, '='))
-	   {
-	     fprintf (stderr, "Invalid comment: %s\n", optarg);
-	     fprintf (stderr, "Comments must be of the form name=value\n");
-	     exit(1);
-	   }
-           comment_add(&comments, &comments_length, NULL, optarg);
-         } else if (strcmp(long_options[option_index].name,"author")==0)
-         {
-           comment_add(&comments, &comments_length, "author=", optarg);
-         } else if (strcmp(long_options[option_index].name,"title")==0)
-         {
-           comment_add(&comments, &comments_length, "title=", optarg);
-         }
-
-         break;
+        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;
+          with_cvbr=0;
+        }else if(strcmp(long_options[option_index].name,"cvbr")==0){
+          with_cvbr=1;
+          with_hard_cbr=0;
+        }else if(strcmp(long_options[option_index].name,"vbr")==0){
+          with_cvbr=0;
+          with_hard_cbr=0;
+        }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);
+        }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,"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,"expect-loss")==0){
+          expect_loss=atoi(optarg);
+          if(expect_loss>100||expect_loss<0){
+            fprintf(stderr,"Invalid expect-loss: %s\n",optarg);
+            fprintf(stderr,"Expected loss is a percent and must be 0-100.\n");
+            exit(1);
+          }
+        }else if(strcmp(long_options[option_index].name,"comp")==0){
+          complexity=atoi(optarg);
+          if(complexity>10||complexity<0){
+            fprintf(stderr,"Invalid complexity: %s\n",optarg);
+            fprintf(stderr,"Complexity must be 0-10.\n");
+            exit(1);
+          }
+        }else if(strcmp(long_options[option_index].name,"framesize")==0){
+          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;
+          else if(strcmp(optarg,"20")==0)frame_size=960;
+          else if(strcmp(optarg,"40")==0)frame_size=1920;
+          else if(strcmp(optarg,"60")==0)frame_size=2880;
+          else{
+            fprintf(stderr,"Invalid framesize: %s\n",optarg);
+            fprintf(stderr,"Framesize must be 2.5, 5, 10, 20, 40, or 60.\n");
+            exit(1);
+          }
+        }else if(strcmp(long_options[option_index].name,"max-ogg-delay")==0){
+          max_ogg_delay=floor(atof(optarg)*48.);
+          if(max_ogg_delay<0||max_ogg_delay>48000){
+            fprintf(stderr,"Invalid max-ogg-delay: %s\n",optarg);
+            fprintf(stderr,"max-ogg-delay 0-1000 ms.\n");
+            exit(1);
+          }
+        }else if(strcmp(long_options[option_index].name,"set-ctl-int")==0){
+          int len=strlen(optarg);
+          char *spos;
+          spos=strchr(optarg,'=');
+          if(len<3||spos==NULL||(spos-optarg)<1||(spos-optarg)>=len){
+            fprintf(stderr, "Invalid set-ctl-int: %s\n", optarg);
+            fprintf(stderr, "Syntax is --set-ctl-int intX=intY\n");
+            exit(1);
+          }
+          if((atoi(optarg)&1)!=0){
+            fprintf(stderr, "Invalid set-ctl-int: %s\n", optarg);
+            fprintf(stderr, "libopus set CTL values are even.\n");
+            exit(1);
+          }
+          if(opt_ctls==0)opt_ctls_ctlval=malloc(sizeof(int)*2);
+          else opt_ctls_ctlval=realloc(opt_ctls_ctlval,sizeof(int)*(opt_ctls+1)*2);
+          opt_ctls_ctlval[opt_ctls<<1]=atoi(optarg);
+          opt_ctls_ctlval[(opt_ctls<<1)+1]=atoi(spos+1);
+          opt_ctls++;
+        }else if(strcmp(long_options[option_index].name,"save-range")==0){
+          frange=fopen(optarg,"w");
+          if(frange==NULL){
+            perror(optarg);
+            fprintf(stderr,"Could not open save-range file: %s\n",optarg);
+            fprintf(stderr,"Must provide a writable file name.\n");
+            exit(1);
+          }
+          range_file=optarg;
+        }else if(strcmp(long_options[option_index].name,"comment")==0){
+          if(!strchr(optarg,'=')){
+            fprintf(stderr, "Invalid comment: %s\n", optarg);
+            fprintf(stderr, "Comments must be of the form name=value\n");
+            exit(1);
+          }
+          comment_add(&comments, &comments_length, NULL, optarg);
+        }else if(strcmp(long_options[option_index].name,"author")==0){
+          comment_add(&comments, &comments_length, "author=", optarg);
+        } else if(strcmp(long_options[option_index].name,"title")==0){
+          comment_add(&comments, &comments_length, "title=", optarg);
+        }
+        break;
       case 'h':
-         usage();
-         exit(0);
-         break;
+        usage();
+        exit(0);
+        break;
       case 'v':
-         version(opus_version);
-         exit(0);
-         break;
-      case 'V':
-         print_bitrate=1;
-         break;
+        version(opus_version);
+        exit(0);
+        break;
       case '?':
-         usage();
-         exit(1);
-         break;
-      }
-   }
-   if (argc-optind!=2)
-   {
-      usage();
-      exit(1);
-   }
-   inFile=argv[optind];
-   outFile=argv[optind+1];
+        usage();
+        exit(1);
+        break;
+    }
+  }
+  if(argc-optind!=2){
+    usage();
+    exit(1);
+  }
+  inFile=argv[optind];
+  outFile=argv[optind+1];
 
-   /*Initialize Ogg stream struct*/
-   srand(time(NULL));
-   if (ogg_stream_init(&os, rand())==-1)
-   {
-      fprintf(stderr,"Error: stream init failed\n");
-      exit(1);
-   }
+  /*Initialize Ogg stream struct*/
+  gettimeofday(&start_time,NULL);
+  srand(start_time.tv_sec^start_time.tv_usec);
+  if(ogg_stream_init(&os, rand())==-1){
+    fprintf(stderr,"Error: stream init failed\n");
+    exit(1);
+  }
 
-   if (strcmp(inFile, "-")==0)
-   {
+  if(strcmp(inFile, "-")==0){
 #if defined WIN32 || defined _WIN32
-         _setmode(_fileno(stdin), _O_BINARY);
+    _setmode(_fileno(stdin), _O_BINARY);
 #elif defined OS2
-         _fsetmode(stdin,"b");
+    _fsetmode(stdin,"b");
 #endif
-      fin=stdin;
-   }
-   else
-   {
-      fin = fopen(inFile, "rb");
-      if (!fin)
-      {
-         perror(inFile);
-         exit(1);
-      }
-      close_in=1;
-   }
+    fin=stdin;
+  }else{
+    fin=fopen(inFile, "rb");
+    if(!fin){
+      perror(inFile);
+      exit(1);
+    }
+    close_in=1;
+  }
 
-   {
-      int ret;
-      ret = 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 ?? */
-      }
-   }
+  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 (rate != 48000)
-   {
-      fprintf(stderr, "Resampling from %d Hz to %d Hz before encoding\n", rate, 48000);
-      resampler = speex_resampler_init(chan, rate, 48000, 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);*/
-   }
-   if (bitrate<=0.005)
-   {
-     if (chan==1)
-       bitrate=64.0;
-     else
-       bitrate=128.0;
-   }
-   bytes_per_packet = MAX_FRAME_BYTES;
+  if(rate>24000)coding_rate=48000;
+  else if(rate>16000)coding_rate=24000;
+  else if(rate>12000)coding_rate=12000;
+  else coding_rate=8000;
 
-   /*Initialize OPUS encoder*/
-   st = opus_multistream_encoder_create(48000, chan, 1, chan==2, mapping, OPUS_APPLICATION_AUDIO, &err);
-   if (err != OPUS_OK)
-   {
-     fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(err));
-     return 1;
-   }
-   opus_multistream_encoder_ctl(st, OPUS_SET_SIGNAL(signal));
-   header.channels = chan;
-   opus_multistream_encoder_ctl(st, OPUS_GET_LOOKAHEAD(&lookahead));
-   header.preskip = lookahead;
-   if (resampler)
-      header.preskip += speex_resampler_get_output_latency(resampler);
-   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;
+  frame_size=frame_size/(48000/coding_rate);
 
-   /* Extra samples that need to be read to compensate for the pre-skip */
-   extra_samples = (int)header.preskip * (rate/48000.);
-   {
-      char *st_string="mono";
-      if (chan==2)
-         st_string="stereo";
-      if (!quiet)
-      {
-         if (with_cbr)
-           fprintf (stderr, "Encoding %.0f kHz %s audio in %.0fms packets at %0.3fkbit/sec (%d bytes per packet, CBR)\n",
-               header.input_sample_rate/1000., st_string, frame_size/48., bitrate, bytes_per_packet);
-         else
-           fprintf (stderr, "Encoding %.0f kHz %s audio in %.0fms packets at %0.3fkbit/sec\n",
-               header.input_sample_rate/1000., st_string, frame_size/48., bitrate);
-      }
-   }
+  bitrate=bitrate>0?bitrate:32000*(force_mono?1:chan)+32000;
+  bytes_per_packet=MAX_FRAME_BYTES;
 
-   {
-      int tmp = (bitrate*1000);
-      if (opus_multistream_encoder_ctl(st, OPUS_SET_BITRATE(tmp)) != OPUS_OK)
-      {
-         fprintf (stderr, "bitrate request failed\n");
-         return 1;
-      }
-   }
-   if (!with_cbr)
-   {
-     if (opus_multistream_encoder_ctl(st, OPUS_SET_VBR(1)) != OPUS_OK)
-     {
-        fprintf (stderr, "VBR request failed\n");
-        return 1;
-     }
-     if (!with_cvbr)
-     {
-        if (opus_multistream_encoder_ctl(st, OPUS_SET_VBR_CONSTRAINT(0)) != OPUS_OK)
-        {
-           fprintf (stderr, "VBR constraint failed\n");
-           return 1;
-        }
-     }
-   }
+  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);
 
-   if (complexity!=-127) {
-     if (opus_multistream_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity)) != OPUS_OK)
-     {
-        fprintf (stderr, "Only complexity 0 through 10 is supported\n");
-        return 1;
-     }
-   }
+  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);*/
+  }
 
-   if (strcmp(outFile,"-")==0)
-   {
+
+  /*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));
+    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));
+    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);
+    }
+  }
+
+  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));
+    exit(1);
+  }
+
+  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));
+    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));
+      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));
+    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));
+    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));
+      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));
+    exit(1);
+  }
+  header.preskip=lookahead*(48000/coding_rate);
+  if(resampler)header.preskip+=speex_resampler_get_output_latency(resampler)*(48000/coding_rate);
+  /* Extra samples that need to be read to compensate for the pre-skip */
+  extra_samples=(int)header.preskip*(rate/48000.);
+
+  if(!quiet){
+    int opus_app;
+    fprintf(stderr,"Encoding using %s",opus_version);
+    opus_multistream_encoder_ctl(st,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");
+    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, %0.2gms packets, %0.6gkbit/sec%s\n",
+       force_mono?1:chan,(force_mono?1:chan)<2?"":"s",
+       frame_size/(coding_rate/1000.), bitrate/1000.,
+       with_hard_cbr?" CBR":with_cvbr?" CVBR":" VBR");
+    fprintf(stderr," Pregap: %d\n",header.preskip);
+
+    if(frange!=NULL)fprintf(stderr,"         Writing final range file %s\n",range_file);
+    fprintf(stderr,"\n");
+  }
+
+  if(strcmp(outFile,"-")==0){
 #if defined WIN32 || defined _WIN32
-      _setmode(_fileno(stdout), _O_BINARY);
+    _setmode(_fileno(stdout), _O_BINARY);
 #endif
-      fout=stdout;
-   }
-   else
-   {
-      fout = fopen(outFile, "wb");
-      if (!fout)
-      {
-         perror(outFile);
-         exit(1);
-      }
-      close_out=1;
-   }
+    fout=stdout;
+  }else{
+    fout=fopen(outFile, "wb");
+    if(!fout){
+      perror(outFile);
+      exit(1);
+    }
+    close_out=1;
+  }
 
-   /*Write header*/
-   {
-      unsigned char header_data[100];
-      int packet_size = opus_header_to_packet(&header, header_data, 100);
-      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);
+  /*Write header*/
+  {
+    unsigned char header_data[100];
+    int packet_size=opus_header_to_packet(&header, header_data, 100);
+    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((result = ogg_stream_flush(&os, &og)))
-      {
-         if(!result) 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);
-         }
-         else
-            bytes_written += ret;
+    while((result=ogg_stream_flush(&os, &og))){
+      if(!result)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++;
+    }
 
-      op.packet = (unsigned char *)comments;
-      op.bytes = comments_length;
-      op.b_o_s = 0;
-      op.e_o_s = 0;
-      op.granulepos = 0;
-      op.packetno = 1;
-      ogg_stream_packetin(&os, &op);
-   }
+    op.packet=(unsigned char *)comments;
+    op.bytes=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((result = ogg_stream_flush(&os, &og)))
-   {
-      if(!result) 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);
-      }
-      else
-         bytes_written += ret;
-   }
+  /* writing the rest of the opus header packets */
+  while((result=ogg_stream_flush(&os, &og))){
+    if(!result)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(comments);
+  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);
-   }
-   if (nb_samples==0)
-      eos=1;
-   total_samples += nb_samples;
-   nb_encoded = -header.preskip;
-   /*Main encoding loop (one frame per iteration)*/
-   while (!eos)
-   {
-      id++;
-      /*Encode current frame*/
+  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);
 
-      nbBytes = opus_multistream_encode_float(st, input, frame_size, bits, bytes_per_packet);
-      if (nbBytes<0)
-      {
-         fprintf(stderr, "Encoding failed: %s. Aborting.\n", opus_strerror(nbBytes));
-         break;
-      }
-      nb_encoded += frame_size;
-      total_bytes += nbBytes;
-      peak_bytes=IMAX(nbBytes,peak_bytes);
+  if(nb_samples==0)eos=1;
+  total_samples+=nb_samples;
+  nb_encoded=-header.preskip;
+  /*Main encoding loop (one frame per iteration)*/
+  while(!eos){
+    id++;
+    /*Encode current frame*/
 
-      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);
+    nbBytes=opus_multistream_encode_float(st, input, frame_size, packet, bytes_per_packet);
+    if(nbBytes<0){
+      fprintf(stderr, "Encoding failed: %s. Aborting.\n", opus_strerror(nbBytes));
+      break;
+    }
+    nb_encoded+=frame_size;
+    total_bytes+=nbBytes;
+    peak_bytes=IMAX(nbBytes,peak_bytes);
+    min_bytes=IMIN(nbBytes,min_bytes);
+
+    if(frange!=NULL){
+      static const char *bw_strings[5]={"NB","MB","WB","SWB","FB"};
+      static const char *mode_strings[3]={"LP","HYB","MDCT"};
+      int streams=1; /*FIXME, mode data should be per stream too*/
+      OpusEncoder *oe;
+      opus_uint32 rng;
+      fprintf(frange,"%d %d ",frame_size*(48000/coding_rate),nbBytes);
+      fprintf(frange,"%s %s %c %d ",mode_strings[((((packet[0]>>3)+48)&92)+4)>>5],
+         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));
+        fprintf(frange,"%llu%c",(unsigned long long)rng,i+1==streams?'\n':' ');
       }
-      if (nb_samples==0)
-      {
-         eos=1;
-      }
-      if (eos && total_samples<=nb_encoded)
-         op.e_o_s = 1;
-      else
-         op.e_o_s = 0;
-      total_samples += nb_samples;
+    }
 
-      op.packet = (unsigned char *)bits;
-      op.bytes = nbBytes;
-      op.b_o_s = 0;
-      /*Is this redundent?*/
-      if (eos && total_samples<=nb_encoded)
-         op.e_o_s = 1;
-      else
-         op.e_o_s = 0;
-      op.granulepos = (id+1)*frame_size;
-      if (op.granulepos>total_samples)
-         op.granulepos = total_samples;
-      op.packetno = 2+id;
-      /*printf ("granulepos: %d %d %d\n", (int)op.granulepos, op.packetno, op.bytes);*/
-      ogg_stream_packetin(&os, &op);
+    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);
+    if(nb_samples==0)eos=1;
+    if(eos && total_samples<=nb_encoded)op.e_o_s=1;
+    else op.e_o_s=0;
 
-      /*Write all new pages (most likely 0 or 1)
-        Flush if we've buffered 1 second to avoid excessive framing delay. */
-      while (eos||(op.granulepos-last_granulepos+frame_size>48000)?
+    total_samples+=nb_samples;
+
+    op.packet=(unsigned char *)packet;
+    op.bytes=nbBytes;
+    op.b_o_s=0;
+    /*Is this redundent?*/
+    if(eos && total_samples<=nb_encoded)op.e_o_s=1;
+    else op.e_o_s=0;
+    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;
+    /*printf("granulepos: %d %d %d\n", (int)op.granulepos, op.packetno, op.bytes);*/
+    ogg_stream_packetin(&os, &op);
+
+    /*Write all new pages (most likely 0 or 1)
+      Flush if we've buffered >max_ogg_delay second to avoid excessive framing delay. */
+    while(eos||(op.granulepos-last_granulepos+(frame_size*(48000/coding_rate))>max_ogg_delay)?
 #if 0
-      /*Libogg > 1.2.2 allows us to achieve lower overhead by
-        producing larger pages. For 20ms frames this is only relevant
-        above ~32kbit/sec. We still target somewhat smaller than the
-        maximum size in order to avoid continued pages.*/
-             ogg_stream_flush_fill(&os, &og,255*255-7*1276):
-             ogg_stream_pageout_fill(&os, &og,255*255-7*1276))
+    /*Libogg > 1.2.2 allows us to achieve lower overhead by
+      producing larger pages. For 20ms frames this is only relevant
+      above ~32kbit/sec. We still target somewhat smaller than the
+      maximum size in order to avoid continued pages.*/
+           ogg_stream_flush_fill(&os, &og,255*255-7*1276):
+           ogg_stream_pageout_fill(&os, &og,255*255-7*1276))
 #else
-             ogg_stream_flush(&os, &og):
-             ogg_stream_pageout(&os, &og))
+#define OLD_LIBOGG
+           ogg_stream_flush(&os, &og):
+           ogg_stream_pageout(&os, &og))
 #endif
-      {
-         if (ogg_page_packets(&og)!=0)
-             last_granulepos = ogg_page_granulepos(&og);
-         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);
-         }
-         else
-            bytes_written += ret;
-      }
-   }
-   /*Flush all pages left to be written*/
-   while (ogg_stream_flush(&os, &og))
-   {
-      ret = oe_write_page(&og, fout);
-      if(ret != og.header_len + og.body_len)
-      {
-         fprintf (stderr,"Error: failed writing header to output stream\n");
+    {
+      if(ogg_page_packets(&og)!=0)last_granulepos=ogg_page_granulepos(&og);
+      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);
       }
-      else
-         bytes_written += ret;
-   }
+      bytes_written+=ret;
+      pages_out++;
+    }
 
-   if (!with_cbr && !quiet)
-     fprintf (stderr, "Average rate %0.3fkbit/sec, %d peak bytes per packet\n", (total_bytes*8.0/((float)nb_encoded/header.input_sample_rate))/1000.0, peak_bytes);
+    if(!quiet){
+      gettimeofday(&stop_time,NULL);
+      if(stop_time.tv_sec>last_spin){
+        double estbitrate;
+        double coded_seconds=nb_encoded/(double)coding_rate;
+        double wall_time=(stop_time.tv_sec-start_time.tv_sec)+
+          (stop_time.tv_usec-start_time.tv_usec)*1e-06;
+        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+
+                      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],
+          (int)(coded_seconds/3600),(int)(coded_seconds/60)%60,
+          (int)(coded_seconds)%60,(int)(coded_seconds*100)%100,
+          coded_seconds/wall_time,
+          estbitrate/1000.);
+        fprintf(stderr,"%s",sbuf);
+        last_spin_len=strlen(sbuf);
+        last_spin=stop_time.tv_sec;
+      }
+    }
+  }
+  gettimeofday(&stop_time,NULL);
 
-   opus_multistream_encoder_destroy(st);
-   ogg_stream_clear(&os);
+  for(i=0;i<last_spin_len;i++)fprintf(stderr," ");
+  if(last_spin_len)fprintf(stderr,"\r");
 
-   if (close_in)
-      fclose(fin);
-   if (close_out)
-      fclose(fout);
-   return 0;
+  /*Flush all pages left to be written*/
+  while(ogg_stream_flush(&os, &og)){
+    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++;
+  }
+
+  if(!quiet){
+    double coded_seconds=nb_encoded/(double)coding_rate;
+    double wall_time=(stop_time.tv_sec-start_time.tv_sec)+
+      (stop_time.tv_usec-start_time.tv_usec)*1e-06;
+    fprintf(stderr,"Encoding complete\n");
+    fprintf(stderr,"-----------------------------------------------------\n");
+    fprintf(stderr,"    Encoded:");
+    print_time(coded_seconds);
+    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,"    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",
+            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)total_bytes*100.);
+#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
+  }
+
+  opus_multistream_encoder_destroy(st);
+  ogg_stream_clear(&os);
+  free(packet);
+  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(frange)fclose(frange);
+  return 0;
 }
 
 /*
@@ -751,6 +877,8 @@
  It is describled 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
@@ -759,17 +887,13 @@
      5) [length] = read an unsigned integer of 32 bits
      6) this iteration's user comment = read a UTF-8 vector as [length] octets
      }
-  7) [framing_bit] = read a single bit as boolean
-  8) if ( [framing_bit]  unset or end of packet ) then ERROR
-  9) done.
+  7) done.
+*/
 
-  If you have troubles, please write to ymnk@jcraft.com.
- */
-
 #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))
 #define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
                                      buf[base+2]=((val)>>16)&0xff; \
                                      buf[base+1]=((val)>>8)&0xff; \
@@ -783,8 +907,8 @@
   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);
+    fprintf(stderr, "malloc failed in comment_init()\n");
+    exit(1);
   }
   memcpy(p, "OpusTags", 8);
   writeint(p, 8, vendor_length);
@@ -793,6 +917,7 @@
   *length=len;
   *comments=p;
 }
+
 void comment_add(char **comments, int* length, char *tag, char *val)
 {
   char* p=*comments;
@@ -804,8 +929,8 @@
 
   p=(char*)realloc(p, len);
   if(p==NULL){
-     fprintf (stderr, "realloc failed in comment_add()\n");
-     exit(1);
+    fprintf(stderr, "realloc failed in comment_add()\n");
+    exit(1);
   }
 
   writeint(p, *length, tag_len+val_len);      /* length of comment */
@@ -812,7 +937,6 @@
   if(tag) memcpy(p+*length+4, tag, tag_len);  /* comment */
   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;
 }