Fixed the default int32 type which was wrong on amd64 (and added testcase).
authorJean-Marc Valin <jean-marc.valin@usherbrooke.ca>
Thu, 31 Jan 2008 12:43:10 +0000 (23:43 +1100)
committerJean-Marc Valin <jean-marc.valin@usherbrooke.ca>
Thu, 31 Jan 2008 12:43:10 +0000 (23:43 +1100)
Also, added an Ogg encoder (doesn't work quite yet).

libcelt/Makefile.am
libcelt/celt_types.h
tests/Makefile.am
tests/type-test.c [new file with mode: 0644]
tools/celtenc.c [new file with mode: 0644]
tools/skeleton.c [new file with mode: 0644]
tools/skeleton.h [new file with mode: 0644]
tools/wav_io.c [new file with mode: 0644]
tools/wav_io.h [new file with mode: 0644]

index 6421b77..dbaaeb0 100644 (file)
@@ -15,9 +15,9 @@ lib_LTLIBRARIES = libcelt.la
 
 # Sources for compilation in the library
 libcelt_la_SOURCES = bands.c bitrdec.c bitree.c bitrenc.c celt.c cwrs.c \
 
 # Sources for compilation in the library
 libcelt_la_SOURCES = bands.c bitrdec.c bitree.c bitrenc.c celt.c cwrs.c \
-       ecintrin.h entcode.c  entdec.c entenc.c fftwrap.c laplace.c mdct.c \
-       modes.c pitch.c psy.c quant_bands.c quant_pitch.c rangedec.c \
-       rangeenc.c rate.c smallft.c vq.c
+       ecintrin.h entcode.c entdec.c entenc.c fftwrap.c header.c laplace.c mdct.c modes.c \
+       pitch.c psy.c quant_bands.c quant_pitch.c rangedec.c rangeenc.c rate.c \
+       smallft.c vq.c
 
 #noinst_HEADERS =
 
 
 #noinst_HEADERS =
 
index 9adb569..364f960 100644 (file)
    /* Give up, take a reasonable guess */
    typedef short celt_int16_t;
    typedef unsigned short celt_uint16_t;
    /* Give up, take a reasonable guess */
    typedef short celt_int16_t;
    typedef unsigned short celt_uint16_t;
-   typedef long celt_int32_t;
-   typedef unsigned long celt_uint32_t;
+   typedef int celt_int32_t;
+   typedef unsigned int celt_uint32_t;
    typedef long long celt_int64_t;
    typedef unsigned long long celt_uint64_t;
 
    typedef long long celt_int64_t;
    typedef unsigned long long celt_uint64_t;
 
index d5fb043..c160e81 100644 (file)
@@ -1,9 +1,12 @@
 INCLUDES = -I$(top_srcdir)/libcelt
 METASOURCES = AUTO
 
 INCLUDES = -I$(top_srcdir)/libcelt
 METASOURCES = AUTO
 
-TESTS = ectest cwrs32-test cwrs64-test
+TESTS = type-test ectest cwrs32-test cwrs64-test
 
 
-bin_PROGRAMS = ectest cwrs32-test cwrs64-test
+bin_PROGRAMS = type-test ectest cwrs32-test cwrs64-test
+
+ectest_SOURCES = type-test.c
+ectest_LDADD = $(top_builddir)/libcelt/libcelt.la
 
 ectest_SOURCES = ectest.c
 ectest_LDADD = $(top_builddir)/libcelt/libcelt.la
 
 ectest_SOURCES = ectest.c
 ectest_LDADD = $(top_builddir)/libcelt/libcelt.la
diff --git a/tests/type-test.c b/tests/type-test.c
new file mode 100644 (file)
index 0000000..58dfbd8
--- /dev/null
@@ -0,0 +1,24 @@
+#include "celt_types.h"
+#include <stdio.h>
+
+int main()
+{
+   celt_int16_t i = 1;
+   i <<= 14;
+   if (i>>14 != 1)
+   {
+      fprintf(stderr, "celt_int16_t isn't 16 bits\n");
+      return 1;
+   }
+   if (sizeof(celt_int16_t)*2 != sizeof(celt_int32_t))
+   {
+      fprintf(stderr, "16*2 != 32\n");
+      return 1;
+   }
+   if (sizeof(celt_int32_t)*2 != sizeof(celt_int64_t))
+   {
+      fprintf(stderr, "32*2 != 64\n");
+      return 1;
+   }
+   return 0;
+}
diff --git a/tools/celtenc.c b/tools/celtenc.c
new file mode 100644 (file)
index 0000000..26072db
--- /dev/null
@@ -0,0 +1,720 @@
+/* Copyright (C) 2002-2008 Jean-Marc Valin 
+   File: celtenc.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.
+   
+   - 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
+
+#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 <time.h>
+
+#include "celt.h"
+#include "celt_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
+
+#include "skeleton.h"
+
+
+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 2000
+#define MAX_FRAME_BYTES 2000
+
+/* 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, celt_int32_t *size)
+{   
+   unsigned char in[MAX_FRAME_BYTES*2];
+   int i;
+   short *s;
+   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;
+
+   s=(short*)in;
+   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]=(short)s[i];
+   }
+
+   for (i=nb_read*channels;i<frame_size*channels;i++)
+   {
+      input[i]=0;
+   }
+
+
+   return nb_read;
+}
+
+void add_fishead_packet (ogg_stream_state *os) {
+
+   fishead_packet fp;
+
+   memset(&fp, 0, sizeof(fp));
+   fp.ptime_n = 0;
+   fp.ptime_d = 1000;
+   fp.btime_n = 0;
+   fp.btime_d = 1000;
+
+   add_fishead_to_stream(os, &fp);
+}
+
+/*
+ * Adds the fishead packets in the skeleton output stream along with the e_o_s packet
+ */
+void add_fisbone_packet (ogg_stream_state *os, celt_int32_t serialno, CELTHeader *header) {
+
+   fisbone_packet fp;
+
+   memset(&fp, 0, sizeof(fp));
+   fp.serial_no = serialno;
+   fp.nr_header_packet = 2 + header->extra_headers;
+   fp.granule_rate_n = header->sample_rate;
+   fp.granule_rate_d = 1;
+   fp.start_granule = 0;
+   fp.preroll = 3;
+   fp.granule_shift = 0;
+
+   add_message_header_field(&fp, "Content-Type", "audio/x-celt");
+
+   add_fisbone_to_stream(os, &fp);
+}
+
+void version()
+{
+   printf ("celtenc (CELT encoder)\n");
+   printf ("Copyright (C) 2008 Jean-Marc Valin\n");
+}
+
+void version_short()
+{
+   printf ("celtenc (CELT encoder)\n");
+   printf ("Copyright (C) 2008 Jean-Marc Valin\n");
+}
+
+void usage()
+{
+   printf ("Usage: celtenc [options] input_file output_file\n");
+   printf ("\n");
+   printf ("Encodes input_file using Speex. 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 (use bit-rate n or lower)\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 (" --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 16-bit, little-endian, mono\n"); 
+   printf ("\n");
+   printf ("More information is available from the Speex site: http://www.speex.org\n");
+   printf ("\n");
+   printf ("Please report bugs to the mailing list `speex-dev@xiph.org'.\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];
+   celt_int32_t frame_size;
+   int quiet=0;
+   int nbBytes;
+   const CELTMode *mode=celt_mono;
+   int modeID = -1;
+   void *st;
+   char bits[MAX_FRAME_BYTES];
+   int with_skeleton = 0;
+   struct option long_options[] =
+   {
+      {"bitrate", 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},
+      {"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;
+   celt_int32_t rate=0;
+   celt_int32_t 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;
+   CELTHeader header;
+   celt_int32_t complexity=3;
+   char vendor_string[64];
+   char *comments;
+   int comments_length;
+   int close_in=0, close_out=0;
+   int eos=0;
+   celt_int32_t bitrate=0;
+   double cumul_bits=0, enc_frames=0;
+   char first_bytes[12];
+   int wave_input=0;
+   celt_int32_t tmp;
+   celt_int32_t lookahead = 0;
+   int bytes_per_packet=48;
+   
+   snprintf(vendor_string, sizeof(vendor_string), "Encoded with CELT\n");
+   
+   comment_init(&comments, &comments_length, vendor_string);
+
+   /*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 = atoi (optarg);
+         } 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();
+            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,"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;
+            mode = celt_stereo;
+         } else if (strcmp(long_options[option_index].name,"rate")==0)
+         {
+            rate=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();
+         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 ?? */
+      }
+   }
+
+   celt_mode_info(mode, CELT_GET_FRAME_SIZE, &frame_size);
+   celt_init_header(&header, rate, 1, mode);
+   header.nb_channels = chan;
+
+   {
+      char *st_string="mono";
+      if (chan==2)
+         st_string="stereo";
+      if (!quiet)
+         fprintf (stderr, "Encoding %d Hz audio using %s\n", 
+               header.sample_rate, st_string);
+   }
+   /*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n", 
+     header.rate, mode->bitrate, mode->modeName);*/
+
+   /*Initialize Speex encoder*/
+   st = celt_encoder_new(mode);
+
+   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");
+   }
+
+   /* first packet should be the skeleton header. */
+   if (with_skeleton) {
+      add_fishead_packet(&so);
+      if ((ret = flush_ogg_stream_to_file(&so, fout))) {
+        fprintf (stderr,"Error: failed skeleton (fishead) header to output stream\n");
+         exit(1);
+      } else
+        bytes_written += ret;
+   }
+
+   /*Write header*/
+   {
+      char header_data[100];
+      int packet_size = celt_header_to_packet(&header, header_data, 100);
+      op.packet = header_data;
+      op.bytes = packet_size;
+      fprintf(stderr, "header size is %d\n", op.bytes);
+      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);
+   }
+
+   /* fisbone packet should be write after all bos pages */
+   if (with_skeleton) {
+      add_fisbone_packet(&so, os.serialno, &header);
+      if ((ret = flush_ogg_stream_to_file(&so, fout))) {
+        fprintf (stderr,"Error: failed writing skeleton (fisbone )header to output stream\n");
+         exit(1);
+      } else
+        bytes_written += ret;
+   }
+
+   /* writing the rest of the speex 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);
+
+   /* write the skeleton eos packet */
+   if (with_skeleton) {
+      add_eos_packet_to_stream(&so);
+      if ((ret = flush_ogg_stream_to_file(&so, fout))) {
+         fprintf (stderr,"Error: failed writing skeleton header to output stream\n");
+         exit(1);
+      } else
+        bytes_written += ret;
+   }
+
+
+   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 = celt_encode(st, input, bits, bytes_per_packet);
+      
+      nb_encoded += frame_size;
+
+      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;
+   }
+
+   celt_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
diff --git a/tools/skeleton.c b/tools/skeleton.c
new file mode 100644 (file)
index 0000000..22159d5
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * skeleton.c
+ * author: Tahseen Mohammad
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <ogg/ogg.h>
+
+#include "skeleton.h"
+
+/* write an ogg_page to a file pointer */
+int write_ogg_page_to_file(ogg_page *og, FILE *out) {
+   int written;
+   
+   written = fwrite(og->header,1, og->header_len, out);
+   written += fwrite(og->body,1, og->body_len, out);
+
+   return written;
+}
+
+int add_message_header_field(fisbone_packet *fp, 
+                                        char *header_key, 
+                                        char *header_value) {
+
+    /* size of both key and value + ': ' + CRLF */
+    int this_message_size = strlen(header_key) + strlen(header_value) + 4;
+    if (fp->message_header_fields == NULL) {
+        fp->message_header_fields = _ogg_calloc(this_message_size, sizeof(char));
+    } else {
+        int new_size = (fp->current_header_size + this_message_size) * sizeof(char);
+        fp->message_header_fields = _ogg_realloc(fp->message_header_fields, new_size);
+    }
+    snprintf(fp->message_header_fields + fp->current_header_size, 
+                this_message_size+1, 
+                "%s: %s\r\n", 
+                header_key, 
+                header_value);
+    fp->current_header_size += this_message_size;
+
+    return 0;
+}
+
+/* create a ogg_packet from a fishead_packet structure */
+ogg_packet ogg_from_fishead(fishead_packet *fp) {
+
+    ogg_packet op;
+
+    memset(&op, 0, sizeof(op));
+    op.packet = _ogg_calloc(FISHEAD_SIZE, sizeof(unsigned char));
+    memset(op.packet, 0, FISHEAD_SIZE);
+
+    memcpy (op.packet, FISHEAD_IDENTIFIER, 8); /* identifier */
+    *((ogg_uint16_t*)(op.packet+8)) = SKELETON_VERSION_MAJOR; /* version major */
+    *((ogg_uint16_t*)(op.packet+10)) = SKELETON_VERSION_MINOR; /* version minor */
+    *((ogg_int64_t*)(op.packet+12)) = (ogg_int64_t)fp->ptime_n; /* presentationtime numerator */
+    *((ogg_int64_t*)(op.packet+20)) = (ogg_int64_t)fp->ptime_d; /* presentationtime denominator */
+    *((ogg_int64_t*)(op.packet+28)) = (ogg_int64_t)fp->btime_n; /* basetime numerator */
+    *((ogg_int64_t*)(op.packet+36)) = (ogg_int64_t)fp->btime_d; /* basetime denominator */
+    /* TODO: UTC time, set to zero for now */
+
+    op.b_o_s = 1;   /* its the first packet of the stream */
+    op.e_o_s = 0;   /* its not the last packet of the stream */
+    op.bytes = FISHEAD_SIZE;  /* length of the packet in bytes */
+
+    return op;
+}
+
+/* create a ogg_packet from a fisbone_packet structure. 
+ * call this method after the fisbone_packet is filled and all message header fields are added
+ * by calling add_message_header_field method.
+ */
+ogg_packet ogg_from_fisbone(fisbone_packet *fp) {
+    
+    ogg_packet op;
+    int packet_size = FISBONE_SIZE + fp->current_header_size;
+
+    memset (&op, 0, sizeof (op));       
+    op.packet = _ogg_calloc (packet_size, sizeof(unsigned char));
+    memset (op.packet, 0, packet_size);
+    memcpy (op.packet, FISBONE_IDENTIFIER, 8); /* identifier */
+    *((ogg_uint32_t*)(op.packet+8)) = FISBONE_MESSAGE_HEADER_OFFSET; /* offset of the message header fields */
+    *((ogg_uint32_t*)(op.packet+12)) = fp->serial_no; /* serialno of the respective stream */
+    *((ogg_uint32_t*)(op.packet+16)) = fp->nr_header_packet; /* number of header packets */
+    *((ogg_int64_t*)(op.packet+20)) = fp->granule_rate_n; /* granulrate numerator */
+    *((ogg_int64_t*)(op.packet+28)) = fp->granule_rate_d; /* granulrate denominator */
+    *((ogg_int64_t*)(op.packet+36)) = fp->start_granule; /* start granule */
+    *((ogg_uint32_t*)(op.packet+44)) = fp->preroll; /* preroll, for theora its 0 */
+    *(op.packet+48) = fp->granule_shift; /* granule shift */
+    memcpy((op.packet+FISBONE_SIZE), fp->message_header_fields, fp->current_header_size);
+
+    op.b_o_s = 0;
+    op.e_o_s = 0;
+    op.bytes = packet_size; /* size of the packet in bytes */
+
+    return op;
+}
+
+/* fills up a fishead_packet from a fishead ogg_packet of a skeleton bistream */
+fishead_packet fishead_from_ogg(ogg_packet *op) {
+       
+    fishead_packet fp;
+
+    if (memcmp(op->packet, FISHEAD_IDENTIFIER, 8))
+       ; /* invalid packet what do we do? */
+
+    fp.version_major = *((ogg_uint16_t*)(op->packet+8)); /* version major */
+    fp.version_minor = *((ogg_uint16_t*)(op->packet+10)); /* version minor */
+    fp.ptime_n = *((ogg_int64_t*)(op->packet+12)); /* presentationtime numerator */
+    fp.ptime_d = *((ogg_int64_t*)(op->packet+20)); /* presentationtime denominator */
+    fp.btime_n = *((ogg_int64_t*)(op->packet+28)); /* basetime numerator */
+    fp.btime_d = *((ogg_int64_t*)(op->packet+36)); /* basetime denominator */
+    memcpy(fp.UTC, op->packet+44, 20);
+
+    return fp;
+}
+
+/* fills up a fisbone_packet from a fisbone ogg_packet of a skeleton bitstream */
+fisbone_packet fisbone_from_ogg(ogg_packet *op) {
+
+    fisbone_packet fp;
+    
+    if (memcmp(op->packet, FISBONE_IDENTIFIER, 8))
+       ; /* invalid value, what do we do? */
+    fp.serial_no = *((ogg_uint32_t*)(op->packet+12)); /* serialno of the stream represented by this fisbone packet */
+    fp.nr_header_packet = *((ogg_uint32_t*)(op->packet+16)); /* number of header packets */
+    fp.granule_rate_n = *((ogg_int64_t*)(op->packet+20)); /* granulrate numerator */
+    fp.granule_rate_d = *((ogg_int64_t*)(op->packet+28)); /* granulrate denominator */
+    fp.start_granule = *((ogg_int64_t*)(op->packet+36)); /* start granule */
+    fp.preroll = *((ogg_uint32_t*)(op->packet+44)); /* preroll, for theora its 0 */
+    fp.granule_shift = *(op->packet+48); /* granule shift */
+    fp.current_header_size = op->bytes - FISBONE_SIZE;
+    fp.message_header_fields = _ogg_calloc(fp.current_header_size+1, sizeof(char));
+    memcpy(fp.message_header_fields, op->packet+FISBONE_SIZE, fp.current_header_size);
+
+    return fp;
+}
+
+int add_fishead_to_stream(ogg_stream_state *os, fishead_packet *fp) {
+
+    ogg_packet op;
+
+    op = ogg_from_fishead(fp);
+    ogg_stream_packetin(os, &op);
+    _ogg_free(op.packet);
+
+    return 0;
+}
+
+int add_fisbone_to_stream(ogg_stream_state *os, fisbone_packet *fp) {
+
+    ogg_packet op;
+
+    op = ogg_from_fisbone(fp);
+    ogg_stream_packetin(os, &op);
+    _ogg_free(op.packet);
+
+    return 0;
+}
+
+int add_eos_packet_to_stream(ogg_stream_state *os) {
+
+    ogg_packet op;
+
+    memset (&op, 0, sizeof(op));
+    op.e_o_s = 1;
+    ogg_stream_packetin(os, &op);
+
+    return 0;
+}
+
+int flush_ogg_stream_to_file(ogg_stream_state *os, FILE *out) {
+
+    ogg_page og;
+    int result;
+
+    while((result = ogg_stream_flush(os, &og)))
+    {
+        if(!result) break;
+        result = write_ogg_page_to_file(&og, out);
+        if(result != og.header_len + og.body_len)
+            return 1;
+    }
+
+    return 0;
+}
diff --git a/tools/skeleton.h b/tools/skeleton.h
new file mode 100644 (file)
index 0000000..f07d7a3
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * skeleton.h
+ * author: Tahseen Mohammad
+ */
+
+#ifndef _SKELETON_H
+#define _SKELETON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef WIN32
+#define snprintf _snprintf
+#endif
+
+#include <ogg/ogg.h>
+
+#define SKELETON_VERSION_MAJOR 3
+#define SKELETON_VERSION_MINOR 0
+#define FISHEAD_IDENTIFIER "fishead\0"
+#define FISBONE_IDENTIFIER "fisbone\0"
+#define FISHEAD_SIZE 64
+#define FISBONE_SIZE 52
+#define FISBONE_MESSAGE_HEADER_OFFSET 44
+
+/* fishead_packet holds a fishead header packet. */
+typedef struct {
+    ogg_uint16_t version_major;                                    /* skeleton version major */
+    ogg_uint16_t version_minor;                                    /* skeleton version minor */
+    /* Start time of the presentation
+     * For a new stream presentationtime & basetime is same. */
+    ogg_int64_t ptime_n;                                    /* presentation time numerator */
+    ogg_int64_t ptime_d;                                    /* presentation time denominator */
+    ogg_int64_t btime_n;                                    /* basetime numerator */
+    ogg_int64_t btime_d;                                    /* basetime denominator */
+    /* will holds the time of origin of the stream, a 20 bit field. */
+    unsigned char UTC[20];
+} fishead_packet;
+
+/* fisbone_packet holds a fisbone header packet. */
+typedef struct {
+    ogg_uint32_t serial_no;                                 /* serial no of the corresponding stream */
+    ogg_uint32_t nr_header_packet;                      /* number of header packets */
+    /* granule rate is the temporal resolution of the logical bitstream */
+    ogg_int64_t granule_rate_n;                            /* granule rate numerator */
+    ogg_int64_t granule_rate_d;                            /* granule rate denominator */
+    ogg_int64_t start_granule;                             /* start granule value */
+    ogg_uint32_t preroll;                                   /* preroll */
+    unsigned char granule_shift;                           /* 1 byte value holding the granule shift */
+    char *message_header_fields;                            /* holds all the message header fields */
+    /* current total size of the message header fields, for realloc purpose, initially zero */
+    ogg_uint32_t current_header_size;
+} fisbone_packet;
+
+extern int write_ogg_page_to_file(ogg_page *og, FILE *out);
+extern int add_message_header_field(fisbone_packet *fp, char *header_key, char *header_value);
+/* remember to deallocate the returned ogg_packet properly */
+extern ogg_packet ogg_from_fishead(fishead_packet *fp);
+extern ogg_packet ogg_from_fisbone(fisbone_packet *fp);
+extern fishead_packet fishead_from_ogg(ogg_packet *op);
+extern fisbone_packet fisbone_from_ogg(ogg_packet *op);
+extern int add_fishead_to_stream(ogg_stream_state *os, fishead_packet *fp);
+extern int add_fisbone_to_stream(ogg_stream_state *os, fisbone_packet *fp);
+extern int add_eos_packet_to_stream(ogg_stream_state *os);
+extern int flush_ogg_stream_to_file(ogg_stream_state *os, FILE *out);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _SKELETON_H */
+
+
+
+
+
+
diff --git a/tools/wav_io.c b/tools/wav_io.c
new file mode 100644 (file)
index 0000000..a6a9e4b
--- /dev/null
@@ -0,0 +1,232 @@
+/* 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.
+   
+   - 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
+
+#include <stdio.h>
+#include <string.h>
+#include "celt_types.h"
+#include "wav_io.h"
+
+
+int read_wav_header(FILE *file, int *rate, int *channels, int *format, celt_int32_t *size)
+{
+   char ch[5];
+   celt_int32_t itmp;
+   celt_int16_t stmp;
+   celt_int32_t bpersec;
+   celt_int16_t 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;
+   if (*rate != 8000 && *rate != 16000 && *rate != 11025 && *rate != 22050 && *rate != 32000 && *rate != 44100 && *rate != 48000)
+   {
+      fprintf (stderr, "Only 8 kHz (narrowband) and 16 kHz (wideband) supported (plus 11.025 kHz and 22.05 kHz, but your mileage may vary)\n");
+      return -1;
+   }
+
+   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];
+   celt_int32_t itmp;
+   celt_int16_t 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);
+
+
+}
diff --git a/tools/wav_io.h b/tools/wav_io.h
new file mode 100644 (file)
index 0000000..cbcf47a
--- /dev/null
@@ -0,0 +1,66 @@
+/* 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.
+   
+   - 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.
+*/
+
+#ifndef WAV_IO_H
+#define WAV_IO_H
+
+#include <stdio.h>
+#include "celt_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 celt_int32_t le_int(celt_int32_t i)
+{
+#if !defined(__LITTLE_ENDIAN__) && ( defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) )
+   celt_uint32_t 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, celt_int32_t *size);
+
+void write_wav_header(FILE *file, int rate, int channels, int format, int size);
+
+#endif