shithub: opus-tools

Download patch

ref: 22f60eeda82c051ea8844f3be02da44e860ec7eb
author: Jean-Marc Valin <jean-marc.valin@usherbrooke.ca>
date: Fri Jul 29 16:44:08 EDT 2011

Copying opusenc/opusdec from celtenc/celtdec

opusenc compiles, but that's about it

diff: cannot open b/src//null: file does not exist: 'b/src//null'
--- /dev/null
+++ b/src/opus_header.c
@@ -1,0 +1,11 @@
+#include "opus_header.h"
+
+void opus_header_parse(const unsigned char *header, OpusHeader *h)
+{
+}
+
+int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len)
+{
+   return len;
+}
+
--- /dev/null
+++ b/src/opus_header.h
@@ -1,0 +1,15 @@
+#ifndef OPUS_HEADER_H
+#define OPUS_HEADER_H
+
+typedef struct {
+   int sample_rate;
+   int multi_stream;
+   int channels;
+   int pregap;
+   unsigned char mapping[256][3];
+} OpusHeader;
+
+void opus_header_parse(const unsigned char *header, OpusHeader *h);
+int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len);
+
+#endif
--- /dev/null
+++ b/src/opusdec.c
@@ -1,0 +1,673 @@
+/* Copyright (c) 2002-2007 Jean-Marc Valin
+   Copyright (c) 2008 CSIRO
+   Copyright (c) 2007-2009 Xiph.Org Foundation
+   File: opusdec.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 <stdio.h>
+#if !defined WIN32 && !defined _WIN32
+#include <unistd.h>
+#endif
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#ifndef HAVE_GETOPT_LONG
+#include "getopt_win.h"
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <opus.h>
+#include <ogg/ogg.h>
+
+#if defined WIN32 || defined _WIN32
+#include "wave_out.h"
+/* We need the following two to set stdout to binary */
+#include <io.h>
+#include <fcntl.h>
+#endif
+#include <math.h>
+
+#ifdef __MINGW32__
+#include "wave_out.c"
+#endif
+
+#ifdef HAVE_SYS_SOUNDCARD_H
+#include <sys/soundcard.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#elif defined HAVE_SYS_AUDIOIO_H
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#ifndef AUDIO_ENCODING_SLINEAR
+#define AUDIO_ENCODING_SLINEAR AUDIO_ENCODING_LINEAR /* Solaris */
+#endif
+
+#endif
+
+#include <string.h>
+#include "wav_io.h"
+#include <opus_header.h>
+
+#define MAX_FRAME_SIZE 2048
+
+#define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
+                           ((buf[base+2]<<16)&0xff0000)| \
+                           ((buf[base+1]<<8)&0xff00)| \
+  	           	    (buf[base]&0xff))
+
+static void print_comments(char *comments, int length)
+{
+   char *c=comments;
+   int len, i, nb_fields;
+   char *end;
+   
+   if (length<8)
+   {
+      fprintf (stderr, "Invalid/corrupted comments\n");
+      return;
+   }
+   end = c+length;
+   len=readint(c, 0);
+   c+=4;
+   if (len < 0 || c+len>end)
+   {
+      fprintf (stderr, "Invalid/corrupted comments\n");
+      return;
+   }
+   fwrite(c, 1, len, stderr);
+   c+=len;
+   fprintf (stderr, "\n");
+   if (c+4>end)
+   {
+      fprintf (stderr, "Invalid/corrupted comments\n");
+      return;
+   }
+   nb_fields=readint(c, 0);
+   c+=4;
+   for (i=0;i<nb_fields;i++)
+   {
+      if (c+4>end)
+      {
+         fprintf (stderr, "Invalid/corrupted comments\n");
+         return;
+      }
+      len=readint(c, 0);
+      c+=4;
+      if (len < 0 || c+len>end)
+      {
+         fprintf (stderr, "Invalid/corrupted comments\n");
+         return;
+      }
+      fwrite(c, 1, len, stderr);
+      c+=len;
+      fprintf (stderr, "\n");
+   }
+}
+
+FILE *out_file_open(char *outFile, int rate, int *channels)
+{
+   FILE *fout=NULL;
+   /*Open output file*/
+   if (strlen(outFile)==0)
+   {
+#if defined HAVE_SYS_SOUNDCARD_H
+      int audio_fd, format, stereo;
+      audio_fd=open("/dev/dsp", O_WRONLY);
+      if (audio_fd<0)
+      {
+         perror("Cannot open /dev/dsp");
+         exit(1);         
+      }
+
+      format=AFMT_S16_NE;
+      if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1)
+      {
+         perror("SNDCTL_DSP_SETFMT");
+         close(audio_fd);
+         exit(1);
+      }
+
+      stereo=0;
+      if (*channels==2)
+         stereo=1;
+      if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1)
+      {
+         perror("SNDCTL_DSP_STEREO");
+         close(audio_fd);
+         exit(1);
+      }
+      if (stereo!=0)
+      {
+         if (*channels==1)
+            fprintf (stderr, "Cannot set mono mode, will decode in stereo\n");
+         *channels=2;
+      }
+
+      if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate)==-1)
+      {
+         perror("SNDCTL_DSP_SPEED");
+         close(audio_fd);
+         exit(1);
+      }
+      fout = fdopen(audio_fd, "w");
+#elif defined HAVE_SYS_AUDIOIO_H
+      audio_info_t info;
+      int audio_fd;
+      
+      audio_fd = open("/dev/audio", O_WRONLY);
+      if (audio_fd<0)
+      {
+         perror("Cannot open /dev/audio");
+         exit(1);
+      }
+
+      AUDIO_INITINFO(&info);
+#ifdef AUMODE_PLAY    /* NetBSD/OpenBSD */
+      info.mode = AUMODE_PLAY;
+#endif
+      info.play.encoding = AUDIO_ENCODING_SLINEAR;
+      info.play.precision = 16;
+      info.play.sample_rate = rate;
+      info.play.channels = *channels;
+      
+      if (ioctl(audio_fd, AUDIO_SETINFO, &info) < 0)
+      {
+         perror ("AUDIO_SETINFO");
+         exit(1);
+      }
+      fout = fdopen(audio_fd, "w");
+#elif defined WIN32 || defined _WIN32
+      {
+         unsigned int opus_channels = *channels;
+         if (Set_WIN_Params (INVALID_FILEDESC, rate, SAMPLE_SIZE, opus_channels))
+         {
+            fprintf (stderr, "Can't access %s\n", "WAVE OUT");
+            exit(1);
+         }
+      }
+#else
+      fprintf (stderr, "No soundcard support\n");
+      exit(1);
+#endif
+   } else {
+      if (strcmp(outFile,"-")==0)
+      {
+#if defined WIN32 || defined _WIN32
+         _setmode(_fileno(stdout), _O_BINARY);
+#endif
+         fout=stdout;
+      }
+      else 
+      {
+         fout = fopen(outFile, "wb");
+         if (!fout)
+         {
+            perror(outFile);
+            exit(1);
+         }
+         if (strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(outFile+strlen(outFile)-4,".WAV")==0)
+            write_wav_header(fout, rate, *channels, 0, 0);
+      }
+   }
+   return fout;
+}
+
+void usage(void)
+{
+   printf ("Usage: opusdec [options] input_file.oga [output_file]\n");
+   printf ("\n");
+   printf ("Decodes a OPUS file and produce a WAV file or raw file\n");
+   printf ("\n");
+   printf ("input_file can be:\n");
+   printf ("  filename.oga         regular OPUS file\n");
+   printf ("  -                    stdin\n");
+   printf ("\n");  
+   printf ("output_file can be:\n");
+   printf ("  filename.wav         Wav file\n");
+   printf ("  filename.*           Raw PCM file (any extension other that .wav)\n");
+   printf ("  -                    stdout\n");
+   printf ("  (nothing)            Will be played to soundcard\n");
+   printf ("\n");  
+   printf ("Options:\n");
+   printf (" --mono                Force decoding in mono\n");
+   printf (" --stereo              Force decoding in stereo\n");
+   printf (" --rate n              Force decoding at sampling rate n Hz\n");
+   printf (" --packet-loss n       Simulate n %% random packet loss\n");
+   printf (" -V                    Verbose mode (show bit-rate)\n"); 
+   printf (" -h, --help            This help\n");
+   printf (" -v, --version         Version information\n");
+   printf ("\n");
+}
+
+void version(void)
+{
+   printf ("opusenc (OPUS %s encoder)\n",OPUS_VERSION);
+   printf ("Copyright (C) 2008 Jean-Marc Valin\n");
+}
+
+void version_short(void)
+{
+   printf ("opusenc (OPUS %s encoder)\n",OPUS_VERSION);
+   printf ("Copyright (C) 2008 Jean-Marc Valin\n");
+}
+
+static OPUSDecoder *process_header(ogg_packet *op, opus_int32 enh_enabled, opus_int32 *frame_size, int *granule_frame_size, opus_int32 *rate, int *nframes, int forceMode, int *channels, int *overlap, int *extra_headers, int quiet, OPUSMode **mode)
+{
+   OPUSDecoder *st;
+   OPUSHeader header;
+   int bitstream;
+      
+   opus_header_from_packet(op->packet, op->bytes, &header);
+
+   if (header.nb_channels>2 || header.nb_channels<1)
+   {
+      fprintf (stderr, "Unsupported number of channels: %d\n", header.nb_channels);
+      return NULL;
+   }
+   *mode = opus_mode_create(header.sample_rate, header.frame_size, NULL);
+   if (*mode == NULL)
+   {
+      fprintf (stderr, "Mode initialization failed.\n");
+      return NULL;
+   }
+
+   /* FIXME: Set that to zero when we freeze */
+   bitstream = 0x80001000;
+   if (bitstream!=header.version_id)
+     fprintf(stderr, "WARNING: Input was encoded with a OPUS bitstream version %d. This decoder uses %d. Output will probably be corrupted.\n",header.version_id,bitstream);
+   
+   *channels = header.nb_channels;
+   *overlap=header.overlap;
+   st = opus_decoder_create_custom(*mode, header.nb_channels, NULL);
+   if (!st)
+   {
+      fprintf (stderr, "Decoder initialization failed.\n");
+      return NULL;
+   }
+   
+   /*opus_mode_info(*mode, OPUS_GET_FRAME_SIZE, frame_size);*/
+   *frame_size = header.frame_size;
+   *granule_frame_size = *frame_size;
+
+   if (!*rate)
+      *rate = header.sample_rate;
+
+   *nframes = 1;
+
+   if (!quiet)
+   {
+      fprintf (stderr, "Decoding %d Hz audio in", *rate);
+
+      if (*channels==1)
+         fprintf (stderr, " (mono");
+      else
+         fprintf (stderr, " (stereo");
+      fprintf(stderr, ")\n");
+   }
+
+   *extra_headers = header.extra_headers;
+
+   return st;
+}
+
+int main(int argc, char **argv)
+{
+   int c;
+   int option_index = 0;
+   char *inFile, *outFile;
+   FILE *fin, *fout=NULL;
+   short out[MAX_FRAME_SIZE];
+   short output[MAX_FRAME_SIZE];
+   int frame_size=0, granule_frame_size=0;
+   void *st=NULL;
+   OPUSMode *mode=NULL;
+   int packet_count=0;
+   int stream_init = 0;
+   int quiet = 0;
+   ogg_int64_t page_granule=0, last_granule=0;
+   int skip_samples=0, page_nb_packets;
+   struct option long_options[] =
+   {
+      {"help", no_argument, NULL, 0},
+      {"quiet", no_argument, NULL, 0},
+      {"version", no_argument, NULL, 0},
+      {"version-short", no_argument, NULL, 0},
+      {"rate", required_argument, NULL, 0},
+      {"mono", no_argument, NULL, 0},
+      {"stereo", no_argument, NULL, 0},
+      {"packet-loss", required_argument, NULL, 0},
+      {0, 0, 0, 0}
+   };
+   ogg_sync_state oy;
+   ogg_page       og;
+   ogg_packet     op;
+   ogg_stream_state os;
+   int enh_enabled;
+   int nframes=2;
+   int print_bitrate=0;
+   int close_in=0;
+   int eos=0;
+   int forceMode=-1;
+   int audio_size=0;
+   float loss_percent=-1;
+   int channels=-1;
+   int rate=0;
+   int extra_headers=0;
+   int wav_format=0;
+   int lookahead=0;
+   int opus_serialno = -1;
+   int firstpacket = 1;
+
+   enh_enabled = 1;
+
+   /*Process options*/
+   while(1)
+   {
+      c = getopt_long (argc, argv, "hvV",
+                       long_options, &option_index);
+      if (c==-1)
+         break;
+      
+      switch(c)
+      {
+      case 0:
+         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();
+            exit(0);
+         } else if (strcmp(long_options[option_index].name,"version-short")==0)
+         {
+            version_short();
+            exit(0);
+         } else if (strcmp(long_options[option_index].name,"mono")==0)
+         {
+            channels=1;
+         } else if (strcmp(long_options[option_index].name,"stereo")==0)
+         {
+            channels=2;
+         } else if (strcmp(long_options[option_index].name,"rate")==0)
+         {
+            rate=atoi (optarg);
+         } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
+         {
+            loss_percent = atof(optarg);
+         }
+         break;
+      case 'h':
+         usage();
+         exit(0);
+         break;
+      case 'v':
+         version();
+         exit(0);
+         break;
+      case 'V':
+         print_bitrate=1;
+         break;
+      case '?':
+         usage();
+         exit(1);
+         break;
+      }
+   }
+   if (argc-optind!=2 && argc-optind!=1)
+   {
+      usage();
+      exit(1);
+   }
+   inFile=argv[optind];
+
+   if (argc-optind==2)
+      outFile=argv[optind+1];
+   else
+      outFile = "";
+   wav_format = strlen(outFile)>=4 && (
+                                       strcmp(outFile+strlen(outFile)-4,".wav")==0
+                                       || strcmp(outFile+strlen(outFile)-4,".WAV")==0);
+   /*Open input file*/
+   if (strcmp(inFile, "-")==0)
+   {
+#if defined WIN32 || defined _WIN32
+      _setmode(_fileno(stdin), _O_BINARY);
+#endif
+      fin=stdin;
+   }
+   else 
+   {
+      fin = fopen(inFile, "rb");
+      if (!fin)
+      {
+         perror(inFile);
+         exit(1);
+      }
+      close_in=1;
+   }
+
+
+   /*Init Ogg data struct*/
+   ogg_sync_init(&oy);
+   
+   /*Main decoding loop*/
+   
+   while (1)
+   {
+      char *data;
+      int i, nb_read;
+      /*Get the ogg buffer for writing*/
+      data = ogg_sync_buffer(&oy, 200);
+      /*Read bitstream from input file*/
+      nb_read = fread(data, sizeof(char), 200, fin);      
+      ogg_sync_wrote(&oy, nb_read);
+
+      /*Loop for all complete pages we got (most likely only one)*/
+      while (ogg_sync_pageout(&oy, &og)==1)
+      {
+         if (stream_init == 0) {
+            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));
+	 }
+         /*Add page to the bitstream*/
+         ogg_stream_pagein(&os, &og);
+         page_granule = ogg_page_granulepos(&og);
+         page_nb_packets = ogg_page_packets(&og);
+         if (page_granule>0 && frame_size)
+         {
+            /* FIXME: shift the granule values if --force-* is specified */
+            skip_samples = frame_size*(page_nb_packets*granule_frame_size*nframes - (page_granule-last_granule))/granule_frame_size;
+            if (ogg_page_eos(&og))
+               skip_samples = -skip_samples;
+            /*else if (!ogg_page_bos(&og))
+               skip_samples = 0;*/
+         } else
+         {
+            skip_samples = 0;
+         }
+         /*printf ("page granulepos: %d %d %d\n", skip_samples, page_nb_packets, (int)page_granule);*/
+         last_granule = page_granule;
+         /*Extract all available packets*/
+         while (!eos && ogg_stream_packetout(&os, &op) == 1)
+         {
+	    if (op.bytes>=8 && !memcmp(op.packet, "OPUS    ", 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)
+            {
+               st = process_header(&op, enh_enabled, &frame_size, &granule_frame_size, &rate, &nframes, forceMode, &channels, &lookahead, &extra_headers, quiet, &mode);
+               if (!st)
+                  exit(1);
+               if (!nframes)
+                  nframes=1;
+               fout = out_file_open(outFile, rate, &channels);
+
+            } else if (packet_count==1)
+            {
+               if (!quiet)
+                  print_comments((char*)op.packet, op.bytes);
+            } else if (packet_count<=1+extra_headers)
+            {
+               /* Ignore extra headers */
+            } else {
+               int lost=0;
+               if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
+                  lost=1;
+
+               /*End of stream condition*/
+               if (op.e_o_s && os.serialno == opus_serialno) /* don't care for anything except opus eos */
+                  eos=1;
+	       
+               {
+                  int ret;
+                  /*Decode frame*/
+                  if (!lost)
+                     ret = opus_decode(st, (unsigned char*)op.packet, op.bytes, output, frame_size);
+                  else
+                     ret = opus_decode(st, NULL, 0, output, frame_size);
+
+                  /*for (i=0;i<frame_size*channels;i++)
+                    printf ("%d\n", (int)output[i]);*/
+
+                  if (ret<0)
+                  {
+                     fprintf (stderr, "Decoding error: %s\n", opus_strerror(ret));
+                     break;
+                  }
+
+                  if (print_bitrate) {
+                     opus_int32 tmp=op.bytes;
+                     char ch=13;
+                     fputc (ch, stderr);
+                     fprintf (stderr, "Bitrate in use: %d bytes/packet     ", tmp);
+                  }
+                  /*Convert to short and save to output file*/
+                  if (strlen(outFile)!=0)
+                  {
+                     for (i=0;i<frame_size*channels;i++)
+                        out[i]=le_short(output[i]);
+                  } else {
+                     for (i=0;i<frame_size*channels;i++)
+                        out[i]=output[i];
+                  }
+                  {
+                     int frame_offset = 0;
+                     int new_frame_size = frame_size;
+                     /*printf ("packet %d %d\n", packet_no, skip_samples);*/
+                     /*fprintf (stderr, "packet %d %d %d\n", packet_no, skip_samples, lookahead);*/
+                     if (firstpacket == 1)
+                     {
+                        /*printf ("chopping first packet\n");*/
+                        new_frame_size -= lookahead;
+                        frame_offset = lookahead;
+                        firstpacket = 0;
+                     }
+                     if (new_frame_size>0)
+                     {  
+#if defined WIN32 || defined _WIN32
+                        if (strlen(outFile)==0)
+                           WIN_Play_Samples (out+frame_offset*channels, sizeof(short) * new_frame_size*channels);
+                        else
+#endif
+                           fwrite(out+frame_offset*channels, sizeof(short), new_frame_size*channels, fout);
+                  
+                        audio_size+=sizeof(short)*new_frame_size*channels;
+                     }
+                  }
+               }
+            }
+            packet_count++;
+         }
+      }
+      if (feof(fin))
+         break;
+
+   }
+
+   if (fout && wav_format)
+   {
+      if (fseek(fout,4,SEEK_SET)==0)
+      {
+         int tmp;
+         tmp = le_int(audio_size+36);
+         fwrite(&tmp,4,1,fout);
+         if (fseek(fout,32,SEEK_CUR)==0)
+         {
+            tmp = le_int(audio_size);
+            fwrite(&tmp,4,1,fout);
+         } else
+         {
+            fprintf (stderr, "First seek worked, second didn't\n");
+         }
+      } else {
+         fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
+      }
+   }
+
+   if (st)
+   {
+      opus_decoder_destroy(st);
+      opus_mode_destroy(mode);
+   } else {
+      fprintf (stderr, "This doesn't look like a OPUS file\n");
+   }
+   if (stream_init)
+      ogg_stream_clear(&os);
+   ogg_sync_clear(&oy);
+
+#if defined WIN32 || defined _WIN32
+   if (strlen(outFile)==0)
+      WIN_Audio_close ();
+#endif
+
+   if (close_in)
+      fclose(fin);
+   if (fout != NULL)
+      fclose(fout);   
+
+   return 0;
+}
--- /dev/null
+++ b/src/opusenc.c
@@ -1,0 +1,720 @@
+/* Copyright (c) 2002-2010 Jean-Marc Valin
+   Copyright (c) 2007-2010 Xiph.Org Foundation
+   Copyright (c) 2008-2010 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
+   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 <stdio.h>
+#include <getopt.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "opus_header.h"
+
+#ifdef _MSC_VER
+#define snprintf _snprintf
+#endif
+
+#include "opus.h"
+#include "opus_header.h"
+#include <ogg/ogg.h>
+#include "wav_io.h"
+
+#if defined WIN32 || defined _WIN32
+/* We need the following two to set stdout to binary */
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+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);
+   
+   return written;
+}
+
+#define MAX_FRAME_SIZE 2048
+#define MAX_FRAME_BYTES 1275
+#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(FILE *fin,int frame_size, int bits, int channels, int lsb, short * input, char *buff, opus_int32 *size)
+{   
+   short s[MAX_FRAME_SIZE];
+   unsigned char *in = (unsigned char*)s;
+   int i;
+   int nb_read;
+
+   if (size && *size<=0)
+   {
+      return 0;
+   }
+   /*Read input audio*/
+   if (size)
+      *size -= bits/8*channels*frame_size;
+   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 {
+      nb_read = fread(in,1,bits/8*channels* frame_size, fin);
+   }
+   nb_read /= bits/8*channels;
+
+   /*fprintf (stderr, "%d\n", nb_read);*/
+   if (nb_read==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]);
+      }
+   }
+
+   /* FIXME: This is probably redundent now */
+   /* copy to float input buffer */
+   for (i=0;i<frame_size*channels;i++)
+   {
+      input[i]=s[i];
+   }
+
+   for (i=nb_read*channels;i<frame_size*channels;i++)
+   {
+      input[i]=0;
+   }
+
+
+   return nb_read;
+}
+
+void version(const char *version)
+{
+   printf ("opusenc (OPUS %s encoder)\n",version);
+   printf ("Copyright (C) 2008-2011 Xiph.Org Foundation (written by Jean-Marc Valin)\n");
+}
+
+void version_short(const char *version)
+{
+   printf ("opusenc (OPUS %s encoder)\n",version);
+   printf ("Copyright (C) 2008-2011 Xiph.Org Foundation (written by Jean-Marc Valin)\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 (" --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 (" --skeleton         Outputs ogg skeleton metadata (may cause incompatibilities)\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");
+}
+
+
+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;
+   short input[MAX_FRAME_SIZE];
+   opus_int32 frame_size = 960;
+   int quiet=0;
+   int nbBytes;
+   void *st;
+   unsigned char bits[MAX_FRAME_BYTES];
+   int with_cbr = 0;
+   int with_cvbr = 0;
+   int with_skeleton = 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},
+      {"skeleton",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},
+      {"rate", required_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_stream_state so; /* ogg stream for skeleton bitstream */
+   ogg_page 		 og;
+   ogg_packet 		 op;
+   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 = "none";
+
+
+   /*Process command-line options*/
+   while(1)
+   {
+      c = getopt_long (argc, argv, "hvV",
+                       long_options, &option_index);
+      if (c==-1)
+         break;
+      
+      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,"skeleton")==0)
+         {
+            with_skeleton=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,"comp")==0)
+         {
+            complexity=atoi (optarg);
+         } else if (strcmp(long_options[option_index].name,"framesize")==0)
+         {
+            frame_size=atoi (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;
+      case 'v':
+         version(opus_version);
+         exit(0);
+         break;
+      case 'V':
+         print_bitrate=1;
+         break;
+      case '?':
+         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);
+   }
+   if (with_skeleton && ogg_stream_init(&so, rand())==-1)
+   {
+      fprintf(stderr,"Error: stream init failed\n");
+      exit(1);
+   }
+
+   if (strcmp(inFile, "-")==0)
+   {
+#if defined WIN32 || defined _WIN32
+         _setmode(_fileno(stdin), _O_BINARY);
+#elif defined OS2
+         _fsetmode(stdin,"b");
+#endif
+      fin=stdin;
+   }
+   else 
+   {
+      fin = fopen(inFile, "rb");
+      if (!fin)
+      {
+         perror(inFile);
+         exit(1);
+      }
+      close_in=1;
+   }
+
+   {
+      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 (bitrate<=0.005)
+     if (chan==1)
+       bitrate=64.0;
+     else
+       bitrate=128.0;
+     
+   bytes_per_packet = MAX_FRAME_BYTES;
+   
+   /*snprintf(vendor_string, sizeof(vendor_string), "Encoded with OPUS %s\n",OPUS_VERSION);*/
+   comment_init(&comments, &comments_length, vendor_string);
+
+   header.channels = chan;
+
+   {
+      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.sample_rate/1000., st_string, frame_size/(float)header.sample_rate*1000., bitrate, bytes_per_packet);
+         else      
+           fprintf (stderr, "Encoding %.0f kHz %s audio in %.0fms packets at %0.3fkbit/sec (%d bytes per packet maximum)\n",
+               header.sample_rate/1000., st_string, frame_size/(float)header.sample_rate*1000., bitrate, bytes_per_packet);
+   }
+
+   /*Initialize OPUS encoder*/
+   st = opus_encoder_create(48000, chan, OPUS_APPLICATION_AUDIO);
+
+   {
+      int tmp = (bitrate*1000);
+      if (opus_encoder_ctl(st, OPUS_SET_BITRATE(tmp)) != OPUS_OK)
+      {
+         fprintf (stderr, "bitrate request failed\n");
+         return 1;
+      }
+   }
+   if (!with_cbr)
+   {
+     if (opus_encoder_ctl(st, OPUS_SET_VBR_FLAG(1)) != OPUS_OK)
+     {
+        fprintf (stderr, "VBR request failed\n");
+        return 1;
+     }
+     if (!with_cvbr)
+     {
+        if (opus_encoder_ctl(st, OPUS_SET_VBR_CONSTRAINT(0)) != OPUS_OK)
+        {
+           fprintf (stderr, "VBR constraint failed\n");
+           return 1;
+        }
+     }
+   }
+
+   if (complexity!=-127) {
+     if (opus_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity)) != OPUS_OK)
+     {
+        fprintf (stderr, "Only complexity 0 through 10 is supported\n");
+        return 1;
+     }
+   }
+
+   if (strcmp(outFile,"-")==0)
+   {
+#if defined WIN32 || defined _WIN32
+      _setmode(_fileno(stdout), _O_BINARY);
+#endif
+      fout=stdout;
+   }
+   else 
+   {
+      fout = fopen(outFile, "wb");
+      if (!fout)
+      {
+         perror(outFile);
+         exit(1);
+      }
+      close_out=1;
+   }
+
+   if (with_skeleton) {
+      fprintf (stderr, "Warning: Enabling skeleton output may cause some decoders to fail.\n");
+   }
+
+   /*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;
+      }
+
+      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;
+   }
+
+   free(comments);
+
+   if (!wave_input)
+   {
+      nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, first_bytes, NULL);
+   } else {
+      nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
+   }
+   if (nb_samples==0)
+      eos=1;
+   total_samples += nb_samples;
+   nb_encoded = -lookahead;
+   /*Main encoding loop (one frame per iteration)*/
+   while (!eos || total_samples>nb_encoded)
+   {
+      id++;
+      /*Encode current frame*/
+
+      nbBytes = opus_encode(st, input, frame_size, bits, bytes_per_packet);
+      if (nbBytes<0)
+      {
+         fprintf(stderr, "Got error %d while encoding. Aborting.\n", nbBytes);
+         break;
+      }
+      nb_encoded += frame_size;
+      total_bytes += nbBytes;
+      peak_bytes=IMAX(nbBytes,peak_bytes);
+
+      if (wave_input)
+      {
+         nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
+      } else {
+         nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, NULL);
+      }
+      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-lookahead;
+      if (op.granulepos>total_samples)
+         op.granulepos = total_samples;
+      /*printf ("granulepos: %d %d %d %d %d %d\n", (int)op.granulepos, id, nframes, lookahead, 5, 6);*/
+      op.packetno = 2+id;
+      ogg_stream_packetin(&os, &op);
+
+      /*Write all new pages (most likely 0 or 1)*/
+      while (ogg_stream_pageout(&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);
+         }
+         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");
+         exit(1);
+      }
+      else
+         bytes_written += ret;
+   }
+
+   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.sample_rate))/1000.0, peak_bytes);
+
+   opus_encoder_destroy(st);
+   ogg_stream_clear(&os);
+
+   if (close_in)
+      fclose(fin);
+   if (close_out)
+      fclose(fout);
+   return 0;
+}
+
+/*                 
+ Comments will be stored in the Vorbis style.            
+ It is describled in the "Structure" section of
+    http://www.xiph.org/ogg/vorbis/doc/v-comment.html
+
+The comment header is decoded as follows:
+  1) [vendor_length] = read an unsigned integer of 32 bits
+  2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
+  3) [user_comment_list_length] = read an unsigned integer of 32 bits
+  4) iterate [user_comment_list_length] times {
+     5) [length] = read an unsigned integer of 32 bits
+     6) this iteration's user comment = read a UTF-8 vector as [length] octets
+     }
+  7) [framing_bit] = read a single bit as boolean
+  8) if ( [framing_bit]  unset or end of packet ) then ERROR
+  9) 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))
+#define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
+                                     buf[base+2]=((val)>>16)&0xff; \
+                                     buf[base+1]=((val)>>8)&0xff; \
+                                     buf[base]=(val)&0xff; \
+                                 }while(0)
+
+void comment_init(char **comments, int* length, char *vendor_string)
+{
+  int vendor_length=strlen(vendor_string);
+  int user_comment_list_length=0;
+  int len=4+vendor_length+4;
+  char *p=(char*)malloc(len);
+  if(p==NULL){
+     fprintf (stderr, "malloc failed in comment_init()\n");
+     exit(1);
+  }
+  writeint(p, 0, vendor_length);
+  memcpy(p+4, vendor_string, vendor_length);
+  writeint(p, 4+vendor_length, user_comment_list_length);
+  *length=len;
+  *comments=p;
+}
+void comment_add(char **comments, int* length, char *tag, char *val)
+{
+  char* p=*comments;
+  int vendor_length=readint(p, 0);
+  int user_comment_list_length=readint(p, 4+vendor_length);
+  int tag_len=(tag?strlen(tag):0);
+  int val_len=strlen(val);
+  int len=(*length)+4+tag_len+val_len;
+
+  p=(char*)realloc(p, len);
+  if(p==NULL){
+     fprintf (stderr, "realloc failed in comment_add()\n");
+     exit(1);
+  }
+
+  writeint(p, *length, tag_len+val_len);      /* length of comment */
+  if(tag) memcpy(p+*length+4, tag, tag_len);  /* comment */
+  memcpy(p+*length+4+tag_len, val, val_len);  /* comment */
+  writeint(p, 4+vendor_length, user_comment_list_length+1);
+
+  *comments=p;
+  *length=len;
+}
+#undef readint
+#undef writeint
--- /dev/null
+++ b/src/wav_io.c
@@ -1,0 +1,223 @@
+/* Copyright (C) 2002 Jean-Marc Valin 
+   File: wav_io.c
+   Routines to handle wav (RIFF) headers
+
+   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 <stdio.h>
+#include <string.h>
+#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;
+
+   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
+   fread(ch, 1, 4, file);
+   while (strcmp(ch, "fmt ")!=0)
+   {
+      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);*/
+      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;
+      }*/
+   
+   fread(&itmp, 4, 1, file);
+   itmp = le_int(itmp);
+   skip_bytes=itmp-16;
+   /*fprintf (stderr, "skip=%d\n", skip_bytes);*/
+   
+   fread(&stmp, 2, 1, file);
+   stmp = le_short(stmp);
+   if (stmp!=1)
+   {
+      fprintf (stderr, "Only PCM encoding is supported\n");
+      return -1;
+   }
+
+   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;
+   }
+
+   fread(&itmp, 4, 1, file);
+   itmp = le_int(itmp);
+   *rate = itmp;
+
+   fread(&itmp, 4, 1, file);
+   bpersec = le_int(itmp);
+
+   fread(&stmp, 2, 1, file);
+   balign = le_short(stmp);
+
+   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);*/
+
+   fread(ch, 1, 4, file);
+   while (strcmp(ch, "data")!=0)
+   {
+      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);*/
+      fread(ch, 1, 4, file);
+      if (feof(file))
+      {
+         fprintf (stderr, "Corrupted WAVE file: no \"data\"\n");
+         return -1;
+      }
+   }
+
+   /*Ignore this for now*/
+   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];
+   opus_int32 itmp;
+   opus_int16 stmp;
+
+   ch[4]=0;
+
+   fprintf (file, "RIFF");
+
+   itmp = 0x7fffffff;
+   fwrite(&itmp, 4, 1, file);
+
+   fprintf (file, "WAVEfmt ");
+
+   itmp = le_int(16);
+   fwrite(&itmp, 4, 1, file);
+
+   stmp = le_short(1);
+   fwrite(&stmp, 2, 1, file);
+
+   stmp = le_short(channels);
+   fwrite(&stmp, 2, 1, file);
+
+   itmp = le_int(rate);
+   fwrite(&itmp, 4, 1, file);
+
+   itmp = le_int(rate*channels*2);
+   fwrite(&itmp, 4, 1, file);
+
+   stmp = le_short(2*channels);
+   fwrite(&stmp, 2, 1, file);
+
+   stmp = le_short(16);
+   fwrite(&stmp, 2, 1, file);
+
+   fprintf (file, "data");
+
+   itmp = le_int(0x7fffffff);
+   fwrite(&itmp, 4, 1, file);
+
+
+}
--- /dev/null
+++ b/src/wav_io.h
@@ -1,0 +1,62 @@
+/* Copyright (C) 2002 Jean-Marc Valin 
+   File: wav_io.h
+
+   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.
+*/
+
+#ifndef WAV_IO_H
+#define WAV_IO_H
+
+#include <stdio.h>
+#include "opus_types.h"
+
+#if !defined(__LITTLE_ENDIAN__) && ( defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) )
+#define le_short(s) ((short) ((unsigned short) (s) << 8) | ((unsigned short) (s) >> 8))
+#define be_short(s) ((short) (s))
+#else
+#define le_short(s) ((short) (s))
+#define be_short(s) ((short) ((unsigned short) (s) << 8) | ((unsigned short) (s) >> 8))
+#endif 
+
+/** Convert little endian */
+static inline opus_int32 le_int(opus_int32 i)
+{
+#if !defined(__LITTLE_ENDIAN__) && ( defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) )
+   opus_uint32 ui, ret;
+   ui = i;
+   ret =  ui>>24;
+   ret |= (ui>>8)&0x0000ff00;
+   ret |= (ui<<8)&0x00ff0000;
+   ret |= (ui<<24);
+   return ret;
+#else
+   return i;
+#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
--- /dev/null
+++ b/src/wave_out.c
@@ -1,0 +1,220 @@
+/* Copyright (c) 2002, John Edwards
+
+   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.
+
+   - Neither the name of the Xiph.org Foundation nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+   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
+
+/* Set TABS = 4 */
+/********************************************************************
+
+ function: To provide playback of 16 bit PCM wave data in Win32
+           environments from decoded compressed files.
+
+ ********************************************************************/
+
+#if defined WIN32 || defined _WIN32
+
+#include <string.h>
+#include <errno.h>
+#include "wave_out.h"
+
+#define MAXWAVESIZE     4294967040LU
+#define MAX_WAVEBLOCKS    32
+
+// This is modified for USE_WIN_AUDIO - ONLY 2002-02-27
+
+
+static CRITICAL_SECTION  cs;
+static HWAVEOUT          dev                    = NULL;
+static int               ScheduledBlocks        = 0;
+static int               PlayedWaveHeadersCount = 0;          // free index
+static WAVEHDR*          PlayedWaveHeaders [MAX_WAVEBLOCKS];
+
+static int
+Box ( const char* msg )
+{
+	MessageBox ( NULL, msg, " "VERSION_STRING": Error Message . . .", MB_OK | MB_ICONEXCLAMATION );
+	return -1;
+}
+
+
+/*
+ *  This function registers already played WAVE chunks. Freeing is done by free_memory(),
+ */
+
+static void CALLBACK
+wave_callback ( HWAVE hWave, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
+{
+	if ( uMsg == WOM_DONE ) {
+		EnterCriticalSection ( &cs );
+		PlayedWaveHeaders [PlayedWaveHeadersCount++] = (WAVEHDR*) dwParam1;
+		LeaveCriticalSection ( &cs );
+	}
+}
+
+
+static void
+free_memory ( void )
+{
+	WAVEHDR*  wh;
+	HGLOBAL   hg;
+
+	EnterCriticalSection ( &cs );
+	wh = PlayedWaveHeaders [--PlayedWaveHeadersCount];
+	ScheduledBlocks--;                               // decrease the number of USED blocks
+	LeaveCriticalSection ( &cs );
+
+	waveOutUnprepareHeader ( dev, wh, sizeof (WAVEHDR) );
+
+	hg = GlobalHandle ( wh -> lpData );       // Deallocate the buffer memory
+	GlobalUnlock (hg);
+	GlobalFree   (hg);
+
+	hg = GlobalHandle ( wh );                 // Deallocate the header memory
+	GlobalUnlock (hg);
+	GlobalFree   (hg);
+}
+
+
+Int
+Set_WIN_Params ( FILE_T   dummyFile ,
+                 Ldouble  SampleFreq,
+                 Uint     BitsPerSample,
+                 Uint     Channels )
+{
+	WAVEFORMATEX  outFormat;
+	UINT          deviceID = WAVE_MAPPER;
+
+	(void) dummyFile;
+
+	if ( waveOutGetNumDevs () == 0 )
+		return Box ( "No audio device present." );
+
+	outFormat.wFormatTag      = WAVE_FORMAT_PCM;
+	outFormat.wBitsPerSample  = BitsPerSample;
+	outFormat.nChannels       = Channels;
+	outFormat.nSamplesPerSec  = (unsigned long)(SampleFreq + 0.5);
+	outFormat.nBlockAlign     = (outFormat.wBitsPerSample + 7) / 8 * outFormat.nChannels;
+	outFormat.nAvgBytesPerSec = outFormat.nSamplesPerSec * outFormat.nBlockAlign;
+
+	switch ( waveOutOpen ( &dev, deviceID, &outFormat, (DWORD)wave_callback, 0, CALLBACK_FUNCTION ) )
+	{
+		case MMSYSERR_ALLOCATED:   return Box ( "Device is already open." );
+		case MMSYSERR_BADDEVICEID: return Box ( "The specified device is out of range." );
+		case MMSYSERR_NODRIVER:    return Box ( "There is no audio driver in this system." );
+		case MMSYSERR_NOMEM:       return Box ( "Unable to allocate sound memory." );
+		case WAVERR_BADFORMAT:     return Box ( "This audio format is not supported." );
+		case WAVERR_SYNC:          return Box ( "The device is synchronous." );
+		default:                   return Box ( "Unknown media error." );
+		case MMSYSERR_NOERROR:     break;
+	}
+
+	waveOutReset ( dev );
+	InitializeCriticalSection ( &cs );
+	SetPriorityClass ( GetCurrentProcess (), HIGH_PRIORITY_CLASS );
+	return 0;
+}
+
+
+int
+WIN_Play_Samples ( const void* data, size_t len )
+{
+	HGLOBAL    hg;
+	HGLOBAL    hg2;
+	LPWAVEHDR  wh;
+	void*      allocptr;
+
+	do {
+		while ( PlayedWaveHeadersCount > 0 )                // free used blocks ...
+			free_memory ();
+
+		if ( ScheduledBlocks < sizeof(PlayedWaveHeaders)/sizeof(*PlayedWaveHeaders) ) // wait for a free block ...
+			break;
+		Sleep (26);
+	} while (1);
+
+	if ( (hg2 = GlobalAlloc ( GMEM_MOVEABLE, len )) == NULL )   // allocate some memory for a copy of the buffer
+		return Box ( "GlobalAlloc failed." );
+
+	allocptr = GlobalLock (hg2);
+	CopyMemory ( allocptr, data, len );                         // Here we can call any modification output functions we want....
+
+	if ( (hg = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof (WAVEHDR))) == NULL ) // now make a header and WRITE IT!
+		return -1;
+
+	wh                   = GlobalLock (hg);
+	wh -> dwBufferLength = len;
+	wh -> lpData         = allocptr;
+
+	if ( waveOutPrepareHeader ( dev, wh, sizeof (WAVEHDR)) != MMSYSERR_NOERROR ) {
+		GlobalUnlock (hg);
+		GlobalFree   (hg);
+		return -1;
+	}
+
+	if ( waveOutWrite ( dev, wh, sizeof (WAVEHDR)) != MMSYSERR_NOERROR ) {
+		GlobalUnlock (hg);
+		GlobalFree   (hg);
+		return -1;
+	}
+
+	EnterCriticalSection ( &cs );
+	ScheduledBlocks++;
+	LeaveCriticalSection ( &cs );
+
+	return len;
+}
+
+
+int
+WIN_Audio_close ( void )
+{
+	if ( dev != NULL ) {
+
+		while ( ScheduledBlocks > 0 ) {
+			Sleep (ScheduledBlocks);
+			while ( PlayedWaveHeadersCount > 0 )         // free used blocks ...
+				free_memory ();
+		}
+
+		waveOutReset (dev);      // reset the device
+		waveOutClose (dev);      // close the device
+		dev = NULL;
+	}
+
+	DeleteCriticalSection ( &cs );
+	ScheduledBlocks = 0;
+	return 0;
+}
+
+#endif
+
+/* end of wave_out.c */
--- /dev/null
+++ b/src/wave_out.h
@@ -1,0 +1,71 @@
+/* Copyright (c) 2002, John Edwards
+
+   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.
+
+   - Neither the name of the Xiph.org Foundation nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+   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.
+*/
+
+//    WAVE_OUT.H - Necessary stuff for WIN_AUDIO
+
+#ifndef WAVE_OUT_H
+#define WAVE_OUT_H
+
+#include <stdio.h>
+#include <windows.h>
+#ifdef __MINGW32__
+#include <mmsystem.h>
+#endif
+
+#define VERSION_STRING "\n 0.11.4\n"
+
+#define Cdecl               __cdecl
+#define __attribute__(x)
+#define sleep(__sec)        Sleep ((__sec) * 1000)
+#define inline              __inline
+#define restrict
+
+//// constants /////////////////////////////////////////////////////
+
+#define CD_SAMPLE_FREQ         44.1e3
+#define SAMPLE_SIZE            16
+#define SAMPLE_SIZE_STRING     ""
+#define WINAUDIO_FD            ((FILE_T)-128)
+#define FILE_T                 FILE*
+#define INVALID_FILEDESC       NULL
+
+//// Simple types //////////////////////////////////////////////////
+
+typedef signed   int        Int;        // at least -32767...+32767, fast type
+typedef unsigned int        Uint;       // at least 0...65535, fast type
+typedef long double         Ldouble;    // most exact floating point format
+
+//// procedures/functions //////////////////////////////////////////
+// wave_out.c
+Int        Set_WIN_Params             ( FILE_T dummyFile , Ldouble SampleFreq, Uint BitsPerSample, Uint Channels);
+int        WIN_Play_Samples           ( const void* buff, size_t len );
+int        WIN_Audio_close            ( void );
+
+#endif /* WAVE_OUT_H */